From 2d31d77a8ba231cb03ec35863c4c4ce2024f6509 Mon Sep 17 00:00:00 2001 From: Jonathan Gordon Date: Thu, 29 Jul 2010 12:37:48 +0000 Subject: FS#11470 - new skin code, finally svn uses the new parser from the theme editor. This means that a skin that passes the editor WILL pass svn and checkwps (unless the target runs out of skin buffer or something. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@27613 a1c6a512-1295-4272-9138-f99709370657 --- apps/gui/skin_engine/skin_render.c | 614 +++++++++++++++++++++++++++++++++++++ 1 file changed, 614 insertions(+) create mode 100644 apps/gui/skin_engine/skin_render.c (limited to 'apps/gui/skin_engine/skin_render.c') diff --git a/apps/gui/skin_engine/skin_render.c b/apps/gui/skin_engine/skin_render.c new file mode 100644 index 0000000000..e05f97ff4e --- /dev/null +++ b/apps/gui/skin_engine/skin_render.c @@ -0,0 +1,614 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: skin_parser.c 26752 2010-06-10 21:22:16Z bieber $ + * + * Copyright (C) 2010 Jonathan Gordon + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include "strlcat.h" + +#include "config.h" +#include "kernel.h" +#ifdef HAVE_ALBUMART +#include "albumart.h" +#endif +#include "skin_display.h" +#include "skin_engine.h" +#include "skin_parser.h" +#include "tag_table.h" +#include "skin_scan.h" +#if CONFIG_TUNER +#include "radio.h" +#endif +#include "language.h" +#include "playback.h" + + +#define MAX_LINE 1024 + +struct skin_draw_info { + struct gui_wps *gwps; + struct skin_viewport *skin_vp; + int line_number; + unsigned long refresh_type; + + char* cur_align_start; + struct align_pos align; + bool no_line_break; + bool line_scrolls; + bool force_redraw; + + char *buf; + size_t buf_size; +}; + +typedef bool (*skin_render_func)(struct skin_element* alternator, struct skin_draw_info *info); +bool skin_render_alternator(struct skin_element* alternator, struct skin_draw_info *info); + + +static bool do_non_text_tags(struct gui_wps *gwps, struct skin_draw_info *info, + struct skin_element *element, struct viewport* vp) +{ +#ifndef HAVE_LCD_BITMAP + (void)vp; /* silence warnings */ +#endif + struct wps_token *token = (struct wps_token *)element->data; + struct wps_data *data = gwps->data; + bool do_refresh = (element->tag->flags & info->refresh_type) > 0; + switch (token->type) + { +#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1)) + case SKIN_TOKEN_VIEWPORT_FGCOLOUR: + { + struct viewport_colour *col = token->value.data; + col->vp->fg_pattern = col->colour; + } + break; + case SKIN_TOKEN_VIEWPORT_BGCOLOUR: + { + struct viewport_colour *col = token->value.data; + col->vp->bg_pattern = col->colour; + } + break; +#endif + case SKIN_TOKEN_VIEWPORT_ENABLE: + { + char label = token->value.i; + char temp = VP_DRAW_HIDEABLE; + struct skin_element *viewport = gwps->data->tree; + while (viewport) + { + struct skin_viewport *skinvp = (struct skin_viewport*)viewport->data; + if (skinvp->label == label) + { + if (skinvp->hidden_flags&VP_DRAW_HIDDEN) + { + temp |= VP_DRAW_WASHIDDEN; + } + skinvp->hidden_flags = temp; + } + viewport = viewport->next; + } + } + break; +#ifdef HAVE_LCD_BITMAP + case SKIN_TOKEN_UIVIEWPORT_ENABLE: + sb_set_info_vp(gwps->display->screen_type, + token->value.i|VP_INFO_LABEL); + break; + case SKIN_TOKEN_PEAKMETER: + data->peak_meter_enabled = true; + if (do_refresh) + draw_peakmeters(gwps, info->line_number, vp); + break; +#endif + case SKIN_TOKEN_VOLUMEBAR: + case SKIN_TOKEN_BATTERY_PERCENTBAR: + case SKIN_TOKEN_PROGRESSBAR: + { +#ifdef HAVE_LCD_BITMAP + struct progressbar *bar = (struct progressbar*)token->value.data; + if (do_refresh) + draw_progressbar(gwps, info->line_number, bar); +#else /* HAVE_LCD_CHARCELL */ + if (do_refresh) + { + if (data->full_line_progressbar) + draw_player_fullbar(gwps, info->buf, info->buf_size); + else + draw_player_progress(gwps); + } +#endif + } + break; +#ifdef HAVE_LCD_BITMAP + case SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY: + { + char n = token->value.i & 0xFF; + int subimage = token->value.i >> 8; + struct gui_img *img = find_image(n, data); + if (img && img->loaded) + img->display = subimage; + break; + } +#ifdef HAVE_ALBUMART + case SKIN_TOKEN_ALBUMART_DISPLAY: + /* now draw the AA */ + if (data->albumart) + { + int handle = playback_current_aa_hid(data->playback_aa_slot); +#if CONFIG_TUNER + if (in_radio_screen() || (get_radio_status() != FMRADIO_OFF)) + { + struct dim dim = {data->albumart->width, data->albumart->height}; + handle = radio_get_art_hid(&dim); + } +#endif + data->albumart->draw_handle = handle; + } + break; +#endif + case SKIN_TOKEN_DRAW_INBUILTBAR: + gui_statusbar_draw(&(statusbars.statusbars[gwps->display->screen_type]), + info->refresh_type == SKIN_REFRESH_ALL, + token->value.data); + break; + case SKIN_TOKEN_VIEWPORT_CUSTOMLIST: + if (do_refresh) + draw_playlist_viewer_list(gwps, token->value.data); + break; + +#endif /* HAVE_LCD_BITMAP */ + default: + return false; + } + return true; +} + + + +static void do_tags_in_hidden_conditional(struct skin_element* branch, + struct skin_draw_info *info) +{ +#ifdef HAVE_LCD_BITMAP + struct gui_wps *gwps = info->gwps; + struct wps_data *data = gwps->data; +#endif + /* Tags here are ones which need to be "turned off" or cleared + * if they are in a conditional branch which isnt being used */ + if (branch->type == LINE_ALTERNATOR) + { + int i; + for (i=0; ichildren_count; i++) + { + do_tags_in_hidden_conditional(branch->children[i], info); + } + } + else if (branch->type == LINE && branch->children_count) + { + struct skin_element *child = branch->children[0]; + struct wps_token *token; + while (child) + { + if (child->type == CONDITIONAL) + { + int i; + for (i=0; ichildren_count; i++) + { + do_tags_in_hidden_conditional(child->children[i], info); + } + child = child->next; + continue; + } + else if (child->type != TAG || !child->data) + { + child = child->next; + continue; + } + token = (struct wps_token *)child->data; +#ifdef HAVE_LCD_BITMAP + /* clear all pictures in the conditional and nested ones */ + if (token->type == SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY) + { + struct gui_img *img = find_image(token->value.i&0xFF, data); + clear_image_pos(gwps, img); + } + else if (token->type == SKIN_TOKEN_PEAKMETER) + { + data->peak_meter_enabled = false; + } + else if (token->type == SKIN_TOKEN_VIEWPORT_ENABLE) + { + char label = token->value.i&0x7f; + struct skin_element *viewport; + for (viewport = data->tree; + viewport; + viewport = viewport->next) + { + struct skin_viewport *skin_viewport = (struct skin_viewport*)viewport->data; + if ((skin_viewport->label&0x7f) != label) + continue; + if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE) + { + continue; + } + if (skin_viewport->hidden_flags&VP_DRAW_HIDEABLE) + { + if (skin_viewport->hidden_flags&VP_DRAW_HIDDEN) + skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN; + else + { + gwps->display->set_viewport(&skin_viewport->vp); + gwps->display->clear_viewport(); + gwps->display->scroll_stop(&skin_viewport->vp); + gwps->display->set_viewport(&info->skin_vp->vp); + skin_viewport->hidden_flags |= VP_DRAW_HIDDEN; + } + } + } + } +#endif +#ifdef HAVE_ALBUMART + else if (data->albumart && token->type == SKIN_TOKEN_ALBUMART_DISPLAY) + { + draw_album_art(gwps, + playback_current_aa_hid(data->playback_aa_slot), true); + } +#endif + child = child->next; + } + } +} + +static void fix_line_alignment(struct skin_draw_info *info, struct skin_element *element) +{ + struct align_pos *align = &info->align; + char *cur_pos = info->cur_align_start + strlen(info->cur_align_start); + switch (element->tag->type) + { + case SKIN_TOKEN_ALIGN_LEFT: + *cur_pos = '\0'; cur_pos++; *cur_pos = '\0'; + align->left = cur_pos; + info->cur_align_start = cur_pos; + break; + case SKIN_TOKEN_ALIGN_LEFT_RTL: + *cur_pos = '\0'; cur_pos++; *cur_pos = '\0'; + if (lang_is_rtl()) + align->right = cur_pos; + else + align->left = cur_pos; + info->cur_align_start = cur_pos; + break; + case SKIN_TOKEN_ALIGN_CENTER: + *cur_pos = '\0'; cur_pos++; *cur_pos = '\0'; + align->center = cur_pos; + info->cur_align_start = cur_pos; + break; + case SKIN_TOKEN_ALIGN_RIGHT: + *cur_pos = '\0'; cur_pos++; *cur_pos = '\0'; + align->right = cur_pos; + info->cur_align_start = cur_pos; + break; + case SKIN_TOKEN_ALIGN_RIGHT_RTL: + *cur_pos = '\0'; cur_pos++; *cur_pos = '\0'; + if (lang_is_rtl()) + align->left = cur_pos; + else + align->right = cur_pos; + info->cur_align_start = cur_pos; + break; + default: + break; + } +} + +/* Draw a LINE element onto the display */ +bool skin_render_line(struct skin_element* line, struct skin_draw_info *info) +{ + bool needs_update = false; + int last_value, value; + + if (line->children_count == 0) + return false; /* empty line, do nothing */ + + struct skin_element *child = line->children[0]; + struct conditional *conditional; + skin_render_func func = skin_render_line; + char tempbuf[128]; + int old_refresh_mode = info->refresh_type; + while (child) + { + tempbuf[0] = '\0'; + switch (child->type) + { + case CONDITIONAL: + conditional = (struct conditional*)child->data; + last_value = conditional->last_value; + value = evaluate_conditional(info->gwps, conditional, child->children_count); + + if (value != 1 && value >= child->children_count) + value = child->children_count-1; + if (child->children_count == 1) + { + /* special handling so + * %?aa and %? need special handlng here */ + + if (value == 1) /* tag is false */ + { + /* we are in a false branch of a %?aa conditional */ + if (last_value == 0) + do_tags_in_hidden_conditional(child->children[0], info); + break; + } + value = 0; + } + else + { + if (last_value >= 0 && value != last_value && last_value < child->children_count) + do_tags_in_hidden_conditional(child->children[last_value], info); + } + if (child->children[value]->type == LINE_ALTERNATOR) + { + func = skin_render_alternator; + } + else if (child->children[value]->type == LINE) + func = skin_render_line; + + if (value != last_value) + { + info->refresh_type = SKIN_REFRESH_ALL; + info->force_redraw = true; + } + + if (func(child->children[value], info)) + needs_update = true; + else + needs_update = needs_update || (last_value != value); + + info->refresh_type = old_refresh_mode; + break; + case TAG: + if (child->tag->flags & NOBREAK) + info->no_line_break = true; + if (child->tag->type == SKIN_TOKEN_SUBLINE_SCROLL) + info->line_scrolls = true; + + fix_line_alignment(info, child); + + if (!child->data) + { + break; + } + if (!do_non_text_tags(info->gwps, info, child, &info->skin_vp->vp)) + { + const char *value = get_token_value(info->gwps, child->data, + tempbuf, sizeof(tempbuf), NULL); + if (value) + { + needs_update = needs_update || + ((child->tag->flags&info->refresh_type)!=0); + strlcat(info->cur_align_start, value, + info->buf_size - (info->cur_align_start-info->buf)); + } + } + break; + case TEXT: + strlcat(info->cur_align_start, child->data, + info->buf_size - (info->cur_align_start-info->buf)); + needs_update = needs_update || + (info->refresh_type&SKIN_REFRESH_STATIC) != 0; + break; + case COMMENT: + default: + break; + } + + child = child->next; + } + return needs_update; +} + +bool skin_render_alternator(struct skin_element* element, struct skin_draw_info *info) +{ + bool changed_lines = false; + struct line_alternator *alternator = (struct line_alternator*)element->data; + unsigned old_refresh = info->refresh_type; + if (info->refresh_type == SKIN_REFRESH_ALL) + { + alternator->current_line = 0; + alternator->last_change_tick = current_tick; + changed_lines = true; + } + else + { + struct skin_element *current_line = element->children[alternator->current_line]; + struct line *line = (struct line *)current_line->data; + int next_change = alternator->last_change_tick + line->timeout; + if (TIME_AFTER(current_tick, next_change)) + { + alternator->current_line++; + if (alternator->current_line >= element->children_count) + alternator->current_line = 0; + alternator->last_change_tick = current_tick; + changed_lines = true; + } + } + if (element->children[alternator->current_line]->children_count == 0) + { + /* skip empty sublines */ + alternator->current_line++; + if (alternator->current_line >= element->children_count) + alternator->current_line = 0; + changed_lines = true; + } + + if (changed_lines) + { + info->refresh_type = SKIN_REFRESH_ALL; + info->force_redraw = true; + } + bool ret = skin_render_line(element->children[alternator->current_line], info); + info->refresh_type = old_refresh; + return changed_lines || ret; +} + +void skin_render_viewport(struct skin_element* viewport, struct gui_wps *gwps, + struct skin_viewport* skin_viewport, unsigned long refresh_type) +{ + struct screen *display = gwps->display; + char linebuf[MAX_LINE]; + skin_render_func func = skin_render_line; + struct skin_element* line = viewport; + struct skin_draw_info info = { + .gwps = gwps, + .buf = linebuf, + .buf_size = sizeof(linebuf), + .line_number = 0, + .no_line_break = false, + .line_scrolls = false, + .refresh_type = refresh_type, + .skin_vp = skin_viewport + }; + + struct align_pos * align = &info.align; + bool needs_update; +#ifdef HAVE_LCD_BITMAP + /* Set images to not to be displayed */ + struct skin_token_list *imglist = gwps->data->images; + while (imglist) + { + struct gui_img *img = (struct gui_img *)imglist->token->value.data; + img->display = -1; + imglist = imglist->next; + } +#endif + + while (line) + { + linebuf[0] = '\0'; + info.no_line_break = false; + info.line_scrolls = false; + info.force_redraw = false; + + info.cur_align_start = info.buf; + align->left = info.buf; + align->center = NULL; + align->right = NULL; + + + if (line->type == LINE_ALTERNATOR) + func = skin_render_alternator; + else if (line->type == LINE) + func = skin_render_line; + + needs_update = func(line, &info); + + /* only update if the line needs to be, and there is something to write */ + if (refresh_type && needs_update) + { + if (info.line_scrolls) + { + /* if the line is a scrolling one we don't want to update + too often, so that it has the time to scroll */ + if ((refresh_type & SKIN_REFRESH_SCROLL) || info.force_redraw) + write_line(display, align, info.line_number, true); + } + else + write_line(display, align, info.line_number, false); + } + if (!info.no_line_break) + info.line_number++; + line = line->next; + } +#ifdef HAVE_LCD_BITMAP + wps_display_images(gwps, &skin_viewport->vp); +#endif +} + +void skin_render(struct gui_wps *gwps, unsigned refresh_mode) +{ + struct wps_data *data = gwps->data; + struct screen *display = gwps->display; + + struct skin_element* viewport = data->tree; + struct skin_viewport* skin_viewport; + + int old_refresh_mode = refresh_mode; + +#ifdef HAVE_LCD_CHARCELLS + int i; + for (i = 0; i < 8; i++) + { + if (data->wps_progress_pat[i] == 0) + data->wps_progress_pat[i] = display->get_locked_pattern(); + } +#endif + viewport = data->tree; + skin_viewport = (struct skin_viewport *)viewport->data; + if (skin_viewport->label == VP_DEFAULT_LABEL && viewport->next) + refresh_mode = 0; + + for (viewport = data->tree; + viewport; + viewport = viewport->next) + { + /* SETUP */ + skin_viewport = (struct skin_viewport*)viewport->data; + unsigned vp_refresh_mode = refresh_mode; +#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1) + skin_viewport->vp.fg_pattern = skin_viewport->start_fgcolour; + skin_viewport->vp.bg_pattern = skin_viewport->start_bgcolour; +#endif + + /* dont redraw the viewport if its disabled */ + if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE) + { /* don't draw anything into this one */ + vp_refresh_mode = 0; + } + else if ((skin_viewport->hidden_flags&VP_DRAW_HIDDEN)) + { + skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN; + continue; + } + else if (((skin_viewport->hidden_flags& + (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE)) + == (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE))) + { + vp_refresh_mode = SKIN_REFRESH_ALL; + skin_viewport->hidden_flags = VP_DRAW_HIDEABLE; + } + + display->set_viewport(&skin_viewport->vp); + if ((vp_refresh_mode&SKIN_REFRESH_ALL) == SKIN_REFRESH_ALL) + { + display->clear_viewport(); + } + /* render */ + skin_render_viewport(viewport->children[0], gwps, + skin_viewport, vp_refresh_mode); + refresh_mode = old_refresh_mode; + } + + /* Restore the default viewport */ + display->set_viewport(NULL); + display->update(); +} -- cgit v1.2.3