(hopefully) Fix FS#12639 and other skin related buflib corruption issues.
[maemo-rb.git] / apps / gui / skin_engine / skin_render.c
blobbf7f03d7382eeca7abcbc1ffc13e011228e6cd08
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id: skin_parser.c 26752 2010-06-10 21:22:16Z bieber $
10 * Copyright (C) 2010 Jonathan Gordon
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <stdbool.h>
26 #include <ctype.h>
27 #include "strlcat.h"
29 #include "config.h"
30 #include "core_alloc.h"
31 #include "kernel.h"
32 #include "appevents.h"
33 #ifdef HAVE_ALBUMART
34 #include "albumart.h"
35 #endif
36 #include "settings.h"
37 #include "skin_display.h"
38 #include "skin_engine.h"
39 #include "skin_parser.h"
40 #include "tag_table.h"
41 #include "skin_scan.h"
42 #if CONFIG_TUNER
43 #include "radio.h"
44 #endif
45 #include "viewport.h"
46 #include "cuesheet.h"
47 #include "language.h"
48 #include "playback.h"
49 #include "playlist.h"
50 #include "root_menu.h"
51 #include "misc.h"
52 #include "list.h"
55 #define MAX_LINE 1024
57 struct skin_draw_info {
58 struct gui_wps *gwps;
59 struct skin_viewport *skin_vp;
60 int line_number;
61 unsigned long refresh_type;
62 unsigned text_style;
64 char* cur_align_start;
65 struct align_pos align;
66 bool no_line_break;
67 bool line_scrolls;
68 bool force_redraw;
70 char *buf;
71 size_t buf_size;
73 int offset; /* used by the playlist viewer */
76 typedef bool (*skin_render_func)(struct skin_element* alternator, struct skin_draw_info *info);
77 bool skin_render_alternator(struct skin_element* alternator, struct skin_draw_info *info);
79 #ifdef HAVE_LCD_BITMAP
80 static void skin_render_playlistviewer(struct playlistviewer* viewer,
81 struct gui_wps *gwps,
82 struct skin_viewport* skin_viewport,
83 unsigned long refresh_type);
84 #endif
86 static char* skin_buffer;
88 static inline struct skin_element*
89 get_child(OFFSETTYPE(struct skin_element**) children, int child)
91 OFFSETTYPE(struct skin_element*) *kids = SKINOFFSETTOPTR(skin_buffer, children);
92 return SKINOFFSETTOPTR(skin_buffer, kids[child]);
96 static bool do_non_text_tags(struct gui_wps *gwps, struct skin_draw_info *info,
97 struct skin_element *element, struct viewport* vp)
99 #ifndef HAVE_LCD_BITMAP
100 (void)vp; /* silence warnings */
101 (void)info;
102 #endif
103 struct wps_token *token = (struct wps_token *)SKINOFFSETTOPTR(skin_buffer, element->data);
105 #ifdef HAVE_LCD_BITMAP
106 struct wps_data *data = gwps->data;
107 bool do_refresh = (element->tag->flags & info->refresh_type) > 0;
108 #endif
109 switch (token->type)
111 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
112 case SKIN_TOKEN_VIEWPORT_FGCOLOUR:
114 struct viewport_colour *col = SKINOFFSETTOPTR(skin_buffer, token->value.data);
115 struct viewport *vp = SKINOFFSETTOPTR(skin_buffer, col->vp);
116 vp->fg_pattern = col->colour;
118 break;
119 case SKIN_TOKEN_VIEWPORT_BGCOLOUR:
121 struct viewport_colour *col = SKINOFFSETTOPTR(skin_buffer, token->value.data);
122 struct viewport *vp = SKINOFFSETTOPTR(skin_buffer, col->vp);
123 vp->bg_pattern = col->colour;
125 break;
126 case SKIN_TOKEN_VIEWPORT_TEXTSTYLE:
127 info->text_style = token->value.l;
128 break;
129 #endif
130 #ifdef HAVE_LCD_COLOR
131 case SKIN_TOKEN_VIEWPORT_GRADIENT_SETUP:
133 struct gradient_config *cfg = SKINOFFSETTOPTR(skin_buffer, token->value.data);
134 vp->lss_pattern = cfg->start;
135 vp->lse_pattern = cfg->end;
136 vp->lst_pattern = cfg->text;
138 break;
139 #endif
140 case SKIN_TOKEN_VIEWPORT_ENABLE:
142 char *label = SKINOFFSETTOPTR(skin_buffer, token->value.data);
143 char temp = VP_DRAW_HIDEABLE;
144 struct skin_element *viewport = SKINOFFSETTOPTR(skin_buffer, gwps->data->tree);
145 while (viewport)
147 struct skin_viewport *skinvp = SKINOFFSETTOPTR(skin_buffer, viewport->data);
149 char *vplabel = SKINOFFSETTOPTR(skin_buffer, skinvp->label);
150 if (skinvp->label == VP_DEFAULT_LABEL)
151 vplabel = VP_DEFAULT_LABEL_STRING;
152 if (vplabel && !skinvp->is_infovp &&
153 !strcmp(vplabel, label))
155 if (skinvp->hidden_flags&VP_DRAW_HIDDEN)
157 temp |= VP_DRAW_WASHIDDEN;
159 skinvp->hidden_flags = temp;
161 viewport = SKINOFFSETTOPTR(skin_buffer, viewport->next);
164 break;
165 #ifdef HAVE_LCD_BITMAP
166 case SKIN_TOKEN_LIST_ITEM_CFG:
167 skinlist_set_cfg(gwps->display->screen_type,
168 SKINOFFSETTOPTR(skin_buffer, token->value.data));
169 break;
170 case SKIN_TOKEN_UIVIEWPORT_ENABLE:
171 sb_set_info_vp(gwps->display->screen_type, token->value.data);
172 break;
173 case SKIN_TOKEN_PEAKMETER:
174 data->peak_meter_enabled = true;
175 if (do_refresh)
176 draw_peakmeters(gwps, info->line_number, vp);
177 break;
178 case SKIN_TOKEN_DRAWRECTANGLE:
179 if (do_refresh)
181 struct draw_rectangle *rect =
182 SKINOFFSETTOPTR(skin_buffer, token->value.data);
183 #ifdef HAVE_LCD_COLOR
184 if (rect->start_colour != rect->end_colour &&
185 gwps->display->screen_type == SCREEN_MAIN)
187 gwps->display->gradient_fillrect(rect->x, rect->y, rect->width,
188 rect->height, rect->start_colour, rect->end_colour);
190 else
191 #endif
193 #if LCD_DEPTH > 1
194 unsigned backup = vp->fg_pattern;
195 vp->fg_pattern = rect->start_colour;
196 #endif
197 gwps->display->fillrect(rect->x, rect->y, rect->width,
198 rect->height);
199 #if LCD_DEPTH > 1
200 vp->fg_pattern = backup;
201 #endif
204 break;
205 case SKIN_TOKEN_PEAKMETER_LEFTBAR:
206 case SKIN_TOKEN_PEAKMETER_RIGHTBAR:
207 data->peak_meter_enabled = true;
208 /* fall through to the progressbar code */
209 #endif
210 case SKIN_TOKEN_VOLUMEBAR:
211 case SKIN_TOKEN_BATTERY_PERCENTBAR:
212 #ifdef HAVE_LCD_BITMAP
213 case SKIN_TOKEN_PROGRESSBAR:
214 case SKIN_TOKEN_TUNER_RSSI_BAR:
215 case SKIN_TOKEN_LIST_SCROLLBAR:
217 struct progressbar *bar = (struct progressbar*)SKINOFFSETTOPTR(skin_buffer, token->value.data);
218 if (do_refresh)
219 draw_progressbar(gwps, info->line_number, bar);
221 #endif
222 break;
223 #ifdef HAVE_LCD_BITMAP
224 case SKIN_TOKEN_IMAGE_DISPLAY:
226 struct gui_img *img = SKINOFFSETTOPTR(skin_buffer, token->value.data);
227 if (img && img->loaded && do_refresh)
228 img->display = 0;
230 break;
231 case SKIN_TOKEN_IMAGE_DISPLAY_LISTICON:
232 case SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY:
234 struct image_display *id = SKINOFFSETTOPTR(skin_buffer, token->value.data);
235 const char* label = SKINOFFSETTOPTR(skin_buffer, id->label);
236 struct gui_img *img = skin_find_item(label,SKIN_FIND_IMAGE, data);
237 if (img && img->loaded)
239 if (SKINOFFSETTOPTR(skin_buffer, id->token) == NULL)
241 img->display = id->subimage;
243 else
245 char buf[16];
246 const char *out;
247 int a = img->num_subimages;
248 out = get_token_value(gwps, SKINOFFSETTOPTR(skin_buffer, id->token),
249 info->offset, buf, sizeof(buf), &a);
251 /* NOTE: get_token_value() returns values starting at 1! */
252 if (a == -1)
253 a = (out && *out) ? 1 : 2;
254 if (token->type == SKIN_TOKEN_IMAGE_DISPLAY_LISTICON)
255 a -= 2; /* 2 is added in statusbar-skinned.c! */
256 else
257 a--;
258 a += id->offset;
260 /* Clear the image, as in conditionals */
261 clear_image_pos(gwps, img);
263 /* If the token returned a value which is higher than
264 * the amount of subimages, don't draw it. */
265 if (a >= 0 && a < img->num_subimages)
267 img->display = a;
271 break;
273 #ifdef HAVE_ALBUMART
274 case SKIN_TOKEN_ALBUMART_DISPLAY:
276 struct skin_albumart *aa = SKINOFFSETTOPTR(skin_buffer, data->albumart);
277 /* now draw the AA */
278 if (do_refresh && aa)
280 int handle = playback_current_aa_hid(data->playback_aa_slot);
281 #if CONFIG_TUNER
282 if (in_radio_screen() || (get_radio_status() != FMRADIO_OFF))
284 struct dim dim = {aa->width, aa->height};
285 handle = radio_get_art_hid(&dim);
287 #endif
288 aa->draw_handle = handle;
290 break;
292 #endif
293 case SKIN_TOKEN_DRAW_INBUILTBAR:
294 gui_statusbar_draw(&(statusbars.statusbars[gwps->display->screen_type]),
295 info->refresh_type == SKIN_REFRESH_ALL,
296 SKINOFFSETTOPTR(skin_buffer, token->value.data));
297 break;
298 case SKIN_TOKEN_VIEWPORT_CUSTOMLIST:
299 if (do_refresh)
300 skin_render_playlistviewer(SKINOFFSETTOPTR(skin_buffer, token->value.data), gwps,
301 info->skin_vp, info->refresh_type);
302 break;
304 #endif /* HAVE_LCD_BITMAP */
305 #ifdef HAVE_SKIN_VARIABLES
306 case SKIN_TOKEN_VAR_SET:
308 struct skin_var_changer *data = SKINOFFSETTOPTR(skin_buffer, token->value.data);
309 struct skin_var *var = SKINOFFSETTOPTR(skin_buffer, data->var);
310 if (data->direct)
311 var->value = data->newval;
312 else
314 var->value += data->newval;
315 if (data->max)
317 if (var->value > data->max)
318 var->value = 1;
319 else if (var->value < 1)
320 var->value = data->max;
323 if (var->value < 1)
324 var->value = 1;
325 var->last_changed = current_tick;
327 break;
328 #endif
329 default:
330 return false;
332 return true;
337 static void do_tags_in_hidden_conditional(struct skin_element* branch,
338 struct skin_draw_info *info)
340 #ifdef HAVE_LCD_BITMAP
341 struct gui_wps *gwps = info->gwps;
342 struct wps_data *data = gwps->data;
343 #endif
344 /* Tags here are ones which need to be "turned off" or cleared
345 * if they are in a conditional branch which isnt being used */
346 if (branch->type == LINE_ALTERNATOR)
348 int i;
349 for (i=0; i<branch->children_count; i++)
351 do_tags_in_hidden_conditional(get_child(branch->children, i), info);
354 else if (branch->type == LINE && branch->children_count)
356 struct skin_element *child = get_child(branch->children, 0);
357 #if defined(HAVE_LCD_BITMAP) || defined(HAVE_ALBUMART)
358 struct wps_token *token;
359 #endif
360 while (child)
362 if (child->type == CONDITIONAL)
364 int i;
365 for (i=0; i<child->children_count; i++)
367 do_tags_in_hidden_conditional(get_child(child->children, i), info);
369 child = SKINOFFSETTOPTR(skin_buffer, child->next);
370 continue;
372 else if (child->type != TAG || !SKINOFFSETTOPTR(skin_buffer, child->data))
374 child = SKINOFFSETTOPTR(skin_buffer, child->next);
375 continue;
377 #if defined(HAVE_LCD_BITMAP) || defined(HAVE_ALBUMART)
378 token = (struct wps_token *)SKINOFFSETTOPTR(skin_buffer, child->data);
379 #endif
380 #ifdef HAVE_LCD_BITMAP
381 /* clear all pictures in the conditional and nested ones */
382 if (token->type == SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY)
384 struct image_display *id = SKINOFFSETTOPTR(skin_buffer, token->value.data);
385 struct gui_img *img = skin_find_item(SKINOFFSETTOPTR(skin_buffer, id->label),
386 SKIN_FIND_IMAGE, data);
387 clear_image_pos(gwps, img);
389 else if (token->type == SKIN_TOKEN_PEAKMETER)
391 data->peak_meter_enabled = false;
393 else if (token->type == SKIN_TOKEN_VIEWPORT_ENABLE)
395 char *label = SKINOFFSETTOPTR(skin_buffer, token->value.data);
396 struct skin_element *viewport;
397 for (viewport = SKINOFFSETTOPTR(skin_buffer, data->tree);
398 viewport;
399 viewport = SKINOFFSETTOPTR(skin_buffer, viewport->next))
401 struct skin_viewport *skin_viewport = SKINOFFSETTOPTR(skin_buffer, viewport->data);
403 char *vplabel = SKINOFFSETTOPTR(skin_buffer, skin_viewport->label);
404 if (skin_viewport->label == VP_DEFAULT_LABEL)
405 vplabel = VP_DEFAULT_LABEL_STRING;
406 if (vplabel && strcmp(vplabel, label))
407 continue;
408 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
410 continue;
412 if (skin_viewport->hidden_flags&VP_DRAW_HIDEABLE)
414 if (skin_viewport->hidden_flags&VP_DRAW_HIDDEN)
415 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
416 else
418 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
419 if (skin_viewport->output_to_backdrop_buffer)
421 void *backdrop = skin_backdrop_get_buffer(data->backdrop_id);
422 gwps->display->set_framebuffer(backdrop);
423 skin_backdrop_show(-1);
425 #endif
426 gwps->display->set_viewport(&skin_viewport->vp);
427 gwps->display->clear_viewport();
428 gwps->display->scroll_stop(&skin_viewport->vp);
429 gwps->display->set_viewport(&info->skin_vp->vp);
430 skin_viewport->hidden_flags |= VP_DRAW_HIDDEN;
432 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
433 if (skin_viewport->output_to_backdrop_buffer)
435 gwps->display->set_framebuffer(NULL);
436 skin_backdrop_show(data->backdrop_id);
438 #endif
443 #endif
444 #ifdef HAVE_ALBUMART
445 else if (data->albumart && token->type == SKIN_TOKEN_ALBUMART_DISPLAY)
447 draw_album_art(gwps,
448 playback_current_aa_hid(data->playback_aa_slot), true);
450 #endif
451 child = SKINOFFSETTOPTR(skin_buffer, child->next);
456 static void fix_line_alignment(struct skin_draw_info *info, struct skin_element *element)
458 struct align_pos *align = &info->align;
459 char *cur_pos = info->cur_align_start + strlen(info->cur_align_start);
460 switch (element->tag->type)
462 case SKIN_TOKEN_ALIGN_LEFT:
463 *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
464 align->left = cur_pos;
465 info->cur_align_start = cur_pos;
466 break;
467 case SKIN_TOKEN_ALIGN_LEFT_RTL:
468 *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
469 if (lang_is_rtl())
470 align->right = cur_pos;
471 else
472 align->left = cur_pos;
473 info->cur_align_start = cur_pos;
474 break;
475 case SKIN_TOKEN_ALIGN_CENTER:
476 *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
477 align->center = cur_pos;
478 info->cur_align_start = cur_pos;
479 break;
480 case SKIN_TOKEN_ALIGN_RIGHT:
481 *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
482 align->right = cur_pos;
483 info->cur_align_start = cur_pos;
484 break;
485 case SKIN_TOKEN_ALIGN_RIGHT_RTL:
486 *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
487 if (lang_is_rtl())
488 align->left = cur_pos;
489 else
490 align->right = cur_pos;
491 info->cur_align_start = cur_pos;
492 break;
493 default:
494 break;
498 /* Draw a LINE element onto the display */
499 static bool skin_render_line(struct skin_element* line, struct skin_draw_info *info)
501 bool needs_update = false;
502 int last_value, value;
504 if (line->children_count == 0)
505 return false; /* empty line, do nothing */
507 struct skin_element *child = get_child(line->children, 0);
508 struct conditional *conditional;
509 skin_render_func func = skin_render_line;
510 int old_refresh_mode = info->refresh_type;
511 while (child)
513 switch (child->type)
515 case CONDITIONAL:
516 conditional = SKINOFFSETTOPTR(skin_buffer, child->data);
517 last_value = conditional->last_value;
518 value = evaluate_conditional(info->gwps, info->offset,
519 conditional, child->children_count);
520 conditional->last_value = value;
521 if (child->children_count == 1)
523 /* special handling so
524 * %?aa<true> and %?<true|false> need special handlng here */
526 if (value == -1) /* tag is false */
528 /* we are in a false branch of a %?aa<true> conditional */
529 if (last_value == 0)
530 do_tags_in_hidden_conditional(get_child(child->children, 0), info);
531 break;
534 else
536 if (last_value >= 0 && value != last_value && last_value < child->children_count)
537 do_tags_in_hidden_conditional(get_child(child->children, last_value), info);
539 if (get_child(child->children, value)->type == LINE_ALTERNATOR)
541 func = skin_render_alternator;
543 else if (get_child(child->children, value)->type == LINE)
544 func = skin_render_line;
546 if (value != last_value)
548 info->refresh_type = SKIN_REFRESH_ALL;
549 info->force_redraw = true;
552 if (func(get_child(child->children, value), info))
553 needs_update = true;
554 else
555 needs_update = needs_update || (last_value != value);
557 info->refresh_type = old_refresh_mode;
558 break;
559 case TAG:
560 if (child->tag->flags & NOBREAK)
561 info->no_line_break = true;
562 if (child->tag->type == SKIN_TOKEN_SUBLINE_SCROLL)
563 info->line_scrolls = true;
565 fix_line_alignment(info, child);
567 if (!SKINOFFSETTOPTR(skin_buffer, child->data))
569 break;
571 if (!do_non_text_tags(info->gwps, info, child, &info->skin_vp->vp))
573 static char tempbuf[128];
574 const char *valuestr = get_token_value(info->gwps, SKINOFFSETTOPTR(skin_buffer, child->data),
575 info->offset, tempbuf,
576 sizeof(tempbuf), NULL);
577 if (valuestr)
579 #if CONFIG_RTC
580 if (child->tag->flags&SKIN_RTC_REFRESH)
581 needs_update = needs_update || info->refresh_type&SKIN_REFRESH_DYNAMIC;
582 #endif
583 needs_update = needs_update ||
584 ((child->tag->flags&info->refresh_type)!=0);
585 strlcat(info->cur_align_start, valuestr,
586 info->buf_size - (info->cur_align_start-info->buf));
589 break;
590 case TEXT:
591 strlcat(info->cur_align_start, SKINOFFSETTOPTR(skin_buffer, child->data),
592 info->buf_size - (info->cur_align_start-info->buf));
593 needs_update = needs_update ||
594 (info->refresh_type&SKIN_REFRESH_STATIC) != 0;
595 break;
596 case COMMENT:
597 default:
598 break;
601 child = SKINOFFSETTOPTR(skin_buffer, child->next);
603 return needs_update;
606 static int get_subline_timeout(struct gui_wps *gwps, struct skin_element* line)
608 struct skin_element *element=line;
609 struct wps_token *token;
610 int retval = DEFAULT_SUBLINE_TIME_MULTIPLIER*TIMEOUT_UNIT;
611 if (element->type == LINE)
613 if (element->children_count == 0)
614 return retval; /* empty line, so force redraw */
615 element = get_child(element->children, 0);
617 while (element)
619 if (element->type == TAG &&
620 element->tag->type == SKIN_TOKEN_SUBLINE_TIMEOUT )
622 token = SKINOFFSETTOPTR(skin_buffer, element->data);
623 return token->value.i;
625 else if (element->type == CONDITIONAL)
627 struct conditional *conditional = SKINOFFSETTOPTR(skin_buffer, element->data);
628 int val = evaluate_conditional(gwps, 0, conditional,
629 element->children_count);
630 if (val >= 0)
632 retval = get_subline_timeout(gwps, get_child(element->children, val));
633 if (retval >= 0)
634 return retval;
637 element = SKINOFFSETTOPTR(skin_buffer, element->next);
639 return retval;
642 bool skin_render_alternator(struct skin_element* element, struct skin_draw_info *info)
644 bool changed_lines = false;
645 struct line_alternator *alternator = SKINOFFSETTOPTR(skin_buffer, element->data);
646 unsigned old_refresh = info->refresh_type;
647 if (info->refresh_type == SKIN_REFRESH_ALL)
649 alternator->current_line = element->children_count-1;
650 changed_lines = true;
652 else if (TIME_AFTER(current_tick, alternator->next_change_tick))
654 changed_lines = true;
657 if (changed_lines)
659 struct skin_element *current_line;
660 int start = alternator->current_line;
661 int try_line = start;
662 bool suitable = false;
663 int rettimeout = DEFAULT_SUBLINE_TIME_MULTIPLIER*TIMEOUT_UNIT;
665 /* find a subline which has at least one token in it,
666 * and that line doesnt have a timeout set to 0 through conditionals */
667 do {
668 try_line++;
669 if (try_line >= element->children_count)
670 try_line = 0;
671 if (get_child(element->children, try_line)->children_count != 0)
673 current_line = get_child(element->children, try_line);
674 rettimeout = get_subline_timeout(info->gwps,
675 get_child(current_line->children, 0));
676 if (rettimeout > 0)
678 suitable = true;
682 while (try_line != start && !suitable);
684 if (suitable)
686 alternator->current_line = try_line;
687 alternator->next_change_tick = current_tick + rettimeout;
690 info->refresh_type = SKIN_REFRESH_ALL;
691 info->force_redraw = true;
693 bool ret = skin_render_line(get_child(element->children, alternator->current_line), info);
694 info->refresh_type = old_refresh;
695 return changed_lines || ret;
698 void skin_render_viewport(struct skin_element* viewport, struct gui_wps *gwps,
699 struct skin_viewport* skin_viewport, unsigned long refresh_type)
701 struct screen *display = gwps->display;
702 char linebuf[MAX_LINE];
703 skin_render_func func = skin_render_line;
704 struct skin_element* line = viewport;
705 struct skin_draw_info info = {
706 .gwps = gwps,
707 .buf = linebuf,
708 .buf_size = sizeof(linebuf),
709 .line_number = 0,
710 .no_line_break = false,
711 .line_scrolls = false,
712 .refresh_type = refresh_type,
713 .skin_vp = skin_viewport,
714 .offset = 0,
715 .text_style = STYLE_DEFAULT
718 struct align_pos * align = &info.align;
719 bool needs_update;
720 skin_buffer = get_skin_buffer(gwps->data);
721 #ifdef HAVE_LCD_BITMAP
722 /* Set images to not to be displayed */
723 struct skin_token_list *imglist = SKINOFFSETTOPTR(skin_buffer, gwps->data->images);
724 while (imglist)
726 struct wps_token *token = SKINOFFSETTOPTR(skin_buffer, imglist->token);
727 struct gui_img *img = (struct gui_img *)SKINOFFSETTOPTR(skin_buffer, token->value.data);
728 img->display = -1;
729 imglist = SKINOFFSETTOPTR(skin_buffer, imglist->next);
732 /* fix font ID's */
733 if (skin_viewport->parsed_fontid == 1)
734 skin_viewport->vp.font = display->getuifont();
735 #endif
737 while (line)
739 linebuf[0] = '\0';
740 info.no_line_break = false;
741 info.line_scrolls = false;
742 info.force_redraw = false;
743 #ifdef HAVE_LCD_COLOR
744 if (info.text_style&STYLE_GRADIENT)
746 int cur = CURLN_UNPACK(info.text_style);
747 int num = NUMLN_UNPACK(info.text_style);
748 if (cur+1 == num)
749 info.text_style = STYLE_DEFAULT;
750 else
751 info.text_style = STYLE_GRADIENT|CURLN_PACK(cur+1)|NUMLN_PACK(num);
753 #endif
754 info.cur_align_start = info.buf;
755 align->left = info.buf;
756 align->center = NULL;
757 align->right = NULL;
760 if (line->type == LINE_ALTERNATOR)
761 func = skin_render_alternator;
762 else if (line->type == LINE)
763 func = skin_render_line;
765 needs_update = func(line, &info);
766 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
767 if (skin_viewport->vp.fg_pattern != skin_viewport->start_fgcolour ||
768 skin_viewport->vp.bg_pattern != skin_viewport->start_bgcolour)
770 /* 2bit lcd drivers need lcd_set_viewport() to be called to change
771 * the colour, 16bit doesnt. But doing this makes static text
772 * get the new colour also */
773 needs_update = true;
774 display->set_viewport(&skin_viewport->vp);
776 #endif
777 /* only update if the line needs to be, and there is something to write */
778 if (refresh_type && needs_update)
780 if (info.force_redraw)
781 display->scroll_stop_line(&skin_viewport->vp, info.line_number);
782 write_line(display, align, info.line_number,
783 info.line_scrolls, info.text_style);
785 if (!info.no_line_break)
786 info.line_number++;
787 line = SKINOFFSETTOPTR(skin_buffer, line->next);
789 #ifdef HAVE_LCD_BITMAP
790 wps_display_images(gwps, &skin_viewport->vp);
791 #endif
794 void skin_render(struct gui_wps *gwps, unsigned refresh_mode)
796 struct wps_data *data = gwps->data;
797 struct screen *display = gwps->display;
799 struct skin_element* viewport;
800 struct skin_viewport* skin_viewport;
801 char *label;
803 int old_refresh_mode = refresh_mode;
804 skin_buffer = get_skin_buffer(gwps->data);
806 #ifdef HAVE_LCD_CHARCELLS
807 int i;
808 for (i = 0; i < 8; i++)
810 if (data->wps_progress_pat[i] == 0)
811 data->wps_progress_pat[i] = display->get_locked_pattern();
813 #endif
815 viewport = SKINOFFSETTOPTR(skin_buffer, data->tree);
816 skin_viewport = SKINOFFSETTOPTR(skin_buffer, viewport->data);
817 label = SKINOFFSETTOPTR(skin_buffer, skin_viewport->label);
818 if (skin_viewport->label == VP_DEFAULT_LABEL)
819 label = VP_DEFAULT_LABEL_STRING;
820 if (label && SKINOFFSETTOPTR(skin_buffer, viewport->next) &&
821 !strcmp(label,VP_DEFAULT_LABEL_STRING))
822 refresh_mode = 0;
824 for (viewport = SKINOFFSETTOPTR(skin_buffer, data->tree);
825 viewport;
826 viewport = SKINOFFSETTOPTR(skin_buffer, viewport->next))
828 /* SETUP */
829 skin_viewport = SKINOFFSETTOPTR(skin_buffer, viewport->data);
830 unsigned vp_refresh_mode = refresh_mode;
831 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)
832 skin_viewport->vp.fg_pattern = skin_viewport->start_fgcolour;
833 skin_viewport->vp.bg_pattern = skin_viewport->start_bgcolour;
834 if (skin_viewport->output_to_backdrop_buffer)
836 display->set_framebuffer(skin_backdrop_get_buffer(data->backdrop_id));
837 skin_backdrop_show(-1);
839 else
841 display->set_framebuffer(NULL);
842 skin_backdrop_show(data->backdrop_id);
844 #endif
845 #ifdef HAVE_LCD_COLOR
846 skin_viewport->vp.lss_pattern = skin_viewport->start_gradient.start;
847 skin_viewport->vp.lse_pattern = skin_viewport->start_gradient.end;
848 skin_viewport->vp.lst_pattern = skin_viewport->start_gradient.text;
849 #endif
851 /* dont redraw the viewport if its disabled */
852 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
853 { /* don't draw anything into this one */
854 vp_refresh_mode = 0;
856 else if ((skin_viewport->hidden_flags&VP_DRAW_HIDDEN))
858 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
859 continue;
861 else if (((skin_viewport->hidden_flags&
862 (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE))
863 == (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE)))
865 vp_refresh_mode = SKIN_REFRESH_ALL;
866 skin_viewport->hidden_flags = VP_DRAW_HIDEABLE;
869 display->set_viewport(&skin_viewport->vp);
870 if ((vp_refresh_mode&SKIN_REFRESH_ALL) == SKIN_REFRESH_ALL)
872 display->clear_viewport();
874 /* render */
875 if (viewport->children_count)
876 skin_render_viewport(get_child(viewport->children, 0), gwps,
877 skin_viewport, vp_refresh_mode);
878 refresh_mode = old_refresh_mode;
880 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
881 display->set_framebuffer(NULL);
882 skin_backdrop_show(data->backdrop_id);
883 #endif
885 if (((refresh_mode&SKIN_REFRESH_ALL) == SKIN_REFRESH_ALL))
887 /* If this is the UI viewport then let the UI know
888 * to redraw itself */
889 send_event(GUI_EVENT_NEED_UI_UPDATE, NULL);
891 /* Restore the default viewport */
892 display->set_viewport(NULL);
893 display->update();
896 #ifdef HAVE_LCD_BITMAP
897 static __attribute__((noinline))
898 void skin_render_playlistviewer(struct playlistviewer* viewer,
899 struct gui_wps *gwps,
900 struct skin_viewport* skin_viewport,
901 unsigned long refresh_type)
903 struct screen *display = gwps->display;
904 char linebuf[MAX_LINE];
905 skin_render_func func = skin_render_line;
906 struct skin_element* line;
907 struct skin_draw_info info = {
908 .gwps = gwps,
909 .buf = linebuf,
910 .buf_size = sizeof(linebuf),
911 .line_number = 0,
912 .no_line_break = false,
913 .line_scrolls = false,
914 .refresh_type = refresh_type,
915 .skin_vp = skin_viewport,
916 .offset = viewer->start_offset,
917 .text_style = STYLE_DEFAULT
920 struct align_pos * align = &info.align;
921 bool needs_update;
922 int cur_pos, start_item, max;
923 int nb_lines = viewport_get_nb_lines(SKINOFFSETTOPTR(skin_buffer, viewer->vp));
924 #if CONFIG_TUNER
925 if (get_current_activity() == ACTIVITY_FM)
927 cur_pos = radio_current_preset();
928 start_item = cur_pos + viewer->start_offset;
929 max = start_item+radio_preset_count();
931 else
932 #endif
934 struct cuesheet *cue = skin_get_global_state()->id3 ?
935 skin_get_global_state()->id3->cuesheet : NULL;
936 cur_pos = playlist_get_display_index();
937 max = playlist_amount()+1;
938 if (cue)
939 max += cue->track_count;
940 start_item = MAX(0, cur_pos + viewer->start_offset);
942 if (max-start_item > nb_lines)
943 max = start_item + nb_lines;
945 line = SKINOFFSETTOPTR(skin_buffer, viewer->line);
946 while (start_item < max)
948 linebuf[0] = '\0';
949 info.no_line_break = false;
950 info.line_scrolls = false;
951 info.force_redraw = false;
953 info.cur_align_start = info.buf;
954 align->left = info.buf;
955 align->center = NULL;
956 align->right = NULL;
959 if (line->type == LINE_ALTERNATOR)
960 func = skin_render_alternator;
961 else if (line->type == LINE)
962 func = skin_render_line;
964 needs_update = func(line, &info);
966 /* only update if the line needs to be, and there is something to write */
967 if (refresh_type && needs_update)
969 if (!info.force_redraw)
970 display->scroll_stop_line(&skin_viewport->vp, info.line_number);
971 write_line(display, align, info.line_number,
972 info.line_scrolls, info.text_style);
974 info.line_number++;
975 info.offset++;
976 start_item++;
979 #endif