summaryrefslogtreecommitdiff
path: root/apps/gui/skin_engine/skin_render.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/gui/skin_engine/skin_render.c')
-rw-r--r--apps/gui/skin_engine/skin_render.c614
1 files changed, 614 insertions, 0 deletions
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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id: skin_parser.c 26752 2010-06-10 21:22:16Z bieber $
9 *
10 * Copyright (C) 2010 Jonathan Gordon
11 *
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.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include <stdlib.h>
23#include <stdio.h>
24#include <string.h>
25#include <stdbool.h>
26#include <ctype.h>
27#include "strlcat.h"
28
29#include "config.h"
30#include "kernel.h"
31#ifdef HAVE_ALBUMART
32#include "albumart.h"
33#endif
34#include "skin_display.h"
35#include "skin_engine.h"
36#include "skin_parser.h"
37#include "tag_table.h"
38#include "skin_scan.h"
39#if CONFIG_TUNER
40#include "radio.h"
41#endif
42#include "language.h"
43#include "playback.h"
44
45
46#define MAX_LINE 1024
47
48struct skin_draw_info {
49 struct gui_wps *gwps;
50 struct skin_viewport *skin_vp;
51 int line_number;
52 unsigned long refresh_type;
53
54 char* cur_align_start;
55 struct align_pos align;
56 bool no_line_break;
57 bool line_scrolls;
58 bool force_redraw;
59
60 char *buf;
61 size_t buf_size;
62};
63
64typedef bool (*skin_render_func)(struct skin_element* alternator, struct skin_draw_info *info);
65bool skin_render_alternator(struct skin_element* alternator, struct skin_draw_info *info);
66
67
68static bool do_non_text_tags(struct gui_wps *gwps, struct skin_draw_info *info,
69 struct skin_element *element, struct viewport* vp)
70{
71#ifndef HAVE_LCD_BITMAP
72 (void)vp; /* silence warnings */
73#endif
74 struct wps_token *token = (struct wps_token *)element->data;
75 struct wps_data *data = gwps->data;
76 bool do_refresh = (element->tag->flags & info->refresh_type) > 0;
77 switch (token->type)
78 {
79#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
80 case SKIN_TOKEN_VIEWPORT_FGCOLOUR:
81 {
82 struct viewport_colour *col = token->value.data;
83 col->vp->fg_pattern = col->colour;
84 }
85 break;
86 case SKIN_TOKEN_VIEWPORT_BGCOLOUR:
87 {
88 struct viewport_colour *col = token->value.data;
89 col->vp->bg_pattern = col->colour;
90 }
91 break;
92#endif
93 case SKIN_TOKEN_VIEWPORT_ENABLE:
94 {
95 char label = token->value.i;
96 char temp = VP_DRAW_HIDEABLE;
97 struct skin_element *viewport = gwps->data->tree;
98 while (viewport)
99 {
100 struct skin_viewport *skinvp = (struct skin_viewport*)viewport->data;
101 if (skinvp->label == label)
102 {
103 if (skinvp->hidden_flags&VP_DRAW_HIDDEN)
104 {
105 temp |= VP_DRAW_WASHIDDEN;
106 }
107 skinvp->hidden_flags = temp;
108 }
109 viewport = viewport->next;
110 }
111 }
112 break;
113#ifdef HAVE_LCD_BITMAP
114 case SKIN_TOKEN_UIVIEWPORT_ENABLE:
115 sb_set_info_vp(gwps->display->screen_type,
116 token->value.i|VP_INFO_LABEL);
117 break;
118 case SKIN_TOKEN_PEAKMETER:
119 data->peak_meter_enabled = true;
120 if (do_refresh)
121 draw_peakmeters(gwps, info->line_number, vp);
122 break;
123#endif
124 case SKIN_TOKEN_VOLUMEBAR:
125 case SKIN_TOKEN_BATTERY_PERCENTBAR:
126 case SKIN_TOKEN_PROGRESSBAR:
127 {
128#ifdef HAVE_LCD_BITMAP
129 struct progressbar *bar = (struct progressbar*)token->value.data;
130 if (do_refresh)
131 draw_progressbar(gwps, info->line_number, bar);
132#else /* HAVE_LCD_CHARCELL */
133 if (do_refresh)
134 {
135 if (data->full_line_progressbar)
136 draw_player_fullbar(gwps, info->buf, info->buf_size);
137 else
138 draw_player_progress(gwps);
139 }
140#endif
141 }
142 break;
143#ifdef HAVE_LCD_BITMAP
144 case SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY:
145 {
146 char n = token->value.i & 0xFF;
147 int subimage = token->value.i >> 8;
148 struct gui_img *img = find_image(n, data);
149 if (img && img->loaded)
150 img->display = subimage;
151 break;
152 }
153#ifdef HAVE_ALBUMART
154 case SKIN_TOKEN_ALBUMART_DISPLAY:
155 /* now draw the AA */
156 if (data->albumart)
157 {
158 int handle = playback_current_aa_hid(data->playback_aa_slot);
159#if CONFIG_TUNER
160 if (in_radio_screen() || (get_radio_status() != FMRADIO_OFF))
161 {
162 struct dim dim = {data->albumart->width, data->albumart->height};
163 handle = radio_get_art_hid(&dim);
164 }
165#endif
166 data->albumart->draw_handle = handle;
167 }
168 break;
169#endif
170 case SKIN_TOKEN_DRAW_INBUILTBAR:
171 gui_statusbar_draw(&(statusbars.statusbars[gwps->display->screen_type]),
172 info->refresh_type == SKIN_REFRESH_ALL,
173 token->value.data);
174 break;
175 case SKIN_TOKEN_VIEWPORT_CUSTOMLIST:
176 if (do_refresh)
177 draw_playlist_viewer_list(gwps, token->value.data);
178 break;
179
180#endif /* HAVE_LCD_BITMAP */
181 default:
182 return false;
183 }
184 return true;
185}
186
187
188
189static void do_tags_in_hidden_conditional(struct skin_element* branch,
190 struct skin_draw_info *info)
191{
192#ifdef HAVE_LCD_BITMAP
193 struct gui_wps *gwps = info->gwps;
194 struct wps_data *data = gwps->data;
195#endif
196 /* Tags here are ones which need to be "turned off" or cleared
197 * if they are in a conditional branch which isnt being used */
198 if (branch->type == LINE_ALTERNATOR)
199 {
200 int i;
201 for (i=0; i<branch->children_count; i++)
202 {
203 do_tags_in_hidden_conditional(branch->children[i], info);
204 }
205 }
206 else if (branch->type == LINE && branch->children_count)
207 {
208 struct skin_element *child = branch->children[0];
209 struct wps_token *token;
210 while (child)
211 {
212 if (child->type == CONDITIONAL)
213 {
214 int i;
215 for (i=0; i<child->children_count; i++)
216 {
217 do_tags_in_hidden_conditional(child->children[i], info);
218 }
219 child = child->next;
220 continue;
221 }
222 else if (child->type != TAG || !child->data)
223 {
224 child = child->next;
225 continue;
226 }
227 token = (struct wps_token *)child->data;
228#ifdef HAVE_LCD_BITMAP
229 /* clear all pictures in the conditional and nested ones */
230 if (token->type == SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY)
231 {
232 struct gui_img *img = find_image(token->value.i&0xFF, data);
233 clear_image_pos(gwps, img);
234 }
235 else if (token->type == SKIN_TOKEN_PEAKMETER)
236 {
237 data->peak_meter_enabled = false;
238 }
239 else if (token->type == SKIN_TOKEN_VIEWPORT_ENABLE)
240 {
241 char label = token->value.i&0x7f;
242 struct skin_element *viewport;
243 for (viewport = data->tree;
244 viewport;
245 viewport = viewport->next)
246 {
247 struct skin_viewport *skin_viewport = (struct skin_viewport*)viewport->data;
248 if ((skin_viewport->label&0x7f) != label)
249 continue;
250 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
251 {
252 continue;
253 }
254 if (skin_viewport->hidden_flags&VP_DRAW_HIDEABLE)
255 {
256 if (skin_viewport->hidden_flags&VP_DRAW_HIDDEN)
257 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
258 else
259 {
260 gwps->display->set_viewport(&skin_viewport->vp);
261 gwps->display->clear_viewport();
262 gwps->display->scroll_stop(&skin_viewport->vp);
263 gwps->display->set_viewport(&info->skin_vp->vp);
264 skin_viewport->hidden_flags |= VP_DRAW_HIDDEN;
265 }
266 }
267 }
268 }
269#endif
270#ifdef HAVE_ALBUMART
271 else if (data->albumart && token->type == SKIN_TOKEN_ALBUMART_DISPLAY)
272 {
273 draw_album_art(gwps,
274 playback_current_aa_hid(data->playback_aa_slot), true);
275 }
276#endif
277 child = child->next;
278 }
279 }
280}
281
282static void fix_line_alignment(struct skin_draw_info *info, struct skin_element *element)
283{
284 struct align_pos *align = &info->align;
285 char *cur_pos = info->cur_align_start + strlen(info->cur_align_start);
286 switch (element->tag->type)
287 {
288 case SKIN_TOKEN_ALIGN_LEFT:
289 *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
290 align->left = cur_pos;
291 info->cur_align_start = cur_pos;
292 break;
293 case SKIN_TOKEN_ALIGN_LEFT_RTL:
294 *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
295 if (lang_is_rtl())
296 align->right = cur_pos;
297 else
298 align->left = cur_pos;
299 info->cur_align_start = cur_pos;
300 break;
301 case SKIN_TOKEN_ALIGN_CENTER:
302 *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
303 align->center = cur_pos;
304 info->cur_align_start = cur_pos;
305 break;
306 case SKIN_TOKEN_ALIGN_RIGHT:
307 *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
308 align->right = cur_pos;
309 info->cur_align_start = cur_pos;
310 break;
311 case SKIN_TOKEN_ALIGN_RIGHT_RTL:
312 *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
313 if (lang_is_rtl())
314 align->left = cur_pos;
315 else
316 align->right = cur_pos;
317 info->cur_align_start = cur_pos;
318 break;
319 default:
320 break;
321 }
322}
323
324/* Draw a LINE element onto the display */
325bool skin_render_line(struct skin_element* line, struct skin_draw_info *info)
326{
327 bool needs_update = false;
328 int last_value, value;
329
330 if (line->children_count == 0)
331 return false; /* empty line, do nothing */
332
333 struct skin_element *child = line->children[0];
334 struct conditional *conditional;
335 skin_render_func func = skin_render_line;
336 char tempbuf[128];
337 int old_refresh_mode = info->refresh_type;
338 while (child)
339 {
340 tempbuf[0] = '\0';
341 switch (child->type)
342 {
343 case CONDITIONAL:
344 conditional = (struct conditional*)child->data;
345 last_value = conditional->last_value;
346 value = evaluate_conditional(info->gwps, conditional, child->children_count);
347
348 if (value != 1 && value >= child->children_count)
349 value = child->children_count-1;
350 if (child->children_count == 1)
351 {
352 /* special handling so
353 * %?aa<true> and %?<true|false> need special handlng here */
354
355 if (value == 1) /* tag is false */
356 {
357 /* we are in a false branch of a %?aa<true> conditional */
358 if (last_value == 0)
359 do_tags_in_hidden_conditional(child->children[0], info);
360 break;
361 }
362 value = 0;
363 }
364 else
365 {
366 if (last_value >= 0 && value != last_value && last_value < child->children_count)
367 do_tags_in_hidden_conditional(child->children[last_value], info);
368 }
369 if (child->children[value]->type == LINE_ALTERNATOR)
370 {
371 func = skin_render_alternator;
372 }
373 else if (child->children[value]->type == LINE)
374 func = skin_render_line;
375
376 if (value != last_value)
377 {
378 info->refresh_type = SKIN_REFRESH_ALL;
379 info->force_redraw = true;
380 }
381
382 if (func(child->children[value], info))
383 needs_update = true;
384 else
385 needs_update = needs_update || (last_value != value);
386
387 info->refresh_type = old_refresh_mode;
388 break;
389 case TAG:
390 if (child->tag->flags & NOBREAK)
391 info->no_line_break = true;
392 if (child->tag->type == SKIN_TOKEN_SUBLINE_SCROLL)
393 info->line_scrolls = true;
394
395 fix_line_alignment(info, child);
396
397 if (!child->data)
398 {
399 break;
400 }
401 if (!do_non_text_tags(info->gwps, info, child, &info->skin_vp->vp))
402 {
403 const char *value = get_token_value(info->gwps, child->data,
404 tempbuf, sizeof(tempbuf), NULL);
405 if (value)
406 {
407 needs_update = needs_update ||
408 ((child->tag->flags&info->refresh_type)!=0);
409 strlcat(info->cur_align_start, value,
410 info->buf_size - (info->cur_align_start-info->buf));
411 }
412 }
413 break;
414 case TEXT:
415 strlcat(info->cur_align_start, child->data,
416 info->buf_size - (info->cur_align_start-info->buf));
417 needs_update = needs_update ||
418 (info->refresh_type&SKIN_REFRESH_STATIC) != 0;
419 break;
420 case COMMENT:
421 default:
422 break;
423 }
424
425 child = child->next;
426 }
427 return needs_update;
428}
429
430bool skin_render_alternator(struct skin_element* element, struct skin_draw_info *info)
431{
432 bool changed_lines = false;
433 struct line_alternator *alternator = (struct line_alternator*)element->data;
434 unsigned old_refresh = info->refresh_type;
435 if (info->refresh_type == SKIN_REFRESH_ALL)
436 {
437 alternator->current_line = 0;
438 alternator->last_change_tick = current_tick;
439 changed_lines = true;
440 }
441 else
442 {
443 struct skin_element *current_line = element->children[alternator->current_line];
444 struct line *line = (struct line *)current_line->data;
445 int next_change = alternator->last_change_tick + line->timeout;
446 if (TIME_AFTER(current_tick, next_change))
447 {
448 alternator->current_line++;
449 if (alternator->current_line >= element->children_count)
450 alternator->current_line = 0;
451 alternator->last_change_tick = current_tick;
452 changed_lines = true;
453 }
454 }
455 if (element->children[alternator->current_line]->children_count == 0)
456 {
457 /* skip empty sublines */
458 alternator->current_line++;
459 if (alternator->current_line >= element->children_count)
460 alternator->current_line = 0;
461 changed_lines = true;
462 }
463
464 if (changed_lines)
465 {
466 info->refresh_type = SKIN_REFRESH_ALL;
467 info->force_redraw = true;
468 }
469 bool ret = skin_render_line(element->children[alternator->current_line], info);
470 info->refresh_type = old_refresh;
471 return changed_lines || ret;
472}
473
474void skin_render_viewport(struct skin_element* viewport, struct gui_wps *gwps,
475 struct skin_viewport* skin_viewport, unsigned long refresh_type)
476{
477 struct screen *display = gwps->display;
478 char linebuf[MAX_LINE];
479 skin_render_func func = skin_render_line;
480 struct skin_element* line = viewport;
481 struct skin_draw_info info = {
482 .gwps = gwps,
483 .buf = linebuf,
484 .buf_size = sizeof(linebuf),
485 .line_number = 0,
486 .no_line_break = false,
487 .line_scrolls = false,
488 .refresh_type = refresh_type,
489 .skin_vp = skin_viewport
490 };
491
492 struct align_pos * align = &info.align;
493 bool needs_update;
494#ifdef HAVE_LCD_BITMAP
495 /* Set images to not to be displayed */
496 struct skin_token_list *imglist = gwps->data->images;
497 while (imglist)
498 {
499 struct gui_img *img = (struct gui_img *)imglist->token->value.data;
500 img->display = -1;
501 imglist = imglist->next;
502 }
503#endif
504
505 while (line)
506 {
507 linebuf[0] = '\0';
508 info.no_line_break = false;
509 info.line_scrolls = false;
510 info.force_redraw = false;
511
512 info.cur_align_start = info.buf;
513 align->left = info.buf;
514 align->center = NULL;
515 align->right = NULL;
516
517
518 if (line->type == LINE_ALTERNATOR)
519 func = skin_render_alternator;
520 else if (line->type == LINE)
521 func = skin_render_line;
522
523 needs_update = func(line, &info);
524
525 /* only update if the line needs to be, and there is something to write */
526 if (refresh_type && needs_update)
527 {
528 if (info.line_scrolls)
529 {
530 /* if the line is a scrolling one we don't want to update
531 too often, so that it has the time to scroll */
532 if ((refresh_type & SKIN_REFRESH_SCROLL) || info.force_redraw)
533 write_line(display, align, info.line_number, true);
534 }
535 else
536 write_line(display, align, info.line_number, false);
537 }
538 if (!info.no_line_break)
539 info.line_number++;
540 line = line->next;
541 }
542#ifdef HAVE_LCD_BITMAP
543 wps_display_images(gwps, &skin_viewport->vp);
544#endif
545}
546
547void skin_render(struct gui_wps *gwps, unsigned refresh_mode)
548{
549 struct wps_data *data = gwps->data;
550 struct screen *display = gwps->display;
551
552 struct skin_element* viewport = data->tree;
553 struct skin_viewport* skin_viewport;
554
555 int old_refresh_mode = refresh_mode;
556
557#ifdef HAVE_LCD_CHARCELLS
558 int i;
559 for (i = 0; i < 8; i++)
560 {
561 if (data->wps_progress_pat[i] == 0)
562 data->wps_progress_pat[i] = display->get_locked_pattern();
563 }
564#endif
565 viewport = data->tree;
566 skin_viewport = (struct skin_viewport *)viewport->data;
567 if (skin_viewport->label == VP_DEFAULT_LABEL && viewport->next)
568 refresh_mode = 0;
569
570 for (viewport = data->tree;
571 viewport;
572 viewport = viewport->next)
573 {
574 /* SETUP */
575 skin_viewport = (struct skin_viewport*)viewport->data;
576 unsigned vp_refresh_mode = refresh_mode;
577#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)
578 skin_viewport->vp.fg_pattern = skin_viewport->start_fgcolour;
579 skin_viewport->vp.bg_pattern = skin_viewport->start_bgcolour;
580#endif
581
582 /* dont redraw the viewport if its disabled */
583 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
584 { /* don't draw anything into this one */
585 vp_refresh_mode = 0;
586 }
587 else if ((skin_viewport->hidden_flags&VP_DRAW_HIDDEN))
588 {
589 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
590 continue;
591 }
592 else if (((skin_viewport->hidden_flags&
593 (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE))
594 == (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE)))
595 {
596 vp_refresh_mode = SKIN_REFRESH_ALL;
597 skin_viewport->hidden_flags = VP_DRAW_HIDEABLE;
598 }
599
600 display->set_viewport(&skin_viewport->vp);
601 if ((vp_refresh_mode&SKIN_REFRESH_ALL) == SKIN_REFRESH_ALL)
602 {
603 display->clear_viewport();
604 }
605 /* render */
606 skin_render_viewport(viewport->children[0], gwps,
607 skin_viewport, vp_refresh_mode);
608 refresh_mode = old_refresh_mode;
609 }
610
611 /* Restore the default viewport */
612 display->set_viewport(NULL);
613 display->update();
614}