fix red
[maemo-rb.git] / apps / gui / skin_engine / skin_parser.c
blobfaf03ec0f194449666be37100e7aaa40d58db581
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 "file.h"
28 #include "misc.h"
29 #include "plugin.h"
30 #include "viewport.h"
32 #include "skin_buffer.h"
33 #include "skin_parser.h"
34 #include "tag_table.h"
36 #ifdef __PCTOOL__
37 #ifdef WPSEDITOR
38 #include "proxy.h"
39 #include "sysfont.h"
40 #else
41 #include "action.h"
42 #include "checkwps.h"
43 #include "audio.h"
44 #define lang_is_rtl() (false)
45 #define DEBUGF printf
46 #endif /*WPSEDITOR*/
47 #else
48 #include "debug.h"
49 #include "language.h"
50 #endif /*__PCTOOL__*/
52 #include <ctype.h>
53 #include <stdbool.h>
54 #include "font.h"
56 #include "wps_internals.h"
57 #include "skin_engine.h"
58 #include "settings.h"
59 #include "settings_list.h"
60 #if CONFIG_TUNER
61 #include "radio.h"
62 #include "tuner.h"
63 #endif
64 #include "skin_fonts.h"
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
80 static bool isdefault(struct skin_tag_parameter *param)
82 return param->type == DEFAULT;
86 /* which screen are we parsing for? */
87 static enum screen_type curr_screen;
89 /* the current viewport */
90 static struct skin_element *curr_viewport_element;
91 static struct skin_viewport *curr_vp;
93 static struct line *curr_line;
95 static int follow_lang_direction = 0;
97 typedef int (*parse_function)(struct skin_element *element,
98 struct wps_token *token,
99 struct wps_data *wps_data);
101 #ifdef HAVE_LCD_BITMAP
102 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
103 * chains require the order to be kept.
105 static void add_to_ll_chain(struct skin_token_list **list, struct skin_token_list *item)
107 if (*list == NULL)
108 *list = item;
109 else
111 struct skin_token_list *t = *list;
112 while (t->next)
113 t = t->next;
114 t->next = item;
118 /* traverse the image linked-list for an image */
119 struct gui_img* find_image(const char *label, struct wps_data *data)
121 struct skin_token_list *list = data->images;
122 while (list)
124 struct gui_img *img = (struct gui_img *)list->token->value.data;
125 if (!strcmp(img->label,label))
126 return img;
127 list = list->next;
129 return NULL;
132 #endif
134 /* traverse the viewport linked list for a viewport */
135 struct skin_viewport* find_viewport(const char *label, bool uivp, struct wps_data *data)
137 struct skin_element *list = data->tree;
138 while (list)
140 struct skin_viewport *vp = (struct skin_viewport *)list->data;
141 if (vp->label && (vp->is_infovp == uivp) &&
142 !strcmp(vp->label, label))
143 return vp;
144 list = list->next;
146 return NULL;
149 #ifdef HAVE_LCD_BITMAP
151 /* create and init a new wpsll item.
152 * passing NULL to token will alloc a new one.
153 * You should only pass NULL for the token when the token type (table above)
154 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
156 static struct skin_token_list *new_skin_token_list_item(struct wps_token *token,
157 void* token_data)
159 struct skin_token_list *llitem =
160 (struct skin_token_list *)skin_buffer_alloc(sizeof(struct skin_token_list));
161 if (!token)
162 token = (struct wps_token*)skin_buffer_alloc(sizeof(struct wps_token));
163 if (!llitem || !token)
164 return NULL;
165 llitem->next = NULL;
166 llitem->token = token;
167 if (token_data)
168 llitem->token->value.data = token_data;
169 return llitem;
172 static int parse_statusbar_tags(struct skin_element* element,
173 struct wps_token *token,
174 struct wps_data *wps_data)
176 (void)element;
177 if (token->type == SKIN_TOKEN_DRAW_INBUILTBAR)
179 token->value.data = (void*)&curr_vp->vp;
181 else
183 struct skin_element *def_vp = wps_data->tree;
184 struct skin_viewport *default_vp = def_vp->data;
185 if (def_vp->params_count == 0)
187 wps_data->wps_sb_tag = true;
188 wps_data->show_sb_on_wps = (token->type == SKIN_TOKEN_ENABLE_THEME);
190 if (wps_data->show_sb_on_wps)
192 viewport_set_defaults(&default_vp->vp, curr_screen);
194 else
196 viewport_set_fullscreen(&default_vp->vp, curr_screen);
198 #ifdef HAVE_REMOTE_LCD
199 /* viewport_set_defaults() sets the font to FONT_UI+curr_screen.
200 * This parser requires font 1 to always be the UI font,
201 * so force it back to FONT_UI and handle the screen number at the end */
202 default_vp->vp.font = FONT_UI;
203 #endif
205 return 0;
208 static int get_image_id(int c)
210 if(c >= 'a' && c <= 'z')
211 return c - 'a';
212 else if(c >= 'A' && c <= 'Z')
213 return c - 'A' + 26;
214 else
215 return -1;
218 char *get_image_filename(const char *start, const char* bmpdir,
219 char *buf, int buf_size)
221 snprintf(buf, buf_size, "%s/%s", bmpdir, start);
223 return buf;
226 static int parse_image_display(struct skin_element *element,
227 struct wps_token *token,
228 struct wps_data *wps_data)
230 char *label = element->params[0].data.text;
231 char sublabel = '\0';
232 int subimage;
233 struct gui_img *img;
234 struct image_display *id = skin_buffer_alloc(sizeof(struct image_display));
236 if (element->params_count == 1 && strlen(label) <= 2)
238 /* backwards compatability. Allow %xd(Aa) to still work */
239 sublabel = label[1];
240 label[1] = '\0';
242 /* sanity check */
243 img = find_image(label, wps_data);
244 if (!img || !id)
246 return WPS_ERROR_INVALID_PARAM;
248 id->label = label;
249 id->offset = 0;
250 id->token = NULL;
251 img->using_preloaded_icons = false;
252 if (!strcmp(img->bm.data, "__list_icons__"))
254 img->num_subimages = Icon_Last_Themeable;
255 img->using_preloaded_icons = true;
256 token->type = SKIN_TOKEN_IMAGE_DISPLAY_LISTICON;
259 if (element->params_count > 1)
261 if (element->params[1].type == CODE)
262 id->token = element->params[1].data.code->data;
263 /* specify a number. 1 being the first subimage (i.e top) NOT 0 */
264 else if (element->params[1].type == INTEGER)
265 id->subimage = element->params[1].data.number - 1;
266 if (element->params_count > 2)
267 id->offset = element->params[2].data.number;
269 else
271 if ((subimage = get_image_id(sublabel)) != -1)
273 if (subimage >= img->num_subimages)
274 return WPS_ERROR_INVALID_PARAM;
275 id->subimage = subimage;
276 } else {
277 id->subimage = 0;
280 token->value.data = id;
281 return 0;
284 static int parse_image_load(struct skin_element *element,
285 struct wps_token *token,
286 struct wps_data *wps_data)
288 const char* filename;
289 const char* id;
290 int x,y;
291 struct gui_img *img;
293 /* format: %x(n,filename.bmp,x,y)
294 or %xl(n,filename.bmp,x,y)
295 or %xl(n,filename.bmp,x,y,num_subimages)
298 id = element->params[0].data.text;
299 filename = element->params[1].data.text;
300 x = element->params[2].data.number;
301 y = element->params[3].data.number;
303 /* check the image number and load state */
304 if(find_image(id, wps_data))
306 /* Invalid image ID */
307 return WPS_ERROR_INVALID_PARAM;
309 img = (struct gui_img*)skin_buffer_alloc(sizeof(struct gui_img));
310 if (!img)
311 return WPS_ERROR_INVALID_PARAM;
312 /* save a pointer to the filename */
313 img->bm.data = (char*)filename;
314 img->label = id;
315 img->x = x;
316 img->y = y;
317 img->num_subimages = 1;
318 img->always_display = false;
319 img->display = -1;
321 /* save current viewport */
322 img->vp = &curr_vp->vp;
324 if (token->type == SKIN_TOKEN_IMAGE_DISPLAY)
326 img->always_display = true;
328 else if (element->params_count == 5)
330 img->num_subimages = element->params[4].data.number;
331 if (img->num_subimages <= 0)
332 return WPS_ERROR_INVALID_PARAM;
335 struct skin_token_list *item =
336 (struct skin_token_list *)new_skin_token_list_item(NULL, img);
337 if (!item)
338 return WPS_ERROR_INVALID_PARAM;
339 add_to_ll_chain(&wps_data->images, item);
341 return 0;
343 struct skin_font {
344 int id; /* the id from font_load */
345 char *name; /* filename without path and extension */
346 int glyphs; /* how many glyphs to reserve room for */
348 static struct skin_font skinfonts[MAXUSERFONTS];
349 static int parse_font_load(struct skin_element *element,
350 struct wps_token *token,
351 struct wps_data *wps_data)
353 (void)wps_data; (void)token;
354 int id = element->params[0].data.number;
355 char *filename = element->params[1].data.text;
356 int glyphs;
357 char *ptr;
359 if(element->params_count > 2)
360 glyphs = element->params[2].data.number;
361 else
362 glyphs = GLYPHS_TO_CACHE;
363 #if defined(DEBUG) || defined(SIMULATOR)
364 if (skinfonts[id-FONT_FIRSTUSERFONT].name != NULL)
366 DEBUGF("font id %d already being used\n", id);
368 #endif
369 /* make sure the filename contains .fnt,
370 * we dont actually use it, but require it anyway */
371 ptr = strchr(filename, '.');
372 if (!ptr || strncmp(ptr, ".fnt", 4))
373 return WPS_ERROR_INVALID_PARAM;
374 skinfonts[id-FONT_FIRSTUSERFONT].id = -1;
375 skinfonts[id-FONT_FIRSTUSERFONT].name = filename;
376 skinfonts[id-FONT_FIRSTUSERFONT].glyphs = glyphs;
378 return 0;
382 #ifdef HAVE_LCD_BITMAP
384 static int parse_playlistview(struct skin_element *element,
385 struct wps_token *token,
386 struct wps_data *wps_data)
388 (void)wps_data;
389 struct playlistviewer *viewer =
390 (struct playlistviewer *)skin_buffer_alloc(sizeof(struct playlistviewer));
391 if (!viewer)
392 return WPS_ERROR_INVALID_PARAM;
393 viewer->vp = &curr_vp->vp;
394 viewer->show_icons = true;
395 viewer->start_offset = element->params[0].data.number;
396 viewer->line = element->params[1].data.code;
398 token->value.data = (void*)viewer;
400 return 0;
402 #endif
404 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
406 static int parse_viewportcolour(struct skin_element *element,
407 struct wps_token *token,
408 struct wps_data *wps_data)
410 (void)wps_data;
411 struct skin_tag_parameter *param = element->params;
412 struct viewport_colour *colour =
413 (struct viewport_colour *)skin_buffer_alloc(sizeof(struct viewport_colour));
414 if (!colour)
415 return -1;
416 if (isdefault(param))
418 colour->colour = get_viewport_default_colour(curr_screen,
419 token->type == SKIN_TOKEN_VIEWPORT_FGCOLOUR);
421 else
423 if (!parse_color(curr_screen, param->data.text, &colour->colour))
424 return -1;
426 colour->vp = &curr_vp->vp;
427 token->value.data = colour;
428 if (element->line == curr_viewport_element->line)
430 if (token->type == SKIN_TOKEN_VIEWPORT_FGCOLOUR)
432 curr_vp->start_fgcolour = colour->colour;
433 curr_vp->vp.fg_pattern = colour->colour;
435 else
437 curr_vp->start_bgcolour = colour->colour;
438 curr_vp->vp.bg_pattern = colour->colour;
441 return 0;
444 static int parse_image_special(struct skin_element *element,
445 struct wps_token *token,
446 struct wps_data *wps_data)
448 (void)wps_data; /* kill warning */
449 (void)token;
451 #if LCD_DEPTH > 1
452 char *filename;
453 if (token->type == SKIN_TOKEN_IMAGE_BACKDROP)
455 if (isdefault(&element->params[0]))
457 filename = "-";
459 else
461 filename = element->params[0].data.text;
462 /* format: %X(filename.bmp) or %X(d) */
463 if (!strcmp(filename, "d"))
464 filename = NULL;
466 wps_data->backdrop = filename;
468 #endif
470 return 0;
472 #endif
474 #endif /* HAVE_LCD_BITMAP */
476 static int parse_setting_and_lang(struct skin_element *element,
477 struct wps_token *token,
478 struct wps_data *wps_data)
480 /* NOTE: both the string validations that happen in here will
481 * automatically PASS on checkwps because its too hard to get
482 * settings_list.c and english.lang built for it.
483 * If that ever changes remove the #ifndef __PCTOOL__'s here
485 (void)wps_data;
486 char *temp = element->params[0].data.text;
487 int i;
489 if (token->type == SKIN_TOKEN_TRANSLATEDSTRING)
491 #ifndef __PCTOOL__
492 i = lang_english_to_id(temp);
493 if (i < 0)
494 return WPS_ERROR_INVALID_PARAM;
495 #endif
497 else
499 /* Find the setting */
500 for (i=0; i<nb_settings; i++)
501 if (settings[i].cfg_name &&
502 !strcmp(settings[i].cfg_name, temp))
503 break;
504 #ifndef __PCTOOL__
505 if (i == nb_settings)
506 return WPS_ERROR_INVALID_PARAM;
507 #endif
509 /* Store the setting number */
510 token->value.i = i;
511 return 0;
513 static int parse_logical_if(struct skin_element *element,
514 struct wps_token *token,
515 struct wps_data *wps_data)
517 (void)wps_data;
518 char *op = element->params[1].data.text;
519 struct logical_if *lif = skin_buffer_alloc(sizeof(struct logical_if));
520 if (!lif)
521 return -1;
522 token->value.data = lif;
523 lif->token = element->params[0].data.code->data;
525 if (!strcmp(op, "="))
526 lif->op = IF_EQUALS;
527 if (!strcmp(op, "!="))
528 lif->op = IF_NOTEQUALS;
529 if (!strcmp(op, "<"))
530 lif->op = IF_LESSTHAN;
531 if (!strcmp(op, "<="))
532 lif->op = IF_LESSTHAN_EQ;
533 if (!strcmp(op, ">"))
534 lif->op = IF_GREATERTHAN;
535 if (!strcmp(op, ">="))
536 lif->op = IF_GREATERTHAN_EQ;
538 memcpy(&lif->operand, &element->params[2], sizeof(lif->operand));
539 if (element->params_count > 3)
540 lif->num_options = element->params[3].data.number;
541 else
542 lif->num_options = TOKEN_VALUE_ONLY;
543 return 0;
546 static int parse_timeout_tag(struct skin_element *element,
547 struct wps_token *token,
548 struct wps_data *wps_data)
550 (void)wps_data;
551 int val = 0;
552 if (element->params_count == 0)
554 switch (token->type)
556 case SKIN_TOKEN_SUBLINE_TIMEOUT:
557 return -1;
558 case SKIN_TOKEN_BUTTON_VOLUME:
559 case SKIN_TOKEN_TRACK_STARTING:
560 case SKIN_TOKEN_TRACK_ENDING:
561 case SKIN_TOKEN_LASTTOUCH:
562 val = 10;
563 break;
564 default:
565 break;
568 else
569 val = element->params[0].data.number;
570 token->value.i = val * TIMEOUT_UNIT;
571 if (token->type == SKIN_TOKEN_SUBLINE_TIMEOUT)
572 curr_line->timeout = token->value.i;
573 return 0;
576 static int parse_progressbar_tag(struct skin_element* element,
577 struct wps_token *token,
578 struct wps_data *wps_data)
580 #ifdef HAVE_LCD_BITMAP
581 struct progressbar *pb;
582 struct skin_token_list *item;
583 struct viewport *vp = &curr_vp->vp;
584 struct skin_tag_parameter *param = element->params;
585 int curr_param = 0;
587 if (element->params_count == 0 &&
588 element->tag->type != SKIN_TOKEN_PROGRESSBAR)
589 return 0; /* nothing to do */
590 pb = (struct progressbar*)skin_buffer_alloc(sizeof(struct progressbar));
592 token->value.data = pb;
594 if (!pb)
595 return WPS_ERROR_INVALID_PARAM;
596 pb->vp = vp;
597 pb->have_bitmap_pb = false;
598 pb->bm.data = NULL; /* no bitmap specified */
599 pb->follow_lang_direction = follow_lang_direction > 0;
600 pb->nofill = false;
601 pb->slider = NULL;
602 pb->invert_fill_direction = false;
603 pb->horizontal = true;
605 if (element->params_count == 0)
607 pb->x = 0;
608 pb->width = vp->width;
609 pb->height = SYSFONT_HEIGHT-2;
610 pb->y = -1; /* Will be computed during the rendering */
611 pb->type = element->tag->type;
612 return 0;
615 item = new_skin_token_list_item(token, pb);
616 if (!item)
617 return -1;
618 add_to_ll_chain(&wps_data->progressbars, item);
620 /* (x,y,width,height,filename) */
621 if (!isdefault(param))
622 pb->x = param->data.number;
623 else
624 pb->x = 0;
625 param++;
627 if (!isdefault(param))
628 pb->y = param->data.number;
629 else
630 pb->y = -1; /* computed at rendering */
631 param++;
633 if (!isdefault(param))
634 pb->width = param->data.number;
635 else
636 pb->width = vp->width - pb->x;
637 param++;
639 if (!isdefault(param))
641 /* A zero height makes no sense - reject it */
642 if (param->data.number == 0)
643 return WPS_ERROR_INVALID_PARAM;
645 pb->height = param->data.number;
647 else
649 if (vp->font > FONT_UI)
650 pb->height = -1; /* calculate at display time */
651 else
653 #ifndef __PCTOOL__
654 pb->height = font_get(vp->font)->height;
655 #else
656 pb->height = 8;
657 #endif
660 param++;
661 if (!isdefault(param))
662 pb->bm.data = param->data.text;
664 curr_param = 5;
665 pb->horizontal = pb->width > pb->height;
666 while (curr_param < element->params_count)
668 param++;
669 if (!strcmp(param->data.text, "invert"))
670 pb->invert_fill_direction = true;
671 else if (!strcmp(param->data.text, "nofill"))
672 pb->nofill = true;
673 else if (!strcmp(param->data.text, "slider"))
675 if (curr_param+1 < element->params_count)
677 curr_param++;
678 param++;
679 pb->slider = find_image(param->data.text, wps_data);
680 if (!pb->slider)
681 return -1;
684 else if (!strcmp(param->data.text, "vertical"))
686 pb->horizontal = false;
687 if (isdefault(&element->params[3]))
688 pb->height = vp->height - pb->x;
690 else if (!strcmp(param->data.text, "horizontal"))
691 pb->horizontal = true;
693 curr_param++;
697 if (token->type == SKIN_TOKEN_VOLUME)
698 token->type = SKIN_TOKEN_VOLUMEBAR;
699 else if (token->type == SKIN_TOKEN_BATTERY_PERCENT)
700 token->type = SKIN_TOKEN_BATTERY_PERCENTBAR;
701 pb->type = token->type;
703 return 0;
705 #else
706 (void)element;
707 if (token->type == SKIN_TOKEN_PROGRESSBAR ||
708 token->type == SKIN_TOKEN_PLAYER_PROGRESSBAR)
710 wps_data->full_line_progressbar =
711 token->type == SKIN_TOKEN_PLAYER_PROGRESSBAR;
713 return 0;
715 #endif
718 #ifdef HAVE_ALBUMART
719 static int parse_albumart_load(struct skin_element* element,
720 struct wps_token *token,
721 struct wps_data *wps_data)
723 struct dim dimensions;
724 int albumart_slot;
725 bool swap_for_rtl = lang_is_rtl() && follow_lang_direction;
726 struct skin_albumart *aa =
727 (struct skin_albumart *)skin_buffer_alloc(sizeof(struct skin_albumart));
728 (void)token; /* silence warning */
729 if (!aa)
730 return -1;
732 /* reset albumart info in wps */
733 aa->width = -1;
734 aa->height = -1;
735 aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
736 aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
738 aa->x = element->params[0].data.number;
739 aa->y = element->params[1].data.number;
740 aa->width = element->params[2].data.number;
741 aa->height = element->params[3].data.number;
743 aa->vp = &curr_vp->vp;
744 aa->draw_handle = -1;
746 /* if we got here, we parsed everything ok .. ! */
747 if (aa->width < 0)
748 aa->width = 0;
749 else if (aa->width > LCD_WIDTH)
750 aa->width = LCD_WIDTH;
752 if (aa->height < 0)
753 aa->height = 0;
754 else if (aa->height > LCD_HEIGHT)
755 aa->height = LCD_HEIGHT;
757 if (swap_for_rtl)
758 aa->x = LCD_WIDTH - (aa->x + aa->width);
760 aa->state = WPS_ALBUMART_LOAD;
761 wps_data->albumart = aa;
763 dimensions.width = aa->width;
764 dimensions.height = aa->height;
766 albumart_slot = playback_claim_aa_slot(&dimensions);
768 if (0 <= albumart_slot)
769 wps_data->playback_aa_slot = albumart_slot;
771 if (element->params_count > 4 && !isdefault(&element->params[4]))
773 switch (*element->params[4].data.text)
775 case 'l':
776 case 'L':
777 if (swap_for_rtl)
778 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
779 else
780 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
781 break;
782 case 'c':
783 case 'C':
784 aa->xalign = WPS_ALBUMART_ALIGN_CENTER;
785 break;
786 case 'r':
787 case 'R':
788 if (swap_for_rtl)
789 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
790 else
791 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
792 break;
795 if (element->params_count > 5 && !isdefault(&element->params[5]))
797 switch (*element->params[5].data.text)
799 case 't':
800 case 'T':
801 aa->yalign = WPS_ALBUMART_ALIGN_TOP;
802 break;
803 case 'c':
804 case 'C':
805 aa->yalign = WPS_ALBUMART_ALIGN_CENTER;
806 break;
807 case 'b':
808 case 'B':
809 aa->yalign = WPS_ALBUMART_ALIGN_BOTTOM;
810 break;
813 return 0;
816 #endif /* HAVE_ALBUMART */
818 #ifdef HAVE_TOUCHSCREEN
820 struct touchaction {const char* s; int action;};
821 static const struct touchaction touchactions[] = {
822 /* generic actions, convert to screen actions on use */
823 {"prev", ACTION_STD_PREV }, {"next", ACTION_STD_NEXT },
824 {"rwd", ACTION_STD_PREVREPEAT }, {"ffwd", ACTION_STD_NEXTREPEAT },
825 {"hotkey", ACTION_STD_HOTKEY}, {"select", ACTION_STD_OK },
826 {"menu", ACTION_STD_MENU }, {"cancel", ACTION_STD_CANCEL },
827 {"contextmenu", ACTION_STD_CONTEXT},{"quickscreen", ACTION_STD_QUICKSCREEN },
828 /* not really WPS specific, but no equivilant ACTION_STD_* */
829 {"voldown", ACTION_WPS_VOLDOWN}, {"volup", ACTION_WPS_VOLUP},
831 /* generic settings changers */
832 {"setting_inc", ACTION_SETTINGS_INC}, {"setting_dec", ACTION_SETTINGS_DEC},
834 /* WPS specific actions */
835 {"browse", ACTION_WPS_BROWSE },
836 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
837 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
838 {"pitch", ACTION_WPS_PITCHSCREEN}, {"playlist", ACTION_WPS_VIEW_PLAYLIST },
840 #if CONFIG_TUNER
841 /* FM screen actions */
842 /* Also allow browse, play, stop from WPS codes */
843 {"mode", ACTION_FM_MODE }, {"record", ACTION_FM_RECORD },
844 {"presets", ACTION_FM_PRESET},
845 #endif
848 static int parse_touchregion(struct skin_element *element,
849 struct wps_token *token,
850 struct wps_data *wps_data)
852 (void)token;
853 unsigned i, imax;
854 struct touchregion *region = NULL;
855 const char *action;
856 const char pb_string[] = "progressbar";
857 const char vol_string[] = "volume";
858 char temp[20];
860 /* format: %T(x,y,width,height,action)
861 * if action starts with & the area must be held to happen
862 * action is one of:
863 * play - play/pause playback
864 * stop - stop playback, exit the wps
865 * prev - prev track
866 * next - next track
867 * ffwd - seek forward
868 * rwd - seek backwards
869 * menu - go back to the main menu
870 * browse - go back to the file/db browser
871 * shuffle - toggle shuffle mode
872 * repmode - cycle the repeat mode
873 * quickscreen - go into the quickscreen
874 * contextmenu - open the context menu
875 * playlist - go into the playlist
876 * pitch - go into the pitchscreen
877 * volup - increase volume by one step
878 * voldown - decrease volume by one step
882 region = (struct touchregion*)skin_buffer_alloc(sizeof(struct touchregion));
883 if (!region)
884 return WPS_ERROR_INVALID_PARAM;
886 /* should probably do some bounds checking here with the viewport... but later */
887 region->action = ACTION_NONE;
888 region->x = element->params[0].data.number;
889 region->y = element->params[1].data.number;
890 region->width = element->params[2].data.number;
891 region->height = element->params[3].data.number;
892 region->wvp = curr_vp;
893 region->armed = false;
894 region->reverse_bar = false;
895 region->extradata = NULL;
896 action = element->params[4].data.text;
898 strcpy(temp, action);
899 action = temp;
901 if (*action == '!')
903 region->reverse_bar = true;
904 action++;
907 if(!strcmp(pb_string, action))
908 region->type = WPS_TOUCHREGION_SCROLLBAR;
909 else if(!strcmp(vol_string, action))
910 region->type = WPS_TOUCHREGION_VOLUME;
911 else
913 region->type = WPS_TOUCHREGION_ACTION;
915 if (*action == '&')
917 action++;
918 region->repeat = true;
920 else
921 region->repeat = false;
923 imax = ARRAYLEN(touchactions);
924 for (i = 0; i < imax; i++)
926 /* try to match with one of our touchregion screens */
927 if (!strcmp(touchactions[i].s, action))
929 region->action = touchactions[i].action;
930 if (region->action == ACTION_SETTINGS_INC ||
931 region->action == ACTION_SETTINGS_DEC)
933 if (element->params_count < 6)
935 return WPS_ERROR_INVALID_PARAM;
937 else
939 char *name = element->params[5].data.text;
940 int j;
941 /* Find the setting */
942 for (j=0; j<nb_settings; j++)
943 if (settings[j].cfg_name &&
944 !strcmp(settings[j].cfg_name, name))
945 break;
946 if (j==nb_settings)
947 return WPS_ERROR_INVALID_PARAM;
948 region->extradata = (void*)&settings[j];
951 break;
954 if (region->action == ACTION_NONE)
955 return WPS_ERROR_INVALID_PARAM;
957 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
958 if (!item)
959 return WPS_ERROR_INVALID_PARAM;
960 add_to_ll_chain(&wps_data->touchregions, item);
961 return 0;
963 #endif
965 static bool check_feature_tag(const int type)
967 switch (type)
969 case SKIN_TOKEN_RTC_PRESENT:
970 #if CONFIG_RTC
971 return true;
972 #else
973 return false;
974 #endif
975 case SKIN_TOKEN_HAVE_RECORDING:
976 #ifdef HAVE_RECORDING
977 return true;
978 #else
979 return false;
980 #endif
981 case SKIN_TOKEN_HAVE_TUNER:
982 #if CONFIG_TUNER
983 if (radio_hardware_present())
984 return true;
985 #endif
986 return false;
988 #if CONFIG_TUNER
989 case SKIN_TOKEN_HAVE_RDS:
990 #ifdef HAVE_RDS_CAP
991 return true;
992 #else
993 return false;
994 #endif /* HAVE_RDS_CAP */
995 #endif /* CONFIG_TUNER */
996 default: /* not a tag we care about, just don't skip */
997 return true;
1002 * initial setup of wps_data; does reset everything
1003 * except fields which need to survive, i.e.
1006 static void skin_data_reset(struct wps_data *wps_data)
1008 wps_data->tree = NULL;
1009 #ifdef HAVE_LCD_BITMAP
1010 wps_data->images = NULL;
1011 wps_data->progressbars = NULL;
1012 #endif
1013 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1014 if (wps_data->backdrop_id >= 0)
1015 skin_backdrop_unload(wps_data->backdrop_id);
1016 wps_data->backdrop = NULL;
1017 #endif
1018 #ifdef HAVE_TOUCHSCREEN
1019 wps_data->touchregions = NULL;
1020 #endif
1021 #ifdef HAVE_ALBUMART
1022 wps_data->albumart = NULL;
1023 if (wps_data->playback_aa_slot >= 0)
1025 playback_release_aa_slot(wps_data->playback_aa_slot);
1026 wps_data->playback_aa_slot = -1;
1028 #endif
1030 #ifdef HAVE_LCD_BITMAP
1031 wps_data->peak_meter_enabled = false;
1032 wps_data->wps_sb_tag = false;
1033 wps_data->show_sb_on_wps = false;
1034 #else /* HAVE_LCD_CHARCELLS */
1035 /* progress bars */
1036 int i;
1037 for (i = 0; i < 8; i++)
1039 wps_data->wps_progress_pat[i] = 0;
1041 wps_data->full_line_progressbar = false;
1042 #endif
1043 wps_data->wps_loaded = false;
1046 #ifdef HAVE_LCD_BITMAP
1047 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
1049 (void)wps_data; /* only needed for remote targets */
1050 char img_path[MAX_PATH];
1051 int fd;
1052 get_image_filename(bitmap->data, bmpdir,
1053 img_path, sizeof(img_path));
1055 /* load the image */
1056 int format;
1057 #ifdef HAVE_REMOTE_LCD
1058 if (curr_screen == SCREEN_REMOTE)
1059 format = FORMAT_ANY|FORMAT_REMOTE;
1060 else
1061 #endif
1062 format = FORMAT_ANY|FORMAT_TRANSPARENT;
1064 fd = open(img_path, O_RDONLY);
1065 if (fd < 0)
1066 return false;
1067 size_t buf_size = read_bmp_file(img_path, bitmap, 0,
1068 format|FORMAT_RETURN_SIZE, NULL);
1069 char* imgbuf = (char*)skin_buffer_alloc(buf_size);
1070 if (!imgbuf)
1072 close(fd);
1073 return NULL;
1075 lseek(fd, 0, SEEK_SET);
1076 bitmap->data = imgbuf;
1077 int ret = read_bmp_fd(fd, bitmap, buf_size, format, NULL);
1079 close(fd);
1080 if (ret > 0)
1082 return true;
1084 else
1086 /* Abort if we can't load an image */
1087 DEBUGF("Couldn't load '%s'\n", img_path);
1088 return false;
1092 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
1094 struct skin_token_list *list;
1095 bool retval = true; /* return false if a single image failed to load */
1096 /* do the progressbars */
1097 list = wps_data->progressbars;
1098 while (list)
1100 struct progressbar *pb = (struct progressbar*)list->token->value.data;
1101 if (pb->bm.data)
1103 pb->have_bitmap_pb = load_skin_bmp(wps_data, &pb->bm, bmpdir);
1104 if (!pb->have_bitmap_pb) /* no success */
1105 retval = false;
1107 list = list->next;
1109 /* regular images */
1110 list = wps_data->images;
1111 while (list)
1113 struct gui_img *img = (struct gui_img*)list->token->value.data;
1114 if (img->bm.data)
1116 if (img->using_preloaded_icons)
1118 img->loaded = true;
1119 list->token->type = SKIN_TOKEN_IMAGE_DISPLAY_LISTICON;
1121 else
1123 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
1124 if (img->loaded)
1125 img->subimage_height = img->bm.height / img->num_subimages;
1126 else
1127 retval = false;
1130 list = list->next;
1133 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1134 wps_data->backdrop_id = skin_backdrop_assign(wps_data->backdrop, bmpdir, curr_screen);
1135 #endif /* has backdrop support */
1136 return retval;
1139 static bool skin_load_fonts(struct wps_data *data)
1141 /* don't spit out after the first failue to aid debugging */
1142 bool success = true;
1143 struct skin_element *vp_list;
1144 int font_id;
1145 /* walk though each viewport and assign its font */
1146 for(vp_list = data->tree; vp_list; vp_list = vp_list->next)
1148 /* first, find the viewports that have a non-sys/ui-font font */
1149 struct skin_viewport *skin_vp =
1150 (struct skin_viewport*)vp_list->data;
1151 struct viewport *vp = &skin_vp->vp;
1154 if (vp->font <= FONT_UI)
1155 { /* the usual case -> built-in fonts */
1156 #ifdef HAVE_REMOTE_LCD
1157 if (vp->font == FONT_UI)
1158 vp->font += curr_screen;
1159 #endif
1160 continue;
1162 font_id = vp->font;
1164 /* now find the corresponding skin_font */
1165 struct skin_font *font = &skinfonts[font_id-FONT_FIRSTUSERFONT];
1166 if (!font->name)
1168 if (success)
1170 DEBUGF("font %d not specified\n", font_id);
1172 success = false;
1173 continue;
1176 /* load the font - will handle loading the same font again if
1177 * multiple viewports use the same */
1178 if (font->id < 0)
1180 char *dot = strchr(font->name, '.');
1181 *dot = '\0';
1182 font->id = skin_font_load(font->name,
1183 skinfonts[font_id-FONT_FIRSTUSERFONT].glyphs);
1186 if (font->id < 0)
1188 DEBUGF("Unable to load font %d: '%s.fnt'\n",
1189 font_id, font->name);
1190 font->name = NULL; /* to stop trying to load it again if we fail */
1191 success = false;
1192 font->name = NULL;
1193 continue;
1196 /* finally, assign the font_id to the viewport */
1197 vp->font = font->id;
1199 return success;
1202 #endif /* HAVE_LCD_BITMAP */
1203 static int convert_viewport(struct wps_data *data, struct skin_element* element)
1205 struct skin_viewport *skin_vp =
1206 (struct skin_viewport *)skin_buffer_alloc(sizeof(struct skin_viewport));
1207 struct screen *display = &screens[curr_screen];
1209 if (!skin_vp)
1210 return CALLBACK_ERROR;
1212 skin_vp->hidden_flags = 0;
1213 skin_vp->label = NULL;
1214 skin_vp->is_infovp = false;
1215 element->data = skin_vp;
1216 curr_vp = skin_vp;
1217 curr_viewport_element = element;
1219 viewport_set_defaults(&skin_vp->vp, curr_screen);
1220 #ifdef HAVE_REMOTE_LCD
1221 /* viewport_set_defaults() sets the font to FONT_UI+curr_screen.
1222 * This parser requires font 1 to always be the UI font,
1223 * so force it back to FONT_UI and handle the screen number at the end */
1224 skin_vp->vp.font = FONT_UI;
1225 #endif
1227 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1228 skin_vp->start_fgcolour = skin_vp->vp.fg_pattern;
1229 skin_vp->start_bgcolour = skin_vp->vp.bg_pattern;
1230 #endif
1233 struct skin_tag_parameter *param = element->params;
1234 if (element->params_count == 0) /* default viewport */
1236 if (!data->tree) /* first viewport in the skin */
1237 data->tree = element;
1238 skin_vp->label = VP_DEFAULT_LABEL;
1239 return CALLBACK_OK;
1242 if (element->params_count == 6)
1244 if (element->tag->type == SKIN_TOKEN_UIVIEWPORT_LOAD)
1246 skin_vp->is_infovp = true;
1247 if (isdefault(param))
1249 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
1250 skin_vp->label = VP_DEFAULT_LABEL;
1252 else
1254 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
1255 skin_vp->label = param->data.text;
1258 else
1260 skin_vp->hidden_flags = VP_DRAW_HIDEABLE|VP_DRAW_HIDDEN;
1261 skin_vp->label = param->data.text;
1263 param++;
1265 /* x */
1266 if (!isdefault(param))
1268 skin_vp->vp.x = param->data.number;
1269 if (param->data.number < 0)
1270 skin_vp->vp.x += display->lcdwidth;
1272 param++;
1273 /* y */
1274 if (!isdefault(param))
1276 skin_vp->vp.y = param->data.number;
1277 if (param->data.number < 0)
1278 skin_vp->vp.y += display->lcdheight;
1280 param++;
1281 /* width */
1282 if (!isdefault(param))
1284 skin_vp->vp.width = param->data.number;
1285 if (param->data.number < 0)
1286 skin_vp->vp.width = (skin_vp->vp.width + display->lcdwidth) - skin_vp->vp.x;
1288 else
1290 skin_vp->vp.width = display->lcdwidth - skin_vp->vp.x;
1292 param++;
1293 /* height */
1294 if (!isdefault(param))
1296 skin_vp->vp.height = param->data.number;
1297 if (param->data.number < 0)
1298 skin_vp->vp.height = (skin_vp->vp.height + display->lcdheight) - skin_vp->vp.y;
1300 else
1302 skin_vp->vp.height = display->lcdheight - skin_vp->vp.y;
1304 param++;
1305 #ifdef HAVE_LCD_BITMAP
1306 /* font */
1307 if (!isdefault(param))
1309 skin_vp->vp.font = param->data.number;
1311 #endif
1313 return CALLBACK_OK;
1317 static int skin_element_callback(struct skin_element* element, void* data)
1319 struct wps_data *wps_data = (struct wps_data *)data;
1320 struct wps_token *token;
1321 parse_function function = NULL;
1323 switch (element->type)
1325 /* IMPORTANT: element params are shared, so copy them if needed
1326 * or use then NOW, dont presume they have a long lifespan
1328 case TAG:
1330 token = (struct wps_token*)skin_buffer_alloc(sizeof(struct wps_token));
1331 memset(token, 0, sizeof(*token));
1332 token->type = element->tag->type;
1334 if (element->tag->flags&SKIN_RTC_REFRESH)
1336 #if CONFIG_RTC
1337 curr_line->update_mode |= SKIN_REFRESH_DYNAMIC;
1338 #else
1339 curr_line->update_mode |= SKIN_REFRESH_STATIC;
1340 #endif
1342 else
1343 curr_line->update_mode |= element->tag->flags&SKIN_REFRESH_ALL;
1345 element->data = token;
1347 /* Some tags need special handling for the tag, so add them here */
1348 switch (token->type)
1350 case SKIN_TOKEN_ALIGN_LANGDIRECTION:
1351 follow_lang_direction = 2;
1352 break;
1353 case SKIN_TOKEN_LOGICAL_IF:
1354 function = parse_logical_if;
1355 break;
1356 case SKIN_TOKEN_PROGRESSBAR:
1357 case SKIN_TOKEN_VOLUME:
1358 case SKIN_TOKEN_BATTERY_PERCENT:
1359 case SKIN_TOKEN_PLAYER_PROGRESSBAR:
1360 function = parse_progressbar_tag;
1361 break;
1362 case SKIN_TOKEN_SUBLINE_TIMEOUT:
1363 case SKIN_TOKEN_BUTTON_VOLUME:
1364 case SKIN_TOKEN_TRACK_STARTING:
1365 case SKIN_TOKEN_TRACK_ENDING:
1366 case SKIN_TOKEN_LASTTOUCH:
1367 function = parse_timeout_tag;
1368 break;
1369 #ifdef HAVE_LCD_BITMAP
1370 case SKIN_TOKEN_DISABLE_THEME:
1371 case SKIN_TOKEN_ENABLE_THEME:
1372 case SKIN_TOKEN_DRAW_INBUILTBAR:
1373 function = parse_statusbar_tags;
1374 break;
1375 case SKIN_TOKEN_LIST_TITLE_TEXT:
1376 #ifndef __PCTOOL__
1377 sb_skin_has_title(curr_screen);
1378 #endif
1379 break;
1380 #endif
1381 case SKIN_TOKEN_FILE_DIRECTORY:
1382 token->value.i = element->params[0].data.number;
1383 break;
1384 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1385 case SKIN_TOKEN_VIEWPORT_FGCOLOUR:
1386 case SKIN_TOKEN_VIEWPORT_BGCOLOUR:
1387 function = parse_viewportcolour;
1388 break;
1389 case SKIN_TOKEN_IMAGE_BACKDROP:
1390 function = parse_image_special;
1391 break;
1392 #endif
1393 case SKIN_TOKEN_TRANSLATEDSTRING:
1394 case SKIN_TOKEN_SETTING:
1395 function = parse_setting_and_lang;
1396 break;
1397 #ifdef HAVE_LCD_BITMAP
1398 case SKIN_TOKEN_VIEWPORT_CUSTOMLIST:
1399 function = parse_playlistview;
1400 break;
1401 case SKIN_TOKEN_LOAD_FONT:
1402 function = parse_font_load;
1403 break;
1404 case SKIN_TOKEN_VIEWPORT_ENABLE:
1405 case SKIN_TOKEN_UIVIEWPORT_ENABLE:
1406 token->value.data = element->params[0].data.text;
1407 break;
1408 case SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY:
1409 function = parse_image_display;
1410 break;
1411 case SKIN_TOKEN_IMAGE_PRELOAD:
1412 case SKIN_TOKEN_IMAGE_DISPLAY:
1413 function = parse_image_load;
1414 break;
1415 #endif
1416 #ifdef HAVE_TOUCHSCREEN
1417 case SKIN_TOKEN_TOUCHREGION:
1418 function = parse_touchregion;
1419 break;
1420 #endif
1421 #ifdef HAVE_ALBUMART
1422 case SKIN_TOKEN_ALBUMART_DISPLAY:
1423 if (wps_data->albumart)
1424 wps_data->albumart->vp = &curr_vp->vp;
1425 break;
1426 case SKIN_TOKEN_ALBUMART_LOAD:
1427 function = parse_albumart_load;
1428 break;
1429 #endif
1430 default:
1431 break;
1433 if (function)
1435 if (function(element, token, wps_data) < 0)
1436 return CALLBACK_ERROR;
1438 /* tags that start with 'F', 'I' or 'D' are for the next file */
1439 if ( *(element->tag->name) == 'I' || *(element->tag->name) == 'F' ||
1440 *(element->tag->name) == 'D')
1441 token->next = true;
1442 if (follow_lang_direction > 0 )
1443 follow_lang_direction--;
1444 break;
1446 case VIEWPORT:
1447 return convert_viewport(wps_data, element);
1448 case LINE:
1450 struct line *line =
1451 (struct line *)skin_buffer_alloc(sizeof(struct line));
1452 line->update_mode = SKIN_REFRESH_STATIC;
1453 line->timeout = DEFAULT_SUBLINE_TIME_MULTIPLIER * TIMEOUT_UNIT;
1454 curr_line = line;
1455 element->data = line;
1457 break;
1458 case LINE_ALTERNATOR:
1460 struct line_alternator *alternator =
1461 (struct line_alternator *)skin_buffer_alloc(sizeof(struct line_alternator));
1462 alternator->current_line = 0;
1463 #ifndef __PCTOOL__
1464 alternator->last_change_tick = current_tick;
1465 #endif
1466 element->data = alternator;
1468 break;
1469 case CONDITIONAL:
1471 struct conditional *conditional =
1472 (struct conditional *)skin_buffer_alloc(sizeof(struct conditional));
1473 conditional->last_value = -1;
1474 conditional->token = element->data;
1475 element->data = conditional;
1476 if (!check_feature_tag(element->tag->type))
1478 return FEATURE_NOT_AVAILABLE;
1480 return CALLBACK_OK;
1482 case TEXT:
1483 curr_line->update_mode |= SKIN_REFRESH_STATIC;
1484 break;
1485 default:
1486 break;
1488 return CALLBACK_OK;
1491 /* to setup up the wps-data from a format-buffer (isfile = false)
1492 from a (wps-)file (isfile = true)*/
1493 bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
1494 const char *buf, bool isfile)
1496 char *wps_buffer = NULL;
1497 if (!wps_data || !buf)
1498 return false;
1499 #ifdef HAVE_ALBUMART
1500 int status;
1501 struct mp3entry *curtrack;
1502 long offset;
1503 struct skin_albumart old_aa = {.state = WPS_ALBUMART_NONE};
1504 if (wps_data->albumart)
1506 old_aa.state = wps_data->albumart->state;
1507 old_aa.height = wps_data->albumart->height;
1508 old_aa.width = wps_data->albumart->width;
1510 #endif
1511 #ifdef HAVE_LCD_BITMAP
1512 int i;
1513 for (i=0;i<MAXUSERFONTS;i++)
1515 skinfonts[i].id = -1;
1516 skinfonts[i].name = NULL;
1518 #endif
1519 #ifdef DEBUG_SKIN_ENGINE
1520 if (isfile && debug_wps)
1522 DEBUGF("\n=====================\nLoading '%s'\n=====================\n", buf);
1524 #endif
1527 skin_data_reset(wps_data);
1528 wps_data->wps_loaded = false;
1529 curr_screen = screen;
1530 curr_line = NULL;
1531 curr_vp = NULL;
1532 curr_viewport_element = NULL;
1534 if (isfile)
1536 int fd = open_utf8(buf, O_RDONLY);
1538 if (fd < 0)
1539 return false;
1541 /* get buffer space from the plugin buffer */
1542 size_t buffersize = 0;
1543 wps_buffer = (char *)plugin_get_buffer(&buffersize);
1545 if (!wps_buffer)
1546 return false;
1548 /* copy the file's content to the buffer for parsing,
1549 ensuring that every line ends with a newline char. */
1550 unsigned int start = 0;
1551 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
1553 start += strlen(wps_buffer + start);
1554 if (start < buffersize - 1)
1556 wps_buffer[start++] = '\n';
1557 wps_buffer[start] = 0;
1560 close(fd);
1561 if (start <= 0)
1562 return false;
1564 else
1566 wps_buffer = (char*)buf;
1568 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1569 wps_data->backdrop = "-";
1570 wps_data->backdrop_id = -1;
1571 #endif
1572 /* parse the skin source */
1573 skin_buffer_save_position();
1574 wps_data->tree = skin_parse(wps_buffer, skin_element_callback, wps_data);
1575 if (!wps_data->tree) {
1576 skin_data_reset(wps_data);
1577 skin_buffer_restore_position();
1578 return false;
1581 #ifdef HAVE_LCD_BITMAP
1582 char bmpdir[MAX_PATH];
1583 if (isfile)
1585 /* get the bitmap dir */
1586 char *dot = strrchr(buf, '.');
1587 strlcpy(bmpdir, buf, dot - buf + 1);
1589 else
1590 { /* fall back to backdrop dir for built-in themes */
1591 /* no get_user_file_path(), assuming we ship bmps for built-in themes */
1592 snprintf(bmpdir, MAX_PATH, "%s", BACKDROP_DIR);
1594 /* load the bitmaps that were found by the parsing */
1595 if (!load_skin_bitmaps(wps_data, bmpdir) ||
1596 !skin_load_fonts(wps_data))
1598 skin_data_reset(wps_data);
1599 skin_buffer_restore_position();
1600 return false;
1602 #endif
1603 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
1604 status = audio_status();
1605 if (status & AUDIO_STATUS_PLAY)
1607 struct skin_albumart *aa = wps_data->albumart;
1608 if (aa && ((aa->state && !old_aa.state) ||
1609 (aa->state &&
1610 (((old_aa.height != aa->height) ||
1611 (old_aa.width != aa->width))))))
1613 curtrack = audio_current_track();
1614 offset = curtrack->offset;
1615 audio_stop();
1616 if (!(status & AUDIO_STATUS_PAUSE))
1617 audio_play(offset);
1620 #endif
1621 wps_data->wps_loaded = true;
1622 #ifdef DEBUG_SKIN_ENGINE
1623 // if (isfile && debug_wps)
1624 // debug_skin_usage();
1625 #endif
1626 return true;