Allow viewport labels to be mostly free text instead of only one character. If this...
[kugel-rb.git] / apps / gui / skin_engine / skin_parser.c
bloba3cb68915ba4223fe3c0171001a295079fbee4b5
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(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 (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(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 *text = element->params[0].data.text;
231 char label = text[0];
232 char sublabel = text[1];
233 int subimage;
234 struct gui_img *img;
236 /* sanity check */
237 img = find_image(label, wps_data);
238 if (!img)
240 token->value.i = label; /* so debug works */
241 return WPS_ERROR_INVALID_PARAM;
244 if ((subimage = get_image_id(sublabel)) != -1)
246 if (subimage >= img->num_subimages)
247 return WPS_ERROR_INVALID_PARAM;
249 /* Store sub-image number to display in high bits */
250 token->value.i = label | (subimage << 8);
251 return 4; /* We have consumed 2 bytes */
252 } else {
253 token->value.i = label;
254 return 3; /* We have consumed 1 byte */
258 static int parse_image_load(struct skin_element *element,
259 struct wps_token *token,
260 struct wps_data *wps_data)
262 const char* filename;
263 const char* id;
264 int x,y;
265 struct gui_img *img;
267 /* format: %x(n,filename.bmp,x,y)
268 or %xl(n,filename.bmp,x,y)
269 or %xl(n,filename.bmp,x,y,num_subimages)
272 id = element->params[0].data.text;
273 filename = element->params[1].data.text;
274 x = element->params[2].data.number;
275 y = element->params[3].data.number;
277 /* check the image number and load state */
278 if(find_image(*id, wps_data))
280 /* Invalid image ID */
281 return WPS_ERROR_INVALID_PARAM;
283 img = (struct gui_img*)skin_buffer_alloc(sizeof(struct gui_img));
284 if (!img)
285 return WPS_ERROR_INVALID_PARAM;
286 /* save a pointer to the filename */
287 img->bm.data = (char*)filename;
288 img->label = *id;
289 img->x = x;
290 img->y = y;
291 img->num_subimages = 1;
292 img->always_display = false;
293 // img->just_drawn = false;
294 img->display = -1;
296 /* save current viewport */
297 img->vp = &curr_vp->vp;
299 if (token->type == SKIN_TOKEN_IMAGE_DISPLAY)
301 img->always_display = true;
303 else if (element->params_count == 5)
305 img->num_subimages = element->params[4].data.number;
306 if (img->num_subimages <= 0)
307 return WPS_ERROR_INVALID_PARAM;
309 struct skin_token_list *item =
310 (struct skin_token_list *)new_skin_token_list_item(NULL, img);
311 if (!item)
312 return WPS_ERROR_INVALID_PARAM;
313 add_to_ll_chain(&wps_data->images, item);
315 return 0;
317 struct skin_font {
318 int id; /* the id from font_load */
319 char *name; /* filename without path and extension */
321 static struct skin_font skinfonts[MAXUSERFONTS];
322 static int parse_font_load(struct skin_element *element,
323 struct wps_token *token,
324 struct wps_data *wps_data)
326 (void)wps_data; (void)token;
327 int id = element->params[0].data.number;
328 char *filename = element->params[1].data.text;
329 char *ptr;
331 #if defined(DEBUG) || defined(SIMULATOR)
332 if (skinfonts[id-FONT_FIRSTUSERFONT].name != NULL)
334 DEBUGF("font id %d already being used\n", id);
336 #endif
337 /* make sure the filename contains .fnt,
338 * we dont actually use it, but require it anyway */
339 ptr = strchr(filename, '.');
340 if (!ptr || strncmp(ptr, ".fnt", 4))
341 return WPS_ERROR_INVALID_PARAM;
342 skinfonts[id-FONT_FIRSTUSERFONT].id = -1;
343 skinfonts[id-FONT_FIRSTUSERFONT].name = filename;
345 return 0;
349 #ifdef HAVE_LCD_BITMAP
351 static int parse_playlistview(struct skin_element *element,
352 struct wps_token *token,
353 struct wps_data *wps_data)
355 (void)wps_data;
356 struct playlistviewer *viewer =
357 (struct playlistviewer *)skin_buffer_alloc(sizeof(struct playlistviewer));
358 if (!viewer)
359 return WPS_ERROR_INVALID_PARAM;
360 viewer->vp = &curr_vp->vp;
361 viewer->show_icons = true;
362 viewer->start_offset = element->params[0].data.number;
363 viewer->lines[0] = element->params[1].data.code;
364 viewer->lines[1] = element->params[2].data.code;
366 token->value.data = (void*)viewer;
368 return 0;
370 #endif
372 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
374 static int parse_viewportcolour(struct skin_element *element,
375 struct wps_token *token,
376 struct wps_data *wps_data)
378 (void)wps_data;
379 struct skin_tag_parameter *param = element->params;
380 struct viewport_colour *colour =
381 (struct viewport_colour *)skin_buffer_alloc(sizeof(struct viewport_colour));
382 if (!colour)
383 return -1;
384 if (isdefault(param))
386 colour->colour = get_viewport_default_colour(curr_screen,
387 token->type == SKIN_TOKEN_VIEWPORT_FGCOLOUR);
389 else
391 if (!parse_color(param->data.text, &colour->colour))
392 return -1;
394 colour->vp = &curr_vp->vp;
395 token->value.data = colour;
396 if (element->line == curr_viewport_element->line)
398 if (token->type == SKIN_TOKEN_VIEWPORT_FGCOLOUR)
400 curr_vp->start_fgcolour = colour->colour;
401 curr_vp->vp.fg_pattern = colour->colour;
403 else
405 curr_vp->start_bgcolour = colour->colour;
406 curr_vp->vp.bg_pattern = colour->colour;
409 return 0;
412 static int parse_image_special(struct skin_element *element,
413 struct wps_token *token,
414 struct wps_data *wps_data)
416 (void)wps_data; /* kill warning */
417 (void)token;
418 bool error = false;
420 #if LCD_DEPTH > 1
421 if (token->type == SKIN_TOKEN_IMAGE_BACKDROP)
423 char *filename = element->params[0].data.text;
424 /* format: %X|filename.bmp| or %Xd */
425 if (!strcmp(filename, "d"))
427 wps_data->backdrop = NULL;
428 return 0;
430 else if (!error)
432 wps_data->backdrop = filename;
435 #endif
436 /* Skip the rest of the line */
437 return error ? WPS_ERROR_INVALID_PARAM : 0;
439 #endif
441 #endif /* HAVE_LCD_BITMAP */
443 static int parse_setting_and_lang(struct skin_element *element,
444 struct wps_token *token,
445 struct wps_data *wps_data)
447 /* NOTE: both the string validations that happen in here will
448 * automatically PASS on checkwps because its too hard to get
449 * settings_list.c and english.lang built for it.
450 * If that ever changes remove the #ifndef __PCTOOL__'s here
452 (void)wps_data;
453 char *temp = element->params[0].data.text;
454 int i;
456 if (token->type == SKIN_TOKEN_TRANSLATEDSTRING)
458 #ifndef __PCTOOL__
459 i = lang_english_to_id(temp);
460 if (i < 0)
461 return WPS_ERROR_INVALID_PARAM;
462 #endif
464 else
466 /* Find the setting */
467 for (i=0; i<nb_settings; i++)
468 if (settings[i].cfg_name &&
469 !strcmp(settings[i].cfg_name, temp))
470 break;
471 #ifndef __PCTOOL__
472 if (i == nb_settings)
473 return WPS_ERROR_INVALID_PARAM;
474 #endif
476 /* Store the setting number */
477 token->value.i = i;
478 return 0;
481 static int parse_timeout_tag(struct skin_element *element,
482 struct wps_token *token,
483 struct wps_data *wps_data)
485 (void)wps_data;
486 int val = 0;
487 if (element->params_count == 0)
489 switch (token->type)
491 case SKIN_TOKEN_SUBLINE_TIMEOUT:
492 return -1;
493 case SKIN_TOKEN_BUTTON_VOLUME:
494 case SKIN_TOKEN_TRACK_STARTING:
495 case SKIN_TOKEN_TRACK_ENDING:
496 case SKIN_TOKEN_LASTTOUCH:
497 val = 10;
498 break;
499 default:
500 break;
503 else
504 val = element->params[0].data.number;
505 token->value.i = val * TIMEOUT_UNIT;
506 return 0;
509 static int parse_progressbar_tag(struct skin_element* element,
510 struct wps_token *token,
511 struct wps_data *wps_data)
513 #ifdef HAVE_LCD_BITMAP
514 struct progressbar *pb;
515 struct skin_token_list *item;
516 struct viewport *vp = &curr_vp->vp;
517 struct skin_tag_parameter *param = element->params;
519 if (element->params_count == 0 &&
520 element->tag->type != SKIN_TOKEN_PROGRESSBAR)
521 return 0; /* nothing to do */
522 pb = (struct progressbar*)skin_buffer_alloc(sizeof(struct progressbar));
524 token->value.data = pb;
526 if (!pb)
527 return WPS_ERROR_INVALID_PARAM;
528 pb->vp = vp;
529 pb->have_bitmap_pb = false;
530 pb->bm.data = NULL; /* no bitmap specified */
531 pb->follow_lang_direction = follow_lang_direction > 0;
533 if (element->params_count == 0)
535 pb->x = 0;
536 pb->width = vp->width;
537 pb->height = SYSFONT_HEIGHT-2;
538 pb->y = -1; /* Will be computed during the rendering */
539 pb->type = element->tag->type;
540 return 0;
543 item = new_skin_token_list_item(token, pb);
544 if (!item)
545 return -1;
546 add_to_ll_chain(&wps_data->progressbars, item);
548 /* (x,y,width,height,filename) */
549 if (!isdefault(param))
550 pb->x = param->data.number;
551 else
552 pb->x = vp->x;
553 param++;
555 if (!isdefault(param))
556 pb->y = param->data.number;
557 else
558 pb->y = -1; /* computed at rendering */
559 param++;
561 if (!isdefault(param))
562 pb->width = param->data.number;
563 else
564 pb->width = vp->width - pb->x;
565 param++;
567 if (!isdefault(param))
569 /* A zero height makes no sense - reject it */
570 if (param->data.number == 0)
571 return WPS_ERROR_INVALID_PARAM;
573 pb->height = param->data.number;
575 else
577 if (vp->font > FONT_UI)
578 pb->height = -1; /* calculate at display time */
579 else
581 #ifndef __PCTOOL__
582 pb->height = font_get(vp->font)->height;
583 #else
584 pb->height = 8;
585 #endif
588 param++;
589 if (!isdefault(param))
590 pb->bm.data = param->data.text;
593 if (token->type == SKIN_TOKEN_VOLUME)
594 token->type = SKIN_TOKEN_VOLUMEBAR;
595 else if (token->type == SKIN_TOKEN_BATTERY_PERCENT)
596 token->type = SKIN_TOKEN_BATTERY_PERCENTBAR;
597 pb->type = token->type;
599 return 0;
601 #else
602 (void)element;
603 if (token->type == SKIN_TOKEN_PROGRESSBAR ||
604 token->type == SKIN_TOKEN_PLAYER_PROGRESSBAR)
606 wps_data->full_line_progressbar =
607 token->type == SKIN_TOKEN_PLAYER_PROGRESSBAR;
609 return 0;
611 #endif
614 #ifdef HAVE_ALBUMART
615 static int parse_albumart_load(struct skin_element* element,
616 struct wps_token *token,
617 struct wps_data *wps_data)
619 struct dim dimensions;
620 int albumart_slot;
621 bool swap_for_rtl = lang_is_rtl() && follow_lang_direction;
622 struct skin_albumart *aa =
623 (struct skin_albumart *)skin_buffer_alloc(sizeof(struct skin_albumart));
624 (void)token; /* silence warning */
625 if (!aa)
626 return -1;
628 /* reset albumart info in wps */
629 aa->width = -1;
630 aa->height = -1;
631 aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
632 aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
634 aa->x = element->params[0].data.number;
635 aa->y = element->params[1].data.number;
636 aa->width = element->params[2].data.number;
637 aa->height = element->params[3].data.number;
639 aa->vp = &curr_vp->vp;
640 aa->draw_handle = -1;
642 /* if we got here, we parsed everything ok .. ! */
643 if (aa->width < 0)
644 aa->width = 0;
645 else if (aa->width > LCD_WIDTH)
646 aa->width = LCD_WIDTH;
648 if (aa->height < 0)
649 aa->height = 0;
650 else if (aa->height > LCD_HEIGHT)
651 aa->height = LCD_HEIGHT;
653 if (swap_for_rtl)
654 aa->x = LCD_WIDTH - (aa->x + aa->width);
656 aa->state = WPS_ALBUMART_LOAD;
657 wps_data->albumart = aa;
659 dimensions.width = aa->width;
660 dimensions.height = aa->height;
662 albumart_slot = playback_claim_aa_slot(&dimensions);
664 if (0 <= albumart_slot)
665 wps_data->playback_aa_slot = albumart_slot;
667 if (element->params_count > 4 && !isdefault(&element->params[4]))
669 switch (*element->params[4].data.text)
671 case 'l':
672 case 'L':
673 if (swap_for_rtl)
674 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
675 else
676 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
677 break;
678 case 'c':
679 case 'C':
680 aa->xalign = WPS_ALBUMART_ALIGN_CENTER;
681 break;
682 case 'r':
683 case 'R':
684 if (swap_for_rtl)
685 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
686 else
687 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
688 break;
691 if (element->params_count > 5 && !isdefault(&element->params[5]))
693 switch (*element->params[5].data.text)
695 case 't':
696 case 'T':
697 aa->yalign = WPS_ALBUMART_ALIGN_TOP;
698 break;
699 case 'c':
700 case 'C':
701 aa->yalign = WPS_ALBUMART_ALIGN_CENTER;
702 break;
703 case 'b':
704 case 'B':
705 aa->yalign = WPS_ALBUMART_ALIGN_BOTTOM;
706 break;
709 return 0;
712 #endif /* HAVE_ALBUMART */
714 #ifdef HAVE_TOUCHSCREEN
716 struct touchaction {const char* s; int action;};
717 static const struct touchaction touchactions[] = {
718 /* generic actions, convert to screen actions on use */
719 {"prev", ACTION_STD_PREV }, {"next", ACTION_STD_NEXT },
720 {"rwd", ACTION_STD_PREVREPEAT }, {"ffwd", ACTION_STD_NEXTREPEAT },
721 {"hotkey", ACTION_STD_HOTKEY}, {"select", ACTION_STD_OK },
722 {"menu", ACTION_STD_MENU }, {"cancel", ACTION_STD_CANCEL },
723 {"contextmenu", ACTION_STD_CONTEXT},{"quickscreen", ACTION_STD_QUICKSCREEN },
724 /* not really WPS specific, but no equivilant ACTION_STD_* */
725 {"voldown", ACTION_WPS_VOLDOWN}, {"volup", ACTION_WPS_VOLUP},
727 /* WPS specific actions */
728 {"browse", ACTION_WPS_BROWSE },
729 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
730 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
731 {"pitch", ACTION_WPS_PITCHSCREEN}, {"playlist", ACTION_WPS_VIEW_PLAYLIST },
733 #if CONFIG_TUNER
734 /* FM screen actions */
735 /* Also allow browse, play, stop from WPS codes */
736 {"mode", ACTION_FM_MODE }, {"record", ACTION_FM_RECORD },
737 {"presets", ACTION_FM_PRESET},
738 #endif
741 static int parse_touchregion(struct skin_element *element,
742 struct wps_token *token,
743 struct wps_data *wps_data)
745 (void)token;
746 unsigned i, imax;
747 struct touchregion *region = NULL;
748 const char *action;
749 const char pb_string[] = "progressbar";
750 const char vol_string[] = "volume";
751 char temp[20];
753 /* format: %T(x,y,width,height,action)
754 * if action starts with & the area must be held to happen
755 * action is one of:
756 * play - play/pause playback
757 * stop - stop playback, exit the wps
758 * prev - prev track
759 * next - next track
760 * ffwd - seek forward
761 * rwd - seek backwards
762 * menu - go back to the main menu
763 * browse - go back to the file/db browser
764 * shuffle - toggle shuffle mode
765 * repmode - cycle the repeat mode
766 * quickscreen - go into the quickscreen
767 * contextmenu - open the context menu
768 * playlist - go into the playlist
769 * pitch - go into the pitchscreen
770 * volup - increase volume by one step
771 * voldown - decrease volume by one step
775 region = (struct touchregion*)skin_buffer_alloc(sizeof(struct touchregion));
776 if (!region)
777 return WPS_ERROR_INVALID_PARAM;
779 /* should probably do some bounds checking here with the viewport... but later */
780 region->action = ACTION_NONE;
781 region->x = element->params[0].data.number;
782 region->y = element->params[1].data.number;
783 region->width = element->params[2].data.number;
784 region->height = element->params[3].data.number;
785 region->wvp = curr_vp;
786 region->armed = false;
787 region->reverse_bar = false;
788 action = element->params[4].data.text;
790 strcpy(temp, action);
791 action = temp;
793 if (*action == '!')
795 region->reverse_bar = true;
796 action++;
799 if(!strcmp(pb_string, action))
800 region->type = WPS_TOUCHREGION_SCROLLBAR;
801 else if(!strcmp(vol_string, action))
802 region->type = WPS_TOUCHREGION_VOLUME;
803 else
805 region->type = WPS_TOUCHREGION_ACTION;
807 if (*action == '&')
809 action++;
810 region->repeat = true;
812 else
813 region->repeat = false;
815 imax = ARRAYLEN(touchactions);
816 for (i = 0; i < imax; i++)
818 /* try to match with one of our touchregion screens */
819 if (!strcmp(touchactions[i].s, action))
821 region->action = touchactions[i].action;
822 break;
825 if (region->action == ACTION_NONE)
826 return WPS_ERROR_INVALID_PARAM;
828 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
829 if (!item)
830 return WPS_ERROR_INVALID_PARAM;
831 add_to_ll_chain(&wps_data->touchregions, item);
832 return 0;
834 #endif
836 static bool check_feature_tag(const int type)
838 switch (type)
840 case SKIN_TOKEN_RTC_PRESENT:
841 #if CONFIG_RTC
842 return true;
843 #else
844 return false;
845 #endif
846 case SKIN_TOKEN_HAVE_RECORDING:
847 #ifdef HAVE_RECORDING
848 return true;
849 #else
850 return false;
851 #endif
852 case SKIN_TOKEN_HAVE_TUNER:
853 #if CONFIG_TUNER
854 if (radio_hardware_present())
855 return true;
856 #endif
857 return false;
859 #if CONFIG_TUNER
860 case SKIN_TOKEN_HAVE_RDS:
861 #ifdef HAVE_RDS_CAP
862 return true;
863 #else
864 return false;
865 #endif /* HAVE_RDS_CAP */
866 #endif /* CONFIG_TUNER */
867 default: /* not a tag we care about, just don't skip */
868 return true;
873 * initial setup of wps_data; does reset everything
874 * except fields which need to survive, i.e.
877 static void skin_data_reset(struct wps_data *wps_data)
879 wps_data->tree = NULL;
880 #ifdef HAVE_LCD_BITMAP
881 wps_data->images = NULL;
882 wps_data->progressbars = NULL;
883 #endif
884 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
885 wps_data->backdrop = NULL;
886 #endif
887 #ifdef HAVE_TOUCHSCREEN
888 wps_data->touchregions = NULL;
889 #endif
890 #ifdef HAVE_ALBUMART
891 wps_data->albumart = NULL;
892 if (wps_data->playback_aa_slot >= 0)
894 playback_release_aa_slot(wps_data->playback_aa_slot);
895 wps_data->playback_aa_slot = -1;
897 #endif
899 #ifdef HAVE_LCD_BITMAP
900 wps_data->peak_meter_enabled = false;
901 wps_data->wps_sb_tag = false;
902 wps_data->show_sb_on_wps = false;
903 #else /* HAVE_LCD_CHARCELLS */
904 /* progress bars */
905 int i;
906 for (i = 0; i < 8; i++)
908 wps_data->wps_progress_pat[i] = 0;
910 wps_data->full_line_progressbar = false;
911 #endif
912 wps_data->wps_loaded = false;
915 #ifdef HAVE_LCD_BITMAP
916 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
918 (void)wps_data; /* only needed for remote targets */
919 char img_path[MAX_PATH];
920 int fd;
921 get_image_filename(bitmap->data, bmpdir,
922 img_path, sizeof(img_path));
924 /* load the image */
925 int format;
926 #ifdef HAVE_REMOTE_LCD
927 if (curr_screen == SCREEN_REMOTE)
928 format = FORMAT_ANY|FORMAT_REMOTE;
929 else
930 #endif
931 format = FORMAT_ANY|FORMAT_TRANSPARENT;
933 fd = open(img_path, O_RDONLY);
934 if (fd < 0)
935 return false;
936 size_t buf_size = read_bmp_file(img_path, bitmap, 0,
937 format|FORMAT_RETURN_SIZE, NULL);
938 char* imgbuf = (char*)skin_buffer_alloc(buf_size);
939 if (!imgbuf)
941 close(fd);
942 return NULL;
944 lseek(fd, 0, SEEK_SET);
945 bitmap->data = imgbuf;
946 int ret = read_bmp_fd(fd, bitmap, buf_size, format, NULL);
948 close(fd);
949 if (ret > 0)
951 return true;
953 else
955 /* Abort if we can't load an image */
956 DEBUGF("Couldn't load '%s'\n", img_path);
957 return false;
961 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
963 struct skin_token_list *list;
964 bool retval = true; /* return false if a single image failed to load */
965 /* do the progressbars */
966 list = wps_data->progressbars;
967 while (list)
969 struct progressbar *pb = (struct progressbar*)list->token->value.data;
970 if (pb->bm.data)
972 pb->have_bitmap_pb = load_skin_bmp(wps_data, &pb->bm, bmpdir);
973 if (!pb->have_bitmap_pb) /* no success */
974 retval = false;
976 list = list->next;
978 /* regular images */
979 list = wps_data->images;
980 while (list)
982 struct gui_img *img = (struct gui_img*)list->token->value.data;
983 if (img->bm.data)
985 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
986 if (img->loaded)
987 img->subimage_height = img->bm.height / img->num_subimages;
988 else
989 retval = false;
991 list = list->next;
994 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
995 /* Backdrop load scheme:
996 * 1) %X|filename|
997 * 2) load the backdrop from settings
999 if (wps_data->backdrop)
1001 if (screens[curr_screen].depth == 1)
1003 wps_data->backdrop = NULL;
1004 return retval;
1006 bool needed = wps_data->backdrop[0] != '-';
1007 wps_data->backdrop = skin_backdrop_load(wps_data->backdrop,
1008 bmpdir, curr_screen);
1009 if (!wps_data->backdrop && needed)
1010 retval = false;
1012 #endif /* has backdrop support */
1014 return retval;
1017 static bool skin_load_fonts(struct wps_data *data)
1019 /* don't spit out after the first failue to aid debugging */
1020 bool success = true;
1021 struct skin_element *vp_list;
1022 int font_id;
1023 /* walk though each viewport and assign its font */
1024 for(vp_list = data->tree; vp_list; vp_list = vp_list->next)
1026 /* first, find the viewports that have a non-sys/ui-font font */
1027 struct skin_viewport *skin_vp =
1028 (struct skin_viewport*)vp_list->data;
1029 struct viewport *vp = &skin_vp->vp;
1032 if (vp->font <= FONT_UI)
1033 { /* the usual case -> built-in fonts */
1034 #ifdef HAVE_REMOTE_LCD
1035 if (vp->font == FONT_UI)
1036 vp->font += curr_screen;
1037 #endif
1038 continue;
1040 font_id = vp->font;
1042 /* now find the corresponding skin_font */
1043 struct skin_font *font = &skinfonts[font_id-FONT_FIRSTUSERFONT];
1044 if (!font->name)
1046 if (success)
1048 DEBUGF("font %d not specified\n", font_id);
1050 success = false;
1051 continue;
1054 /* load the font - will handle loading the same font again if
1055 * multiple viewports use the same */
1056 if (font->id < 0)
1058 char *dot = strchr(font->name, '.');
1059 *dot = '\0';
1060 font->id = skin_font_load(font->name);
1063 if (font->id < 0)
1065 DEBUGF("Unable to load font %d: '%s.fnt'\n",
1066 font_id, font->name);
1067 font->name = NULL; /* to stop trying to load it again if we fail */
1068 success = false;
1069 font->name = NULL;
1070 continue;
1073 /* finally, assign the font_id to the viewport */
1074 vp->font = font->id;
1076 return success;
1079 #endif /* HAVE_LCD_BITMAP */
1080 static int convert_viewport(struct wps_data *data, struct skin_element* element)
1082 struct skin_viewport *skin_vp =
1083 (struct skin_viewport *)skin_buffer_alloc(sizeof(struct skin_viewport));
1084 struct screen *display = &screens[curr_screen];
1086 if (!skin_vp)
1087 return CALLBACK_ERROR;
1089 skin_vp->hidden_flags = 0;
1090 skin_vp->label = NULL;
1091 skin_vp->is_infovp = false;
1092 element->data = skin_vp;
1093 curr_vp = skin_vp;
1094 curr_viewport_element = element;
1096 viewport_set_defaults(&skin_vp->vp, curr_screen);
1097 #ifdef HAVE_REMOTE_LCD
1098 /* viewport_set_defaults() sets the font to FONT_UI+curr_screen.
1099 * This parser requires font 1 to always be the UI font,
1100 * so force it back to FONT_UI and handle the screen number at the end */
1101 skin_vp->vp.font = FONT_UI;
1102 #endif
1104 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1105 skin_vp->start_fgcolour = skin_vp->vp.fg_pattern;
1106 skin_vp->start_bgcolour = skin_vp->vp.bg_pattern;
1107 #endif
1110 struct skin_tag_parameter *param = element->params;
1111 if (element->params_count == 0) /* default viewport */
1113 if (!data->tree) /* first viewport in the skin */
1114 data->tree = element;
1115 skin_vp->label = VP_DEFAULT_LABEL;
1116 return CALLBACK_OK;
1119 if (element->params_count == 6)
1121 if (element->tag->type == SKIN_TOKEN_UIVIEWPORT_LOAD)
1123 skin_vp->is_infovp = true;
1124 if (isdefault(param))
1126 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
1127 skin_vp->label = VP_DEFAULT_LABEL;
1129 else
1131 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
1132 skin_vp->label = param->data.text;
1135 else
1137 skin_vp->hidden_flags = VP_DRAW_HIDEABLE|VP_DRAW_HIDDEN;
1138 skin_vp->label = param->data.text;
1140 param++;
1142 /* x */
1143 if (!isdefault(param))
1145 skin_vp->vp.x = param->data.number;
1146 if (param->data.number < 0)
1147 skin_vp->vp.x += display->lcdwidth;
1149 param++;
1150 /* y */
1151 if (!isdefault(param))
1153 skin_vp->vp.y = param->data.number;
1154 if (param->data.number < 0)
1155 skin_vp->vp.y += display->lcdheight;
1157 param++;
1158 /* width */
1159 if (!isdefault(param))
1161 skin_vp->vp.width = param->data.number;
1162 if (param->data.number < 0)
1163 skin_vp->vp.width = (skin_vp->vp.width + display->lcdwidth) - skin_vp->vp.x;
1165 else
1167 skin_vp->vp.width = display->lcdwidth - skin_vp->vp.x;
1169 param++;
1170 /* height */
1171 if (!isdefault(param))
1173 skin_vp->vp.height = param->data.number;
1174 if (param->data.number < 0)
1175 skin_vp->vp.height = (skin_vp->vp.height + display->lcdheight) - skin_vp->vp.y;
1177 else
1179 skin_vp->vp.height = display->lcdheight - skin_vp->vp.y;
1181 param++;
1182 #ifdef HAVE_LCD_BITMAP
1183 /* font */
1184 if (!isdefault(param))
1186 skin_vp->vp.font = param->data.number;
1188 #endif
1190 return CALLBACK_OK;
1194 static int skin_element_callback(struct skin_element* element, void* data)
1196 struct wps_data *wps_data = (struct wps_data *)data;
1197 struct wps_token *token;
1198 parse_function function = NULL;
1200 switch (element->type)
1202 /* IMPORTANT: element params are shared, so copy them if needed
1203 * or use then NOW, dont presume they have a long lifespan
1205 case TAG:
1207 token = (struct wps_token*)skin_buffer_alloc(sizeof(struct wps_token));
1208 memset(token, 0, sizeof(*token));
1209 token->type = element->tag->type;
1211 if ((element->tag->flags&SKIN_REFRESH_ALL) == SKIN_RTC_REFRESH)
1213 #ifdef CONFIG_RTC
1214 curr_line->update_mode |= SKIN_REFRESH_DYNAMIC;
1215 #else
1216 curr_line->update_mode |= SKIN_REFRESH_STATIC;
1217 #endif
1219 else
1220 curr_line->update_mode |= element->tag->flags&SKIN_REFRESH_ALL;
1222 element->data = token;
1224 /* Some tags need special handling for the tag, so add them here */
1225 switch (token->type)
1227 case SKIN_TOKEN_ALIGN_LANGDIRECTION:
1228 follow_lang_direction = 2;
1229 break;
1230 case SKIN_TOKEN_PROGRESSBAR:
1231 case SKIN_TOKEN_VOLUME:
1232 case SKIN_TOKEN_BATTERY_PERCENT:
1233 case SKIN_TOKEN_PLAYER_PROGRESSBAR:
1234 function = parse_progressbar_tag;
1235 break;
1236 case SKIN_TOKEN_SUBLINE_TIMEOUT:
1237 case SKIN_TOKEN_BUTTON_VOLUME:
1238 case SKIN_TOKEN_TRACK_STARTING:
1239 case SKIN_TOKEN_TRACK_ENDING:
1240 case SKIN_TOKEN_LASTTOUCH:
1241 function = parse_timeout_tag;
1242 break;
1243 #ifdef HAVE_LCD_BITMAP
1244 case SKIN_TOKEN_DISABLE_THEME:
1245 case SKIN_TOKEN_ENABLE_THEME:
1246 case SKIN_TOKEN_DRAW_INBUILTBAR:
1247 function = parse_statusbar_tags;
1248 break;
1249 #endif
1250 case SKIN_TOKEN_FILE_DIRECTORY:
1251 token->value.i = element->params[0].data.number;
1252 break;
1253 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1254 case SKIN_TOKEN_VIEWPORT_FGCOLOUR:
1255 case SKIN_TOKEN_VIEWPORT_BGCOLOUR:
1256 function = parse_viewportcolour;
1257 break;
1258 case SKIN_TOKEN_IMAGE_BACKDROP:
1259 function = parse_image_special;
1260 break;
1261 #endif
1262 case SKIN_TOKEN_TRANSLATEDSTRING:
1263 case SKIN_TOKEN_SETTING:
1264 function = parse_setting_and_lang;
1265 break;
1266 #ifdef HAVE_LCD_BITMAP
1267 case SKIN_TOKEN_VIEWPORT_CUSTOMLIST:
1268 function = parse_playlistview;
1269 break;
1270 case SKIN_TOKEN_LOAD_FONT:
1271 function = parse_font_load;
1272 break;
1273 case SKIN_TOKEN_VIEWPORT_ENABLE:
1274 case SKIN_TOKEN_UIVIEWPORT_ENABLE:
1275 token->value.data = element->params[0].data.text;
1276 break;
1277 case SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY:
1278 function = parse_image_display;
1279 break;
1280 case SKIN_TOKEN_IMAGE_PRELOAD:
1281 case SKIN_TOKEN_IMAGE_DISPLAY:
1282 function = parse_image_load;
1283 break;
1284 #endif
1285 #ifdef HAVE_TOUCHSCREEN
1286 case SKIN_TOKEN_TOUCHREGION:
1287 function = parse_touchregion;
1288 break;
1289 #endif
1290 #ifdef HAVE_ALBUMART
1291 case SKIN_TOKEN_ALBUMART_DISPLAY:
1292 if (wps_data->albumart)
1293 wps_data->albumart->vp = &curr_vp->vp;
1294 break;
1295 case SKIN_TOKEN_ALBUMART_LOAD:
1296 function = parse_albumart_load;
1297 break;
1298 #endif
1299 default:
1300 break;
1302 if (function)
1304 if (function(element, token, wps_data) < 0)
1305 return CALLBACK_ERROR;
1307 /* tags that start with 'F', 'I' or 'D' are for the next file */
1308 if ( *(element->tag->name) == 'I' || *(element->tag->name) == 'F' ||
1309 *(element->tag->name) == 'D')
1310 token->next = true;
1311 if (follow_lang_direction > 0 )
1312 follow_lang_direction--;
1313 break;
1315 case VIEWPORT:
1316 return convert_viewport(wps_data, element);
1317 case LINE:
1319 struct line *line =
1320 (struct line *)skin_buffer_alloc(sizeof(struct line));
1321 line->update_mode = SKIN_REFRESH_STATIC;
1322 line->timeout = DEFAULT_SUBLINE_TIME_MULTIPLIER * TIMEOUT_UNIT;
1323 curr_line = line;
1324 element->data = line;
1326 break;
1327 case LINE_ALTERNATOR:
1329 struct line_alternator *alternator =
1330 (struct line_alternator *)skin_buffer_alloc(sizeof(struct line_alternator));
1331 alternator->current_line = 0;
1332 #ifndef __PCTOOL__
1333 alternator->last_change_tick = current_tick;
1334 #endif
1335 element->data = alternator;
1337 break;
1338 case CONDITIONAL:
1340 struct conditional *conditional =
1341 (struct conditional *)skin_buffer_alloc(sizeof(struct conditional));
1342 conditional->last_value = -1;
1343 conditional->token = element->data;
1344 element->data = conditional;
1345 if (!check_feature_tag(element->tag->type))
1347 return FEATURE_NOT_AVAILABLE;
1349 return CALLBACK_OK;
1351 case TEXT:
1352 curr_line->update_mode |= SKIN_REFRESH_STATIC;
1353 break;
1354 default:
1355 break;
1357 return CALLBACK_OK;
1360 /* to setup up the wps-data from a format-buffer (isfile = false)
1361 from a (wps-)file (isfile = true)*/
1362 bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
1363 const char *buf, bool isfile)
1365 char *wps_buffer = NULL;
1366 if (!wps_data || !buf)
1367 return false;
1368 #ifdef HAVE_ALBUMART
1369 int status;
1370 struct mp3entry *curtrack;
1371 long offset;
1372 struct skin_albumart old_aa = {.state = WPS_ALBUMART_NONE};
1373 if (wps_data->albumart)
1375 old_aa.state = wps_data->albumart->state;
1376 old_aa.height = wps_data->albumart->height;
1377 old_aa.width = wps_data->albumart->width;
1379 #endif
1380 #ifdef HAVE_LCD_BITMAP
1381 int i;
1382 for (i=0;i<MAXUSERFONTS;i++)
1384 skinfonts[i].id = -1;
1385 skinfonts[i].name = NULL;
1387 #endif
1388 #ifdef DEBUG_SKIN_ENGINE
1389 if (isfile && debug_wps)
1391 DEBUGF("\n=====================\nLoading '%s'\n=====================\n", buf);
1393 #endif
1396 skin_data_reset(wps_data);
1397 wps_data->wps_loaded = false;
1398 curr_screen = screen;
1399 curr_line = NULL;
1400 curr_vp = NULL;
1401 curr_viewport_element = NULL;
1403 if (isfile)
1405 int fd = open_utf8(buf, O_RDONLY);
1407 if (fd < 0)
1408 return false;
1410 /* get buffer space from the plugin buffer */
1411 size_t buffersize = 0;
1412 wps_buffer = (char *)plugin_get_buffer(&buffersize);
1414 if (!wps_buffer)
1415 return false;
1417 /* copy the file's content to the buffer for parsing,
1418 ensuring that every line ends with a newline char. */
1419 unsigned int start = 0;
1420 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
1422 start += strlen(wps_buffer + start);
1423 if (start < buffersize - 1)
1425 wps_buffer[start++] = '\n';
1426 wps_buffer[start] = 0;
1429 close(fd);
1430 if (start <= 0)
1431 return false;
1433 else
1435 wps_buffer = (char*)buf;
1437 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1438 wps_data->backdrop = "-";
1439 #endif
1440 /* parse the skin source */
1441 skin_buffer_save_position();
1442 wps_data->tree = skin_parse(wps_buffer, skin_element_callback, wps_data);
1443 if (!wps_data->tree) {
1444 skin_data_reset(wps_data);
1445 skin_buffer_restore_position();
1446 return false;
1449 #ifdef HAVE_LCD_BITMAP
1450 char bmpdir[MAX_PATH];
1451 if (isfile)
1453 /* get the bitmap dir */
1454 char *dot = strrchr(buf, '.');
1455 strlcpy(bmpdir, buf, dot - buf + 1);
1457 else
1458 { /* fall back to backdrop dir for built-in themes */
1459 /* no get_user_file_path(), assuming we ship bmps for built-in themes */
1460 snprintf(bmpdir, MAX_PATH, "%s", BACKDROP_DIR);
1462 /* load the bitmaps that were found by the parsing */
1463 if (!load_skin_bitmaps(wps_data, bmpdir) ||
1464 !skin_load_fonts(wps_data))
1466 skin_data_reset(wps_data);
1467 skin_buffer_restore_position();
1468 return false;
1470 #endif
1471 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
1472 status = audio_status();
1473 if (status & AUDIO_STATUS_PLAY)
1475 struct skin_albumart *aa = wps_data->albumart;
1476 if (aa && ((aa->state && !old_aa.state) ||
1477 (aa->state &&
1478 (((old_aa.height != aa->height) ||
1479 (old_aa.width != aa->width))))))
1481 curtrack = audio_current_track();
1482 offset = curtrack->offset;
1483 audio_stop();
1484 if (!(status & AUDIO_STATUS_PAUSE))
1485 audio_play(offset);
1488 #endif
1489 wps_data->wps_loaded = true;
1490 #ifdef DEBUG_SKIN_ENGINE
1491 // if (isfile && debug_wps)
1492 // debug_skin_usage();
1493 #endif
1494 return true;