Allow fonts to use smaller buffers than the default size. use font_load_ex() to spefi...
[maemo-rb.git] / apps / gui / skin_engine / skin_parser.c
blobc8cca3d380fc0403e606bdde88b92b956b658691
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2007 Nicolas Pennequin, Dan Everton, Matthias Mohr
11 * 2010 Jonathan Gordon
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ****************************************************************************/
23 #include <stdio.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include "config.h"
27 #include "core_alloc.h"
28 #include "file.h"
29 #include "misc.h"
30 #include "plugin.h"
31 #include "viewport.h"
33 #include "skin_buffer.h"
34 #include "skin_parser.h"
35 #include "tag_table.h"
37 #ifdef __PCTOOL__
38 #ifdef WPSEDITOR
39 #include "proxy.h"
40 #include "sysfont.h"
41 #else
42 #include "action.h"
43 #include "checkwps.h"
44 #include "audio.h"
45 #define lang_is_rtl() (false)
46 #define DEBUGF printf
47 #endif /*WPSEDITOR*/
48 #else
49 #include "debug.h"
50 #include "language.h"
51 #endif /*__PCTOOL__*/
53 #include <ctype.h>
54 #include <stdbool.h>
55 #include "font.h"
57 #include "wps_internals.h"
58 #include "skin_engine.h"
59 #include "settings.h"
60 #include "settings_list.h"
61 #if CONFIG_TUNER
62 #include "radio.h"
63 #include "tuner.h"
64 #endif
66 #ifdef HAVE_LCD_BITMAP
67 #include "bmp.h"
68 #endif
70 #ifdef HAVE_ALBUMART
71 #include "playback.h"
72 #endif
74 #include "backdrop.h"
75 #include "statusbar-skinned.h"
77 #define WPS_ERROR_INVALID_PARAM -1
79 #if LCD_HEIGHT > 160
80 #define SKIN_FONT_SIZE (1024*10)
81 #else
82 #define SKIN_FONT_SIZE (1024*3)
83 #endif
84 #define GLYPHS_TO_CACHE 256
86 static bool isdefault(struct skin_tag_parameter *param)
88 return param->type == DEFAULT;
92 /* which screen are we parsing for? */
93 static enum screen_type curr_screen;
95 /* the current viewport */
96 static struct skin_element *curr_viewport_element;
97 static struct skin_viewport *curr_vp;
99 static struct line *curr_line;
101 static int follow_lang_direction = 0;
103 typedef int (*parse_function)(struct skin_element *element,
104 struct wps_token *token,
105 struct wps_data *wps_data);
107 #ifdef HAVE_LCD_BITMAP
108 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
109 * chains require the order to be kept.
111 static void add_to_ll_chain(struct skin_token_list **list, struct skin_token_list *item)
113 if (*list == NULL)
114 *list = item;
115 else
117 struct skin_token_list *t = *list;
118 while (t->next)
119 t = t->next;
120 t->next = item;
124 #endif
127 void *skin_find_item(const char *label, enum skin_find_what what,
128 struct wps_data *data)
130 const char *itemlabel = NULL;
131 union {
132 struct skin_token_list *linkedlist;
133 struct skin_element *vplist;
134 } list = {NULL};
135 bool isvplist = false;
136 void *ret = NULL;
137 switch (what)
139 case SKIN_FIND_UIVP:
140 case SKIN_FIND_VP:
141 list.vplist = data->tree;
142 isvplist = true;
143 break;
144 #ifdef HAVE_LCD_BITMAP
145 case SKIN_FIND_IMAGE:
146 list.linkedlist = data->images;
147 break;
148 #endif
149 #ifdef HAVE_TOUCHSCREEN
150 case SKIN_FIND_TOUCHREGION:
151 list.linkedlist = data->touchregions;
152 break;
153 #endif
154 #ifdef HAVE_SKIN_VARIABLES
155 case SKIN_VARIABLE:
156 list.linkedlist = data->skinvars;
157 break;
158 #endif
161 while (list.linkedlist)
163 bool skip = false;
164 switch (what)
166 case SKIN_FIND_UIVP:
167 case SKIN_FIND_VP:
168 ret = list.vplist->data;
169 itemlabel = ((struct skin_viewport *)ret)->label;
170 skip = !(((struct skin_viewport *)ret)->is_infovp ==
171 (what==SKIN_FIND_UIVP));
172 break;
173 #ifdef HAVE_LCD_BITMAP
174 case SKIN_FIND_IMAGE:
175 ret = list.linkedlist->token->value.data;
176 itemlabel = ((struct gui_img *)ret)->label;
177 break;
178 #endif
179 #ifdef HAVE_TOUCHSCREEN
180 case SKIN_FIND_TOUCHREGION:
181 ret = list.linkedlist->token->value.data;
182 itemlabel = ((struct touchregion *)ret)->label;
183 break;
184 #endif
185 #ifdef HAVE_SKIN_VARIABLES
186 case SKIN_VARIABLE:
187 ret = list.linkedlist->token->value.data;
188 itemlabel = ((struct skin_var *)ret)->label;
189 break;
190 #endif
193 if (!skip && itemlabel && !strcmp(itemlabel, label))
194 return ret;
196 if (isvplist)
197 list.vplist = list.vplist->next;
198 else
199 list.linkedlist = list.linkedlist->next;
201 return NULL;
204 #ifdef HAVE_LCD_BITMAP
206 /* create and init a new wpsll item.
207 * passing NULL to token will alloc a new one.
208 * You should only pass NULL for the token when the token type (table above)
209 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
211 static struct skin_token_list *new_skin_token_list_item(struct wps_token *token,
212 void* token_data)
214 struct skin_token_list *llitem =
215 (struct skin_token_list *)skin_buffer_alloc(sizeof(struct skin_token_list));
216 if (!token)
217 token = (struct wps_token*)skin_buffer_alloc(sizeof(struct wps_token));
218 if (!llitem || !token)
219 return NULL;
220 llitem->next = NULL;
221 llitem->token = token;
222 if (token_data)
223 llitem->token->value.data = token_data;
224 return llitem;
227 static int parse_statusbar_tags(struct skin_element* element,
228 struct wps_token *token,
229 struct wps_data *wps_data)
231 (void)element;
232 if (token->type == SKIN_TOKEN_DRAW_INBUILTBAR)
234 token->value.data = (void*)&curr_vp->vp;
236 else
238 struct skin_element *def_vp = wps_data->tree;
239 struct skin_viewport *default_vp = def_vp->data;
240 if (def_vp->params_count == 0)
242 wps_data->wps_sb_tag = true;
243 wps_data->show_sb_on_wps = (token->type == SKIN_TOKEN_ENABLE_THEME);
245 if (wps_data->show_sb_on_wps)
247 viewport_set_defaults(&default_vp->vp, curr_screen);
249 else
251 viewport_set_fullscreen(&default_vp->vp, curr_screen);
253 #ifdef HAVE_REMOTE_LCD
254 /* viewport_set_defaults() sets the font to FONT_UI+curr_screen.
255 * This parser requires font 1 to always be the UI font,
256 * so force it back to FONT_UI and handle the screen number at the end */
257 default_vp->vp.font = FONT_UI;
258 #endif
260 return 0;
263 static int get_image_id(int c)
265 if(c >= 'a' && c <= 'z')
266 return c - 'a';
267 else if(c >= 'A' && c <= 'Z')
268 return c - 'A' + 26;
269 else
270 return -1;
273 char *get_image_filename(const char *start, const char* bmpdir,
274 char *buf, int buf_size)
276 snprintf(buf, buf_size, "%s/%s", bmpdir, start);
278 return buf;
281 static int parse_image_display(struct skin_element *element,
282 struct wps_token *token,
283 struct wps_data *wps_data)
285 char *label = element->params[0].data.text;
286 char sublabel = '\0';
287 int subimage;
288 struct gui_img *img;
289 struct image_display *id = skin_buffer_alloc(sizeof(struct image_display));
291 if (element->params_count == 1 && strlen(label) <= 2)
293 /* backwards compatability. Allow %xd(Aa) to still work */
294 sublabel = label[1];
295 label[1] = '\0';
297 /* sanity check */
298 img = skin_find_item(label, SKIN_FIND_IMAGE, wps_data);
299 if (!img || !id)
301 return WPS_ERROR_INVALID_PARAM;
303 id->label = label;
304 id->offset = 0;
305 id->token = NULL;
306 if (img->using_preloaded_icons)
308 token->type = SKIN_TOKEN_IMAGE_DISPLAY_LISTICON;
311 if (element->params_count > 1)
313 if (element->params[1].type == CODE)
314 id->token = element->params[1].data.code->data;
315 /* specify a number. 1 being the first subimage (i.e top) NOT 0 */
316 else if (element->params[1].type == INTEGER)
317 id->subimage = element->params[1].data.number - 1;
318 if (element->params_count > 2)
319 id->offset = element->params[2].data.number;
321 else
323 if ((subimage = get_image_id(sublabel)) != -1)
325 if (subimage >= img->num_subimages)
326 return WPS_ERROR_INVALID_PARAM;
327 id->subimage = subimage;
328 } else {
329 id->subimage = 0;
332 token->value.data = id;
333 return 0;
336 static int parse_image_load(struct skin_element *element,
337 struct wps_token *token,
338 struct wps_data *wps_data)
340 const char* filename;
341 const char* id;
342 int x,y;
343 struct gui_img *img;
345 /* format: %x(n,filename.bmp,x,y)
346 or %xl(n,filename.bmp,x,y)
347 or %xl(n,filename.bmp,x,y,num_subimages)
350 id = element->params[0].data.text;
351 filename = element->params[1].data.text;
352 x = element->params[2].data.number;
353 y = element->params[3].data.number;
355 /* check the image number and load state */
356 if(skin_find_item(id, SKIN_FIND_IMAGE, wps_data))
358 /* Invalid image ID */
359 return WPS_ERROR_INVALID_PARAM;
361 img = (struct gui_img*)skin_buffer_alloc(sizeof(struct gui_img));
362 if (!img)
363 return WPS_ERROR_INVALID_PARAM;
364 /* save a pointer to the filename */
365 img->bm.data = (char*)filename;
366 img->label = id;
367 img->x = x;
368 img->y = y;
369 img->num_subimages = 1;
370 img->always_display = false;
371 img->display = -1;
372 img->using_preloaded_icons = false;
373 img->buflib_handle = -1;
375 /* save current viewport */
376 img->vp = &curr_vp->vp;
378 if (token->type == SKIN_TOKEN_IMAGE_DISPLAY)
380 img->always_display = true;
382 else if (element->params_count == 5)
384 img->num_subimages = element->params[4].data.number;
385 if (img->num_subimages <= 0)
386 return WPS_ERROR_INVALID_PARAM;
389 if (!strcmp(img->bm.data, "__list_icons__"))
391 img->num_subimages = Icon_Last_Themeable;
392 img->using_preloaded_icons = true;
395 struct skin_token_list *item =
396 (struct skin_token_list *)new_skin_token_list_item(NULL, img);
397 if (!item)
398 return WPS_ERROR_INVALID_PARAM;
399 add_to_ll_chain(&wps_data->images, item);
401 return 0;
403 struct skin_font {
404 int id; /* the id from font_load */
405 char *name; /* filename without path and extension */
406 int glyphs; /* how many glyphs to reserve room for */
408 static struct skin_font skinfonts[MAXUSERFONTS];
409 static int parse_font_load(struct skin_element *element,
410 struct wps_token *token,
411 struct wps_data *wps_data)
413 (void)wps_data; (void)token;
414 int id = element->params[0].data.number;
415 char *filename = element->params[1].data.text;
416 int glyphs;
417 char *ptr;
419 if(element->params_count > 2)
420 glyphs = element->params[2].data.number;
421 else
422 glyphs = 0;
423 if (id < 2)
425 DEBUGF("font id must be >= 2\n");
426 return 1;
428 #if defined(DEBUG) || defined(SIMULATOR)
429 if (skinfonts[id-2].name != NULL)
431 DEBUGF("font id %d already being used\n", id);
433 #endif
434 /* make sure the filename contains .fnt,
435 * we dont actually use it, but require it anyway */
436 ptr = strchr(filename, '.');
437 if (!ptr || strncmp(ptr, ".fnt", 4))
438 return WPS_ERROR_INVALID_PARAM;
439 skinfonts[id-2].id = -1;
440 skinfonts[id-2].name = filename;
441 skinfonts[id-2].glyphs = glyphs;
443 return 0;
447 #ifdef HAVE_LCD_BITMAP
449 static int parse_playlistview(struct skin_element *element,
450 struct wps_token *token,
451 struct wps_data *wps_data)
453 (void)wps_data;
454 struct playlistviewer *viewer =
455 (struct playlistviewer *)skin_buffer_alloc(sizeof(struct playlistviewer));
456 if (!viewer)
457 return WPS_ERROR_INVALID_PARAM;
458 viewer->vp = &curr_vp->vp;
459 viewer->show_icons = true;
460 viewer->start_offset = element->params[0].data.number;
461 viewer->line = element->params[1].data.code;
463 token->value.data = (void*)viewer;
465 return 0;
467 #endif
468 #ifdef HAVE_LCD_COLOR
469 static int parse_viewport_gradient_setup(struct skin_element *element,
470 struct wps_token *token,
471 struct wps_data *wps_data)
473 (void)wps_data;
474 struct gradient_config *cfg;
475 if (element->params_count < 2) /* only start and end are required */
476 return 1;
477 cfg = (struct gradient_config *)skin_buffer_alloc(sizeof(struct gradient_config));
478 if (!cfg)
479 return 1;
480 if (!parse_color(curr_screen, element->params[0].data.text, &cfg->start) ||
481 !parse_color(curr_screen, element->params[1].data.text, &cfg->end))
482 return 1;
483 if (element->params_count > 2)
485 if (!parse_color(curr_screen, element->params[2].data.text, &cfg->text))
486 return 1;
488 else
490 cfg->text = curr_vp->vp.fg_pattern;
493 token->value.data = cfg;
494 return 0;
496 #endif
498 static int parse_listitem(struct skin_element *element,
499 struct wps_token *token,
500 struct wps_data *wps_data)
502 (void)wps_data;
503 struct listitem *li = (struct listitem *)skin_buffer_alloc(sizeof(struct listitem));
504 if (!li)
505 return 1;
506 token->value.data = li;
507 if (element->params_count == 0)
508 li->offset = 0;
509 else
511 li->offset = element->params[0].data.number;
512 if (element->params_count > 1)
513 li->wrap = strcasecmp(element->params[1].data.text, "nowrap") != 0;
514 else
515 li->wrap = true;
517 return 0;
520 static int parse_listitemviewport(struct skin_element *element,
521 struct wps_token *token,
522 struct wps_data *wps_data)
524 #ifndef __PCTOOL__
525 struct listitem_viewport_cfg *cfg =
526 (struct listitem_viewport_cfg *)skin_buffer_alloc(
527 sizeof(struct listitem_viewport_cfg));
528 if (!cfg)
529 return -1;
530 cfg->data = wps_data;
531 cfg->tile = false;
532 cfg->label = element->params[0].data.text;
533 cfg->width = -1;
534 cfg->height = -1;
535 if (!isdefault(&element->params[1]))
536 cfg->width = element->params[1].data.number;
537 if (!isdefault(&element->params[2]))
538 cfg->height = element->params[2].data.number;
539 if (element->params_count > 3 &&
540 !strcmp(element->params[3].data.text, "tile"))
541 cfg->tile = true;
542 token->value.data = (void*)cfg;
543 #endif
544 return 0;
547 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
548 static int parse_viewporttextstyle(struct skin_element *element,
549 struct wps_token *token,
550 struct wps_data *wps_data)
552 (void)wps_data;
553 int style;
554 char *mode = element->params[0].data.text;
555 unsigned colour;
557 if (!strcmp(mode, "invert"))
559 style = STYLE_INVERT;
561 else if (!strcmp(mode, "colour") || !strcmp(mode, "color"))
563 if (element->params_count < 2 ||
564 !parse_color(curr_screen, element->params[1].data.text, &colour))
565 return 1;
566 style = STYLE_COLORED|(STYLE_COLOR_MASK&colour);
568 #ifdef HAVE_LCD_COLOR
569 else if (!strcmp(mode, "gradient"))
571 int num_lines;
572 if (element->params_count < 2)
573 num_lines = 1;
574 else /* atoi() instead of using a number in the parser is because [si]
575 * will select the number for something which looks like a colour
576 * making the "colour" case (above) harder to parse */
577 num_lines = atoi(element->params[1].data.text);
578 style = STYLE_GRADIENT|NUMLN_PACK(num_lines)|CURLN_PACK(0);
580 #endif
581 else if (!strcmp(mode, "clear"))
583 style = STYLE_DEFAULT;
585 else
586 return 1;
587 token->value.l = style;
588 return 0;
591 static int parse_viewportcolour(struct skin_element *element,
592 struct wps_token *token,
593 struct wps_data *wps_data)
595 (void)wps_data;
596 struct skin_tag_parameter *param = element->params;
597 struct viewport_colour *colour =
598 (struct viewport_colour *)skin_buffer_alloc(sizeof(struct viewport_colour));
599 if (!colour)
600 return -1;
601 if (isdefault(param))
603 colour->colour = get_viewport_default_colour(curr_screen,
604 token->type == SKIN_TOKEN_VIEWPORT_FGCOLOUR);
606 else
608 if (!parse_color(curr_screen, param->data.text, &colour->colour))
609 return -1;
611 colour->vp = &curr_vp->vp;
612 token->value.data = colour;
613 if (element->line == curr_viewport_element->line)
615 if (token->type == SKIN_TOKEN_VIEWPORT_FGCOLOUR)
617 curr_vp->start_fgcolour = colour->colour;
618 curr_vp->vp.fg_pattern = colour->colour;
620 else
622 curr_vp->start_bgcolour = colour->colour;
623 curr_vp->vp.bg_pattern = colour->colour;
626 return 0;
629 static int parse_image_special(struct skin_element *element,
630 struct wps_token *token,
631 struct wps_data *wps_data)
633 (void)wps_data; /* kill warning */
634 (void)token;
636 #if LCD_DEPTH > 1
637 char *filename;
638 if (token->type == SKIN_TOKEN_IMAGE_BACKDROP)
640 if (isdefault(&element->params[0]))
642 filename = "-";
644 else
646 filename = element->params[0].data.text;
647 /* format: %X(filename.bmp) or %X(d) */
648 if (!strcmp(filename, "d"))
649 filename = NULL;
651 wps_data->backdrop = filename;
653 #endif
655 return 0;
657 #endif
659 #endif /* HAVE_LCD_BITMAP */
661 static int parse_setting_and_lang(struct skin_element *element,
662 struct wps_token *token,
663 struct wps_data *wps_data)
665 /* NOTE: both the string validations that happen in here will
666 * automatically PASS on checkwps because its too hard to get
667 * settings_list.c and english.lang built for it.
668 * If that ever changes remove the #ifndef __PCTOOL__'s here
670 (void)wps_data;
671 char *temp = element->params[0].data.text;
672 int i;
674 if (token->type == SKIN_TOKEN_TRANSLATEDSTRING)
676 #ifndef __PCTOOL__
677 i = lang_english_to_id(temp);
678 if (i < 0)
679 return WPS_ERROR_INVALID_PARAM;
680 #endif
682 else
684 #ifndef __PCTOOL__
685 if (find_setting_by_cfgname(temp, &i) == NULL)
686 return WPS_ERROR_INVALID_PARAM;
687 #endif
689 /* Store the setting number */
690 token->value.i = i;
691 return 0;
693 static int parse_logical_if(struct skin_element *element,
694 struct wps_token *token,
695 struct wps_data *wps_data)
697 (void)wps_data;
698 char *op = element->params[1].data.text;
699 struct logical_if *lif = skin_buffer_alloc(sizeof(struct logical_if));
700 if (!lif)
701 return -1;
702 token->value.data = lif;
703 lif->token = element->params[0].data.code->data;
705 if (!strncmp(op, "=", 1))
706 lif->op = IF_EQUALS;
707 else if (!strncmp(op, "!=", 2))
708 lif->op = IF_NOTEQUALS;
709 else if (!strncmp(op, ">=", 2))
710 lif->op = IF_GREATERTHAN_EQ;
711 else if (!strncmp(op, "<=", 2))
712 lif->op = IF_LESSTHAN_EQ;
713 else if (!strncmp(op, ">", 2))
714 lif->op = IF_GREATERTHAN;
715 else if (!strncmp(op, "<", 1))
716 lif->op = IF_LESSTHAN;
718 memcpy(&lif->operand, &element->params[2], sizeof(lif->operand));
719 if (element->params_count > 3)
720 lif->num_options = element->params[3].data.number;
721 else
722 lif->num_options = TOKEN_VALUE_ONLY;
723 return 0;
727 static int parse_timeout_tag(struct skin_element *element,
728 struct wps_token *token,
729 struct wps_data *wps_data)
731 (void)wps_data;
732 int val = 0;
733 if (element->params_count == 0)
735 switch (token->type)
737 case SKIN_TOKEN_SUBLINE_TIMEOUT:
738 return -1;
739 case SKIN_TOKEN_BUTTON_VOLUME:
740 case SKIN_TOKEN_TRACK_STARTING:
741 case SKIN_TOKEN_TRACK_ENDING:
742 val = 10;
743 break;
744 default:
745 break;
748 else
749 val = element->params[0].data.number;
750 token->value.i = val * TIMEOUT_UNIT;
751 return 0;
754 static int parse_substring_tag(struct skin_element* element,
755 struct wps_token *token,
756 struct wps_data *wps_data)
758 (void)wps_data;
759 struct substring *ss = (struct substring*)skin_buffer_alloc(sizeof(struct substring));
760 if (!ss)
761 return 1;
762 ss->start = element->params[0].data.number;
763 if (element->params[1].type == DEFAULT)
764 ss->length = -1;
765 else
766 ss->length = element->params[1].data.number;
767 ss->token = element->params[2].data.code->data;
768 token->value.data = ss;
769 return 0;
772 static int parse_progressbar_tag(struct skin_element* element,
773 struct wps_token *token,
774 struct wps_data *wps_data)
776 #ifdef HAVE_LCD_BITMAP
777 struct progressbar *pb;
778 struct viewport *vp = &curr_vp->vp;
779 struct skin_tag_parameter *param = element->params;
780 int curr_param = 0;
781 char *image_filename = NULL;
783 if (element->params_count == 0 &&
784 element->tag->type != SKIN_TOKEN_PROGRESSBAR)
785 return 0; /* nothing to do */
786 pb = (struct progressbar*)skin_buffer_alloc(sizeof(struct progressbar));
788 token->value.data = pb;
790 if (!pb)
791 return WPS_ERROR_INVALID_PARAM;
792 pb->vp = vp;
793 pb->follow_lang_direction = follow_lang_direction > 0;
794 pb->nofill = false;
795 pb->nobar = false;
796 pb->image = NULL;
797 pb->slider = NULL;
798 pb->backdrop = NULL;
799 pb->invert_fill_direction = false;
800 pb->horizontal = true;
802 if (element->params_count == 0)
804 pb->x = 0;
805 pb->width = vp->width;
806 pb->height = SYSFONT_HEIGHT-2;
807 pb->y = -1; /* Will be computed during the rendering */
808 pb->type = element->tag->type;
809 return 0;
812 /* (x, y, width, height, ...) */
813 if (!isdefault(param))
814 pb->x = param->data.number;
815 else
816 pb->x = 0;
817 param++;
819 if (!isdefault(param))
820 pb->y = param->data.number;
821 else
822 pb->y = -1; /* computed at rendering */
823 param++;
825 if (!isdefault(param))
826 pb->width = param->data.number;
827 else
828 pb->width = vp->width - pb->x;
829 param++;
831 if (!isdefault(param))
833 /* A zero height makes no sense - reject it */
834 if (param->data.number == 0)
835 return WPS_ERROR_INVALID_PARAM;
837 pb->height = param->data.number;
839 else
841 if (vp->font > FONT_UI)
842 pb->height = -1; /* calculate at display time */
843 else
845 #ifndef __PCTOOL__
846 pb->height = font_get(vp->font)->height;
847 #else
848 pb->height = 8;
849 #endif
852 /* optional params, first is the image filename if it isnt recognised as a keyword */
854 curr_param = 4;
855 if (isdefault(&element->params[curr_param]))
857 param++;
858 curr_param++;
861 pb->horizontal = pb->width > pb->height;
862 while (curr_param < element->params_count)
864 param++;
865 if (!strcmp(param->data.text, "invert"))
866 pb->invert_fill_direction = true;
867 else if (!strcmp(param->data.text, "nofill"))
868 pb->nofill = true;
869 else if (!strcmp(param->data.text, "nobar"))
870 pb->nobar = true;
871 else if (!strcmp(param->data.text, "slider"))
873 if (curr_param+1 < element->params_count)
875 curr_param++;
876 param++;
877 pb->slider = skin_find_item(param->data.text,
878 SKIN_FIND_IMAGE, wps_data);
880 else /* option needs the next param */
881 return -1;
883 else if (!strcmp(param->data.text, "image"))
885 if (curr_param+1 < element->params_count)
887 curr_param++;
888 param++;
889 image_filename = param->data.text;
892 else /* option needs the next param */
893 return -1;
895 else if (!strcmp(param->data.text, "backdrop"))
897 if (curr_param+1 < element->params_count)
899 curr_param++;
900 param++;
901 pb->backdrop = skin_find_item(param->data.text,
902 SKIN_FIND_IMAGE, wps_data);
905 else /* option needs the next param */
906 return -1;
908 else if (!strcmp(param->data.text, "vertical"))
910 pb->horizontal = false;
911 if (isdefault(&element->params[3]))
912 pb->height = vp->height - pb->y;
914 else if (!strcmp(param->data.text, "horizontal"))
915 pb->horizontal = true;
916 else if (curr_param == 4)
917 image_filename = param->data.text;
919 curr_param++;
922 if (image_filename)
924 pb->image = skin_find_item(image_filename, SKIN_FIND_IMAGE, wps_data);
925 if (!pb->image) /* load later */
927 struct gui_img* img = (struct gui_img*)skin_buffer_alloc(sizeof(struct gui_img));
928 if (!img)
929 return WPS_ERROR_INVALID_PARAM;
930 /* save a pointer to the filename */
931 img->bm.data = (char*)image_filename;
932 img->label = image_filename;
933 img->x = 0;
934 img->y = 0;
935 img->num_subimages = 1;
936 img->always_display = false;
937 img->display = -1;
938 img->using_preloaded_icons = false;
939 img->buflib_handle = -1;
940 img->vp = &curr_vp->vp;
941 struct skin_token_list *item =
942 (struct skin_token_list *)new_skin_token_list_item(NULL, img);
943 if (!item)
944 return WPS_ERROR_INVALID_PARAM;
945 add_to_ll_chain(&wps_data->images, item);
946 pb->image = img;
950 if (token->type == SKIN_TOKEN_VOLUME)
951 token->type = SKIN_TOKEN_VOLUMEBAR;
952 else if (token->type == SKIN_TOKEN_BATTERY_PERCENT)
953 token->type = SKIN_TOKEN_BATTERY_PERCENTBAR;
954 else if (token->type == SKIN_TOKEN_TUNER_RSSI)
955 token->type = SKIN_TOKEN_TUNER_RSSI_BAR;
956 else if (token->type == SKIN_TOKEN_PEAKMETER_LEFT)
957 token->type = SKIN_TOKEN_PEAKMETER_LEFTBAR;
958 else if (token->type == SKIN_TOKEN_PEAKMETER_RIGHT)
959 token->type = SKIN_TOKEN_PEAKMETER_RIGHTBAR;
960 else if (token->type == SKIN_TOKEN_LIST_NEEDS_SCROLLBAR)
961 token->type = SKIN_TOKEN_LIST_SCROLLBAR;
962 pb->type = token->type;
964 return 0;
966 #else
967 (void)element;
968 if (token->type == SKIN_TOKEN_PROGRESSBAR ||
969 token->type == SKIN_TOKEN_PLAYER_PROGRESSBAR)
971 wps_data->full_line_progressbar =
972 token->type == SKIN_TOKEN_PLAYER_PROGRESSBAR;
974 return 0;
976 #endif
979 #ifdef HAVE_ALBUMART
980 static int parse_albumart_load(struct skin_element* element,
981 struct wps_token *token,
982 struct wps_data *wps_data)
984 struct dim dimensions;
985 int albumart_slot;
986 bool swap_for_rtl = lang_is_rtl() && follow_lang_direction;
987 struct skin_albumart *aa =
988 (struct skin_albumart *)skin_buffer_alloc(sizeof(struct skin_albumart));
989 (void)token; /* silence warning */
990 if (!aa)
991 return -1;
993 /* reset albumart info in wps */
994 aa->width = -1;
995 aa->height = -1;
996 aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
997 aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
999 aa->x = element->params[0].data.number;
1000 aa->y = element->params[1].data.number;
1001 aa->width = element->params[2].data.number;
1002 aa->height = element->params[3].data.number;
1004 aa->vp = &curr_vp->vp;
1005 aa->draw_handle = -1;
1007 /* if we got here, we parsed everything ok .. ! */
1008 if (aa->width < 0)
1009 aa->width = 0;
1010 else if (aa->width > LCD_WIDTH)
1011 aa->width = LCD_WIDTH;
1013 if (aa->height < 0)
1014 aa->height = 0;
1015 else if (aa->height > LCD_HEIGHT)
1016 aa->height = LCD_HEIGHT;
1018 if (swap_for_rtl)
1019 aa->x = LCD_WIDTH - (aa->x + aa->width);
1021 aa->state = WPS_ALBUMART_LOAD;
1022 wps_data->albumart = aa;
1024 dimensions.width = aa->width;
1025 dimensions.height = aa->height;
1027 albumart_slot = playback_claim_aa_slot(&dimensions);
1029 if (0 <= albumart_slot)
1030 wps_data->playback_aa_slot = albumart_slot;
1032 if (element->params_count > 4 && !isdefault(&element->params[4]))
1034 switch (*element->params[4].data.text)
1036 case 'l':
1037 case 'L':
1038 if (swap_for_rtl)
1039 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1040 else
1041 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1042 break;
1043 case 'c':
1044 case 'C':
1045 aa->xalign = WPS_ALBUMART_ALIGN_CENTER;
1046 break;
1047 case 'r':
1048 case 'R':
1049 if (swap_for_rtl)
1050 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1051 else
1052 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1053 break;
1056 if (element->params_count > 5 && !isdefault(&element->params[5]))
1058 switch (*element->params[5].data.text)
1060 case 't':
1061 case 'T':
1062 aa->yalign = WPS_ALBUMART_ALIGN_TOP;
1063 break;
1064 case 'c':
1065 case 'C':
1066 aa->yalign = WPS_ALBUMART_ALIGN_CENTER;
1067 break;
1068 case 'b':
1069 case 'B':
1070 aa->yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1071 break;
1074 return 0;
1077 #endif /* HAVE_ALBUMART */
1078 #ifdef HAVE_SKIN_VARIABLES
1079 static struct skin_var* find_or_add_var(const char* label,
1080 struct wps_data *data)
1082 struct skin_var* ret = skin_find_item(label, SKIN_VARIABLE, data);
1083 if (!ret)
1085 ret = (struct skin_var*)skin_buffer_alloc(sizeof(struct skin_var));
1086 if (!ret)
1087 return NULL;
1088 ret->label = label;
1089 ret->value = 1;
1090 ret->last_changed = 0xffff;
1091 struct skin_token_list *item = new_skin_token_list_item(NULL, ret);
1092 if (!item)
1093 return NULL;
1094 add_to_ll_chain(&data->skinvars, item);
1096 return ret;
1098 static int parse_skinvar( struct skin_element *element,
1099 struct wps_token *token,
1100 struct wps_data *wps_data)
1102 const char* label = element->params[0].data.text;
1103 struct skin_var* var = find_or_add_var(label, wps_data);
1104 if (!var)
1105 return WPS_ERROR_INVALID_PARAM;
1106 switch (token->type)
1108 case SKIN_TOKEN_VAR_GETVAL:
1109 token->value.data = var;
1110 break;
1111 case SKIN_TOKEN_VAR_SET:
1113 struct skin_var_changer *data =
1114 (struct skin_var_changer*)skin_buffer_alloc(
1115 sizeof(struct skin_var_changer));
1116 if (!data)
1117 return WPS_ERROR_INVALID_PARAM;
1118 data->var = var;
1119 data->newval = element->params[2].data.number;
1120 data->max = 0;
1121 if (!strcmp(element->params[1].data.text, "set"))
1122 data->direct = true;
1123 else if (!strcmp(element->params[1].data.text, "inc"))
1125 data->direct = false;
1127 else if (!strcmp(element->params[1].data.text, "dec"))
1129 data->direct = false;
1130 data->newval *= -1;
1132 if (element->params_count > 3)
1133 data->max = element->params[3].data.number;
1134 token->value.data = data;
1136 break;
1137 case SKIN_TOKEN_VAR_TIMEOUT:
1139 struct skin_var_lastchange *data =
1140 (struct skin_var_lastchange*)skin_buffer_alloc(
1141 sizeof(struct skin_var_lastchange));
1142 if (!data)
1143 return WPS_ERROR_INVALID_PARAM;
1144 data->var = var;
1145 data->timeout = 10;
1146 if (element->params_count > 1)
1147 data->timeout = element->params[1].data.number;
1148 data->timeout *= TIMEOUT_UNIT;
1149 token->value.data = data;
1151 break;
1152 default: /* kill the warning */
1153 break;
1155 return 0;
1157 #endif /* HAVE_SKIN_VARIABLES */
1158 #ifdef HAVE_TOUCHSCREEN
1159 static int parse_lasttouch(struct skin_element *element,
1160 struct wps_token *token,
1161 struct wps_data *wps_data)
1163 struct touchregion_lastpress *data =
1164 (struct touchregion_lastpress*)skin_buffer_alloc(
1165 sizeof(struct touchregion_lastpress));
1166 int i;
1167 if (!data)
1168 return WPS_ERROR_INVALID_PARAM;
1169 data->region = NULL;
1170 data->timeout = 10;
1172 for (i=0; i<element->params_count; i++)
1174 if (element->params[i].type == STRING)
1175 data->region = skin_find_item(element->params[i].data.text,
1176 SKIN_FIND_TOUCHREGION, wps_data);
1177 else if (element->params[i].type == INTEGER ||
1178 element->params[i].type == DECIMAL)
1179 data->timeout = element->params[i].data.number;
1182 data->timeout *= TIMEOUT_UNIT;
1183 token->value.data = data;
1184 return 0;
1187 struct touchaction {const char* s; int action;};
1188 static const struct touchaction touchactions[] = {
1189 /* generic actions, convert to screen actions on use */
1190 {"none", ACTION_TOUCHSCREEN}, {"lock", ACTION_TOUCH_SOFTLOCK },
1191 {"prev", ACTION_STD_PREV }, {"next", ACTION_STD_NEXT },
1192 {"rwd", ACTION_STD_PREVREPEAT }, {"ffwd", ACTION_STD_NEXTREPEAT },
1193 {"hotkey", ACTION_STD_HOTKEY}, {"select", ACTION_STD_OK },
1194 {"menu", ACTION_STD_MENU }, {"cancel", ACTION_STD_CANCEL },
1195 {"contextmenu", ACTION_STD_CONTEXT},{"quickscreen", ACTION_STD_QUICKSCREEN },
1197 /* list/tree actions */
1198 { "resumeplayback", ACTION_TREE_WPS}, /* returns to previous music, WPS/FM */
1199 /* not really WPS specific, but no equivilant ACTION_STD_* */
1200 {"voldown", ACTION_WPS_VOLDOWN}, {"volup", ACTION_WPS_VOLUP},
1201 {"mute", ACTION_TOUCH_MUTE },
1203 /* generic settings changers */
1204 {"setting_inc", ACTION_SETTINGS_INC}, {"setting_dec", ACTION_SETTINGS_DEC},
1205 {"setting_set", ACTION_SETTINGS_SET},
1207 /* WPS specific actions */
1208 {"wps_prev", ACTION_WPS_SKIPPREV }, {"wps_next", ACTION_WPS_SKIPNEXT },
1209 {"browse", ACTION_WPS_BROWSE },
1210 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1211 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1212 {"pitch", ACTION_WPS_PITCHSCREEN}, {"playlist", ACTION_WPS_VIEW_PLAYLIST },
1214 #if CONFIG_TUNER
1215 /* FM screen actions */
1216 /* Also allow browse, play, stop from WPS codes */
1217 {"mode", ACTION_FM_MODE }, {"record", ACTION_FM_RECORD },
1218 {"presets", ACTION_FM_PRESET},
1219 #endif
1222 static int touchregion_setup_setting(struct skin_element *element, int param_no,
1223 struct touchregion *region)
1225 #ifndef __PCTOOL__
1226 int p = param_no;
1227 char *name = element->params[p++].data.text;
1228 int j;
1230 region->setting_data.setting = find_setting_by_cfgname(name, &j);
1231 if (region->setting_data.setting == NULL)
1232 return WPS_ERROR_INVALID_PARAM;
1234 if (region->action == ACTION_SETTINGS_SET)
1236 char* text;
1237 int temp;
1238 struct touchsetting *setting =
1239 &region->setting_data;
1240 if (element->params_count < p+1)
1241 return -1;
1243 text = element->params[p++].data.text;
1244 switch (settings[j].flags&F_T_MASK)
1246 case F_T_CUSTOM:
1247 setting->value.text = text;
1248 break;
1249 case F_T_INT:
1250 case F_T_UINT:
1251 if (settings[j].cfg_vals == NULL)
1253 setting->value.number = atoi(text);
1255 else if (cfg_string_to_int(j, &temp, text))
1257 if (settings[j].flags&F_TABLE_SETTING)
1258 setting->value.number =
1259 settings[j].table_setting->values[temp];
1260 else
1261 setting->value.number = temp;
1263 else
1264 return -1;
1265 break;
1266 case F_T_BOOL:
1267 if (cfg_string_to_int(j, &temp, text))
1269 setting->value.number = temp;
1271 else
1272 return -1;
1273 break;
1274 default:
1275 return -1;
1278 return p-param_no;
1279 #endif /* __PCTOOL__ */
1280 return 0;
1283 static int parse_touchregion(struct skin_element *element,
1284 struct wps_token *token,
1285 struct wps_data *wps_data)
1287 (void)token;
1288 unsigned i, imax;
1289 int p;
1290 struct touchregion *region = NULL;
1291 const char *action;
1292 const char pb_string[] = "progressbar";
1293 const char vol_string[] = "volume";
1295 /* format: %T([label,], x,y,width,height,action[, ...])
1296 * if action starts with & the area must be held to happen
1300 region = (struct touchregion*)skin_buffer_alloc(sizeof(struct touchregion));
1301 if (!region)
1302 return WPS_ERROR_INVALID_PARAM;
1304 /* should probably do some bounds checking here with the viewport... but later */
1305 region->action = ACTION_NONE;
1307 if (element->params[0].type == STRING)
1309 region->label = element->params[0].data.text;
1310 p = 1;
1311 /* "[SI]III[SI]|SS" is the param list. There MUST be 4 numbers
1312 * followed by at least one string. Verify that here */
1313 if (element->params_count < 6 ||
1314 element->params[4].type != INTEGER)
1315 return WPS_ERROR_INVALID_PARAM;
1317 else
1319 region->label = NULL;
1320 p = 0;
1323 region->x = element->params[p++].data.number;
1324 region->y = element->params[p++].data.number;
1325 region->width = element->params[p++].data.number;
1326 region->height = element->params[p++].data.number;
1327 region->wvp = curr_vp;
1328 region->armed = false;
1329 region->reverse_bar = false;
1330 region->value = 0;
1331 region->last_press = 0xffff;
1332 region->press_length = PRESS;
1333 region->allow_while_locked = false;
1334 action = element->params[p++].data.text;
1336 /* figure out the action */
1337 if(!strcmp(pb_string, action))
1338 region->action = ACTION_TOUCH_SCROLLBAR;
1339 else if(!strcmp(vol_string, action))
1340 region->action = ACTION_TOUCH_VOLUME;
1341 else
1343 imax = ARRAYLEN(touchactions);
1344 for (i = 0; i < imax; i++)
1346 /* try to match with one of our touchregion screens */
1347 if (!strcmp(touchactions[i].s, action))
1349 region->action = touchactions[i].action;
1350 if (region->action == ACTION_SETTINGS_INC ||
1351 region->action == ACTION_SETTINGS_DEC ||
1352 region->action == ACTION_SETTINGS_SET)
1354 int val;
1355 if (element->params_count < p+1)
1356 return WPS_ERROR_INVALID_PARAM;
1357 val = touchregion_setup_setting(element, p, region);
1358 if (val < 0)
1359 return WPS_ERROR_INVALID_PARAM;
1360 p += val;
1362 break;
1365 if (region->action == ACTION_NONE)
1366 return WPS_ERROR_INVALID_PARAM;
1368 while (p < element->params_count)
1370 char* param = element->params[p++].data.text;
1371 if (!strcmp(param, "allow_while_locked"))
1372 region->allow_while_locked = true;
1373 else if (!strcmp(param, "reverse_bar"))
1374 region->reverse_bar = true;
1375 else if (!strcmp(param, "repeat_press"))
1376 region->press_length = REPEAT;
1377 else if (!strcmp(param, "long_press"))
1378 region->press_length = LONG_PRESS;
1380 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
1381 if (!item)
1382 return WPS_ERROR_INVALID_PARAM;
1383 add_to_ll_chain(&wps_data->touchregions, item);
1385 if (region->action == ACTION_TOUCH_MUTE)
1387 region->value = global_settings.volume;
1391 return 0;
1393 #endif
1395 static bool check_feature_tag(const int type)
1397 switch (type)
1399 case SKIN_TOKEN_RTC_PRESENT:
1400 #if CONFIG_RTC
1401 return true;
1402 #else
1403 return false;
1404 #endif
1405 case SKIN_TOKEN_HAVE_RECORDING:
1406 #ifdef HAVE_RECORDING
1407 return true;
1408 #else
1409 return false;
1410 #endif
1411 case SKIN_TOKEN_HAVE_TUNER:
1412 #if CONFIG_TUNER
1413 if (radio_hardware_present())
1414 return true;
1415 #endif
1416 return false;
1417 case SKIN_TOKEN_HAVE_TOUCH:
1418 #ifdef HAVE_TOUCHSCREEN
1419 return true;
1420 #else
1421 return false;
1422 #endif
1424 #if CONFIG_TUNER
1425 case SKIN_TOKEN_HAVE_RDS:
1426 #ifdef HAVE_RDS_CAP
1427 return true;
1428 #else
1429 return false;
1430 #endif /* HAVE_RDS_CAP */
1431 #endif /* CONFIG_TUNER */
1432 default: /* not a tag we care about, just don't skip */
1433 return true;
1437 /* This is used to free any buflib allocations before the rest of
1438 * wps_data is reset.
1439 * The call to this in settings_apply_skins() is the last chance to do
1440 * any core_free()'s before wps_data is trashed and those handles lost
1442 void skin_data_free_buflib_allocs(struct wps_data *wps_data)
1444 (void)wps_data;
1445 #ifdef HAVE_LCD_BITMAP
1446 #ifndef __PCTOOL__
1447 struct skin_token_list *list = wps_data->images;
1448 while (list)
1450 struct gui_img *img = (struct gui_img*)list->token->value.data;
1451 if (img->buflib_handle > 0)
1452 core_free(img->buflib_handle);
1453 list = list->next;
1455 if (wps_data->font_ids != NULL)
1457 while (wps_data->font_count > 0)
1458 font_unload(wps_data->font_ids[--wps_data->font_count]);
1460 #endif
1461 #endif
1465 * initial setup of wps_data; does reset everything
1466 * except fields which need to survive, i.e.
1467 * Also called if the load fails
1469 static void skin_data_reset(struct wps_data *wps_data)
1471 skin_data_free_buflib_allocs(wps_data);
1472 #ifdef HAVE_LCD_BITMAP
1473 wps_data->images = NULL;
1474 #endif
1475 wps_data->tree = NULL;
1476 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1477 if (wps_data->backdrop_id >= 0)
1478 skin_backdrop_unload(wps_data->backdrop_id);
1479 wps_data->backdrop = NULL;
1480 #endif
1481 #ifdef HAVE_TOUCHSCREEN
1482 wps_data->touchregions = NULL;
1483 #endif
1484 #ifdef HAVE_SKIN_VARIABLES
1485 wps_data->skinvars = NULL;
1486 #endif
1487 #ifdef HAVE_ALBUMART
1488 wps_data->albumart = NULL;
1489 if (wps_data->playback_aa_slot >= 0)
1491 playback_release_aa_slot(wps_data->playback_aa_slot);
1492 wps_data->playback_aa_slot = -1;
1494 #endif
1496 #ifdef HAVE_LCD_BITMAP
1497 wps_data->peak_meter_enabled = false;
1498 wps_data->wps_sb_tag = false;
1499 wps_data->show_sb_on_wps = false;
1500 #else /* HAVE_LCD_CHARCELLS */
1501 /* progress bars */
1502 int i;
1503 for (i = 0; i < 8; i++)
1505 wps_data->wps_progress_pat[i] = 0;
1507 wps_data->full_line_progressbar = false;
1508 #endif
1509 wps_data->wps_loaded = false;
1512 #ifdef HAVE_LCD_BITMAP
1513 #ifndef __PCTOOL__
1514 static int currently_loading_handle = -1;
1515 static int buflib_move_callback(int handle, void* current, void* new)
1517 (void)current;
1518 (void)new;
1519 if (handle == currently_loading_handle)
1520 return BUFLIB_CB_CANNOT_MOVE;
1521 return BUFLIB_CB_OK;
1523 static struct buflib_callbacks buflib_ops = {buflib_move_callback, NULL};
1524 static void lock_handle(int handle)
1526 currently_loading_handle = handle;
1528 static void unlock_handle(void)
1530 currently_loading_handle = -1;
1532 #endif
1534 static int load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
1536 (void)wps_data; /* only needed for remote targets */
1537 char img_path[MAX_PATH];
1538 int fd;
1539 int handle;
1540 get_image_filename(bitmap->data, bmpdir,
1541 img_path, sizeof(img_path));
1543 /* load the image */
1544 int format;
1545 #ifdef HAVE_REMOTE_LCD
1546 if (curr_screen == SCREEN_REMOTE)
1547 format = FORMAT_ANY|FORMAT_REMOTE;
1548 else
1549 #endif
1550 format = FORMAT_ANY|FORMAT_TRANSPARENT;
1552 fd = open(img_path, O_RDONLY);
1553 if (fd < 0)
1555 DEBUGF("Couldn't open %s\n", img_path);
1556 return fd;
1558 #ifndef __PCTOOL__
1559 size_t buf_size = read_bmp_fd(fd, bitmap, 0,
1560 format|FORMAT_RETURN_SIZE, NULL);
1561 handle = core_alloc_ex(bitmap->data, buf_size, &buflib_ops);
1562 if (handle < 0)
1564 #ifndef APPLICATION
1565 DEBUGF("Not enough skin buffer: need %zd more.\n",
1566 buf_size - skin_buffer_freespace());
1567 #endif
1568 close(fd);
1569 return handle;
1571 lseek(fd, 0, SEEK_SET);
1572 lock_handle(handle);
1573 bitmap->data = core_get_data(handle);
1574 int ret = read_bmp_fd(fd, bitmap, buf_size, format, NULL);
1575 bitmap->data = NULL; /* do this to force a crash later if the
1576 caller doesnt call core_get_data() */
1577 unlock_handle();
1578 close(fd);
1579 if (ret > 0)
1581 return handle;
1583 else
1585 /* Abort if we can't load an image */
1586 DEBUGF("Couldn't load '%s'\n", img_path);
1587 core_free(handle);
1588 return -1;
1590 #else /* !__PCTOOL__ */
1591 close(fd);
1592 return 1;
1593 #endif
1596 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
1598 struct skin_token_list *list;
1599 bool retval = true; /* return false if a single image failed to load */
1601 /* regular images */
1602 list = wps_data->images;
1603 while (list)
1605 struct gui_img *img = (struct gui_img*)list->token->value.data;
1606 if (img->bm.data)
1608 if (img->using_preloaded_icons)
1610 img->loaded = true;
1611 list->token->type = SKIN_TOKEN_IMAGE_DISPLAY_LISTICON;
1613 else
1615 img->buflib_handle = load_skin_bmp(wps_data, &img->bm, bmpdir);
1616 img->loaded = img->buflib_handle >= 0;
1617 if (img->loaded)
1618 img->subimage_height = img->bm.height / img->num_subimages;
1619 else
1620 retval = false;
1623 list = list->next;
1626 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1627 wps_data->backdrop_id = skin_backdrop_assign(wps_data->backdrop, bmpdir, curr_screen);
1628 #endif /* has backdrop support */
1629 return retval;
1632 static bool skin_load_fonts(struct wps_data *data)
1634 /* don't spit out after the first failue to aid debugging */
1635 int id_array[MAXUSERFONTS];
1636 int font_count = 0;
1637 bool success = true;
1638 struct skin_element *vp_list;
1639 int font_id;
1640 /* walk though each viewport and assign its font */
1641 for(vp_list = data->tree; vp_list; vp_list = vp_list->next)
1643 /* first, find the viewports that have a non-sys/ui-font font */
1644 struct skin_viewport *skin_vp =
1645 (struct skin_viewport*)vp_list->data;
1646 struct viewport *vp = &skin_vp->vp;
1648 font_id = skin_vp->parsed_fontid;
1649 if (font_id == 1)
1650 { /* the usual case -> built-in fonts */
1651 vp->font = global_status.font_id[curr_screen];
1652 continue;
1654 else if (font_id <= 0)
1656 vp->font = FONT_SYSFIXED;
1657 continue;
1660 /* now find the corresponding skin_font */
1661 struct skin_font *font = &skinfonts[font_id-2];
1662 if (!font->name)
1664 if (success)
1666 DEBUGF("font %d not specified\n", font_id);
1668 success = false;
1669 continue;
1672 /* load the font - will handle loading the same font again if
1673 * multiple viewports use the same */
1674 if (font->id < 0)
1676 char path[MAX_PATH];
1677 snprintf(path, sizeof path, FONT_DIR "/%s", font->name);
1678 if (skinfonts[font_id-2].glyphs > 0)
1680 font->id = font_load_ex(path,
1681 font_glyphs_to_bufsize(path, skinfonts[font_id-2].glyphs));
1683 else
1684 font->id = font_load(path);
1685 //printf("[%d] %s -> %d\n",font_id, font->name, font->id);
1686 id_array[font_count++] = font->id;
1689 if (font->id < 0)
1691 DEBUGF("Unable to load font %d: '%s.fnt'\n",
1692 font_id, font->name);
1693 font->name = NULL; /* to stop trying to load it again if we fail */
1694 success = false;
1695 continue;
1698 /* finally, assign the font_id to the viewport */
1699 vp->font = font->id;
1701 data->font_ids = skin_buffer_alloc(font_count * sizeof(int));
1702 if (!success || data->font_ids == NULL)
1704 while (font_count > 0)
1705 font_unload(id_array[--font_count]);
1706 data->font_ids = NULL;
1707 return false;
1709 memcpy(data->font_ids, id_array, sizeof(int)*font_count);
1710 data->font_count = font_count;
1711 return success;
1714 #endif /* HAVE_LCD_BITMAP */
1715 static int convert_viewport(struct wps_data *data, struct skin_element* element)
1717 struct skin_viewport *skin_vp =
1718 (struct skin_viewport *)skin_buffer_alloc(sizeof(struct skin_viewport));
1719 struct screen *display = &screens[curr_screen];
1721 if (!skin_vp)
1722 return CALLBACK_ERROR;
1724 skin_vp->hidden_flags = 0;
1725 skin_vp->label = NULL;
1726 skin_vp->is_infovp = false;
1727 skin_vp->parsed_fontid = 1;
1728 element->data = skin_vp;
1729 curr_vp = skin_vp;
1730 curr_viewport_element = element;
1732 viewport_set_defaults(&skin_vp->vp, curr_screen);
1734 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1735 skin_vp->start_fgcolour = skin_vp->vp.fg_pattern;
1736 skin_vp->start_bgcolour = skin_vp->vp.bg_pattern;
1737 #endif
1738 #ifdef HAVE_LCD_COLOR
1739 skin_vp->start_gradient.start = skin_vp->vp.lss_pattern;
1740 skin_vp->start_gradient.end = skin_vp->vp.lse_pattern;
1741 skin_vp->start_gradient.text = skin_vp->vp.lst_pattern;
1742 #endif
1745 struct skin_tag_parameter *param = element->params;
1746 if (element->params_count == 0) /* default viewport */
1748 if (!data->tree) /* first viewport in the skin */
1749 data->tree = element;
1750 skin_vp->label = VP_DEFAULT_LABEL;
1751 return CALLBACK_OK;
1754 if (element->params_count == 6)
1756 if (element->tag->type == SKIN_TOKEN_UIVIEWPORT_LOAD)
1758 skin_vp->is_infovp = true;
1759 if (isdefault(param))
1761 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
1762 skin_vp->label = VP_DEFAULT_LABEL;
1764 else
1766 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
1767 skin_vp->label = param->data.text;
1770 else
1772 skin_vp->hidden_flags = VP_DRAW_HIDEABLE|VP_DRAW_HIDDEN;
1773 skin_vp->label = param->data.text;
1775 param++;
1777 /* x */
1778 if (!isdefault(param))
1780 skin_vp->vp.x = param->data.number;
1781 if (param->data.number < 0)
1782 skin_vp->vp.x += display->lcdwidth;
1784 param++;
1785 /* y */
1786 if (!isdefault(param))
1788 skin_vp->vp.y = param->data.number;
1789 if (param->data.number < 0)
1790 skin_vp->vp.y += display->lcdheight;
1792 param++;
1793 /* width */
1794 if (!isdefault(param))
1796 skin_vp->vp.width = param->data.number;
1797 if (param->data.number < 0)
1798 skin_vp->vp.width = (skin_vp->vp.width + display->lcdwidth) - skin_vp->vp.x;
1800 else
1802 skin_vp->vp.width = display->lcdwidth - skin_vp->vp.x;
1804 param++;
1805 /* height */
1806 if (!isdefault(param))
1808 skin_vp->vp.height = param->data.number;
1809 if (param->data.number < 0)
1810 skin_vp->vp.height = (skin_vp->vp.height + display->lcdheight) - skin_vp->vp.y;
1812 else
1814 skin_vp->vp.height = display->lcdheight - skin_vp->vp.y;
1816 param++;
1817 #ifdef HAVE_LCD_BITMAP
1818 /* font */
1819 if (!isdefault(param))
1820 skin_vp->parsed_fontid = param->data.number;
1821 #endif
1822 if ((unsigned) skin_vp->vp.x >= (unsigned) display->lcdwidth ||
1823 skin_vp->vp.width + skin_vp->vp.x > display->lcdwidth ||
1824 (unsigned) skin_vp->vp.y >= (unsigned) display->lcdheight ||
1825 skin_vp->vp.height + skin_vp->vp.y > display->lcdheight)
1826 return CALLBACK_ERROR;
1828 return CALLBACK_OK;
1831 static int skin_element_callback(struct skin_element* element, void* data)
1833 struct wps_data *wps_data = (struct wps_data *)data;
1834 struct wps_token *token;
1835 parse_function function = NULL;
1837 switch (element->type)
1839 /* IMPORTANT: element params are shared, so copy them if needed
1840 * or use then NOW, dont presume they have a long lifespan
1842 case TAG:
1844 token = (struct wps_token*)skin_buffer_alloc(sizeof(struct wps_token));
1845 memset(token, 0, sizeof(*token));
1846 token->type = element->tag->type;
1848 if (element->tag->flags&SKIN_RTC_REFRESH)
1850 #if CONFIG_RTC
1851 curr_line->update_mode |= SKIN_REFRESH_DYNAMIC;
1852 #else
1853 curr_line->update_mode |= SKIN_REFRESH_STATIC;
1854 #endif
1856 else
1857 curr_line->update_mode |= element->tag->flags&SKIN_REFRESH_ALL;
1859 element->data = token;
1861 /* Some tags need special handling for the tag, so add them here */
1862 switch (token->type)
1864 case SKIN_TOKEN_ALIGN_LANGDIRECTION:
1865 follow_lang_direction = 2;
1866 break;
1867 case SKIN_TOKEN_LOGICAL_IF:
1868 function = parse_logical_if;
1869 break;
1870 case SKIN_TOKEN_SUBSTRING:
1871 function = parse_substring_tag;
1872 break;
1873 case SKIN_TOKEN_PROGRESSBAR:
1874 case SKIN_TOKEN_VOLUME:
1875 case SKIN_TOKEN_BATTERY_PERCENT:
1876 case SKIN_TOKEN_PLAYER_PROGRESSBAR:
1877 case SKIN_TOKEN_PEAKMETER_LEFT:
1878 case SKIN_TOKEN_PEAKMETER_RIGHT:
1879 case SKIN_TOKEN_LIST_NEEDS_SCROLLBAR:
1880 #ifdef HAVE_RADIO_RSSI
1881 case SKIN_TOKEN_TUNER_RSSI:
1882 #endif
1883 function = parse_progressbar_tag;
1884 break;
1885 case SKIN_TOKEN_SUBLINE_TIMEOUT:
1886 case SKIN_TOKEN_BUTTON_VOLUME:
1887 case SKIN_TOKEN_TRACK_STARTING:
1888 case SKIN_TOKEN_TRACK_ENDING:
1889 function = parse_timeout_tag;
1890 break;
1891 #ifdef HAVE_LCD_BITMAP
1892 case SKIN_TOKEN_LIST_ITEM_TEXT:
1893 case SKIN_TOKEN_LIST_ITEM_ICON:
1894 function = parse_listitem;
1895 break;
1896 case SKIN_TOKEN_DISABLE_THEME:
1897 case SKIN_TOKEN_ENABLE_THEME:
1898 case SKIN_TOKEN_DRAW_INBUILTBAR:
1899 function = parse_statusbar_tags;
1900 break;
1901 case SKIN_TOKEN_LIST_TITLE_TEXT:
1902 #ifndef __PCTOOL__
1903 sb_skin_has_title(curr_screen);
1904 #endif
1905 break;
1906 #endif
1907 case SKIN_TOKEN_FILE_DIRECTORY:
1908 token->value.i = element->params[0].data.number;
1909 break;
1910 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1911 case SKIN_TOKEN_VIEWPORT_FGCOLOUR:
1912 case SKIN_TOKEN_VIEWPORT_BGCOLOUR:
1913 function = parse_viewportcolour;
1914 break;
1915 case SKIN_TOKEN_IMAGE_BACKDROP:
1916 function = parse_image_special;
1917 break;
1918 case SKIN_TOKEN_VIEWPORT_TEXTSTYLE:
1919 function = parse_viewporttextstyle;
1920 break;
1921 #endif
1922 #ifdef HAVE_LCD_COLOR
1923 case SKIN_TOKEN_VIEWPORT_GRADIENT_SETUP:
1924 function = parse_viewport_gradient_setup;
1925 break;
1926 #endif
1927 case SKIN_TOKEN_TRANSLATEDSTRING:
1928 case SKIN_TOKEN_SETTING:
1929 function = parse_setting_and_lang;
1930 break;
1931 #ifdef HAVE_LCD_BITMAP
1932 case SKIN_TOKEN_VIEWPORT_CUSTOMLIST:
1933 function = parse_playlistview;
1934 break;
1935 case SKIN_TOKEN_LOAD_FONT:
1936 function = parse_font_load;
1937 break;
1938 case SKIN_TOKEN_VIEWPORT_ENABLE:
1939 case SKIN_TOKEN_UIVIEWPORT_ENABLE:
1940 token->value.data = element->params[0].data.text;
1941 break;
1942 case SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY:
1943 function = parse_image_display;
1944 break;
1945 case SKIN_TOKEN_IMAGE_PRELOAD:
1946 case SKIN_TOKEN_IMAGE_DISPLAY:
1947 function = parse_image_load;
1948 break;
1949 case SKIN_TOKEN_LIST_ITEM_CFG:
1950 function = parse_listitemviewport;
1951 break;
1952 #endif
1953 #ifdef HAVE_TOUCHSCREEN
1954 case SKIN_TOKEN_TOUCHREGION:
1955 function = parse_touchregion;
1956 break;
1957 case SKIN_TOKEN_LASTTOUCH:
1958 function = parse_lasttouch;
1959 break;
1960 #endif
1961 #ifdef HAVE_ALBUMART
1962 case SKIN_TOKEN_ALBUMART_DISPLAY:
1963 if (wps_data->albumart)
1964 wps_data->albumart->vp = &curr_vp->vp;
1965 break;
1966 case SKIN_TOKEN_ALBUMART_LOAD:
1967 function = parse_albumart_load;
1968 break;
1969 #endif
1970 #ifdef HAVE_SKIN_VARIABLES
1971 case SKIN_TOKEN_VAR_SET:
1972 case SKIN_TOKEN_VAR_GETVAL:
1973 case SKIN_TOKEN_VAR_TIMEOUT:
1974 function = parse_skinvar;
1975 break;
1976 #endif
1977 default:
1978 break;
1980 if (function)
1982 if (function(element, token, wps_data) < 0)
1983 return CALLBACK_ERROR;
1985 /* tags that start with 'F', 'I' or 'D' are for the next file */
1986 if ( *(element->tag->name) == 'I' || *(element->tag->name) == 'F' ||
1987 *(element->tag->name) == 'D')
1988 token->next = true;
1989 if (follow_lang_direction > 0 )
1990 follow_lang_direction--;
1991 break;
1993 case VIEWPORT:
1994 return convert_viewport(wps_data, element);
1995 case LINE:
1997 struct line *line =
1998 (struct line *)skin_buffer_alloc(sizeof(struct line));
1999 line->update_mode = SKIN_REFRESH_STATIC;
2000 curr_line = line;
2001 element->data = line;
2003 break;
2004 case LINE_ALTERNATOR:
2006 struct line_alternator *alternator =
2007 (struct line_alternator *)skin_buffer_alloc(sizeof(struct line_alternator));
2008 alternator->current_line = 0;
2009 #ifndef __PCTOOL__
2010 alternator->next_change_tick = current_tick;
2011 #endif
2012 element->data = alternator;
2014 break;
2015 case CONDITIONAL:
2017 struct conditional *conditional =
2018 (struct conditional *)skin_buffer_alloc(sizeof(struct conditional));
2019 conditional->last_value = -1;
2020 conditional->token = element->data;
2021 element->data = conditional;
2022 if (!check_feature_tag(element->tag->type))
2024 return FEATURE_NOT_AVAILABLE;
2026 return CALLBACK_OK;
2028 case TEXT:
2029 curr_line->update_mode |= SKIN_REFRESH_STATIC;
2030 break;
2031 default:
2032 break;
2034 return CALLBACK_OK;
2037 /* to setup up the wps-data from a format-buffer (isfile = false)
2038 from a (wps-)file (isfile = true)*/
2039 bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
2040 const char *buf, bool isfile)
2042 char *wps_buffer = NULL;
2043 if (!wps_data || !buf)
2044 return false;
2045 #ifdef HAVE_ALBUMART
2046 int status;
2047 struct mp3entry *curtrack;
2048 long offset;
2049 struct skin_albumart old_aa = {.state = WPS_ALBUMART_NONE};
2050 if (wps_data->albumart)
2052 old_aa.state = wps_data->albumart->state;
2053 old_aa.height = wps_data->albumart->height;
2054 old_aa.width = wps_data->albumart->width;
2056 #endif
2057 #ifdef HAVE_LCD_BITMAP
2058 int i;
2059 for (i=0;i<MAXUSERFONTS;i++)
2061 skinfonts[i].id = -1;
2062 skinfonts[i].name = NULL;
2064 #endif
2065 #ifdef DEBUG_SKIN_ENGINE
2066 if (isfile && debug_wps)
2068 DEBUGF("\n=====================\nLoading '%s'\n=====================\n", buf);
2070 #endif
2073 skin_data_reset(wps_data);
2074 wps_data->wps_loaded = false;
2075 curr_screen = screen;
2076 curr_line = NULL;
2077 curr_vp = NULL;
2078 curr_viewport_element = NULL;
2080 if (isfile)
2082 int fd = open_utf8(buf, O_RDONLY);
2084 if (fd < 0)
2085 return false;
2087 /* get buffer space from the plugin buffer */
2088 size_t buffersize = 0;
2089 wps_buffer = (char *)plugin_get_buffer(&buffersize);
2091 if (!wps_buffer)
2092 return false;
2094 /* copy the file's content to the buffer for parsing,
2095 ensuring that every line ends with a newline char. */
2096 unsigned int start = 0;
2097 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
2099 start += strlen(wps_buffer + start);
2100 if (start < buffersize - 1)
2102 wps_buffer[start++] = '\n';
2103 wps_buffer[start] = 0;
2106 close(fd);
2107 if (start <= 0)
2108 return false;
2110 else
2112 wps_buffer = (char*)buf;
2114 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
2115 wps_data->backdrop = "-";
2116 wps_data->backdrop_id = -1;
2117 #endif
2118 /* parse the skin source */
2119 #ifndef APPLICATION
2120 skin_buffer_save_position();
2121 #endif
2122 wps_data->tree = skin_parse(wps_buffer, skin_element_callback, wps_data);
2123 if (!wps_data->tree) {
2124 skin_data_reset(wps_data);
2125 #ifndef APPLICATION
2126 skin_buffer_restore_position();
2127 #endif
2128 return false;
2131 #ifdef HAVE_LCD_BITMAP
2132 char bmpdir[MAX_PATH];
2133 if (isfile)
2135 /* get the bitmap dir */
2136 char *dot = strrchr(buf, '.');
2137 strlcpy(bmpdir, buf, dot - buf + 1);
2139 else
2141 snprintf(bmpdir, MAX_PATH, "%s", BACKDROP_DIR);
2143 /* load the bitmaps that were found by the parsing */
2144 if (!load_skin_bitmaps(wps_data, bmpdir) ||
2145 !skin_load_fonts(wps_data))
2147 skin_data_reset(wps_data);
2148 #ifndef APPLICATION
2149 skin_buffer_restore_position();
2150 #endif
2151 return false;
2153 #endif
2154 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
2155 status = audio_status();
2156 if (status & AUDIO_STATUS_PLAY)
2158 struct skin_albumart *aa = wps_data->albumart;
2159 if (aa && ((aa->state && !old_aa.state) ||
2160 (aa->state &&
2161 (((old_aa.height != aa->height) ||
2162 (old_aa.width != aa->width))))))
2164 curtrack = audio_current_track();
2165 offset = curtrack->offset;
2166 audio_stop();
2167 if (!(status & AUDIO_STATUS_PAUSE))
2168 audio_play(offset);
2171 #endif
2172 wps_data->wps_loaded = true;
2173 #ifdef DEBUG_SKIN_ENGINE
2174 // if (isfile && debug_wps)
2175 // debug_skin_usage();
2176 #endif
2177 return true;