Rockbox as an application: add get_user_file_path().
[kugel-rb.git] / apps / gui / skin_engine / skin_parser.c
blobd0194c669bed9177897fe539f6815ba1212d66df
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, 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 == label)
142 return vp;
143 list = list->next;
145 return NULL;
148 #ifdef HAVE_LCD_BITMAP
150 /* create and init a new wpsll item.
151 * passing NULL to token will alloc a new one.
152 * You should only pass NULL for the token when the token type (table above)
153 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
155 static struct skin_token_list *new_skin_token_list_item(struct wps_token *token,
156 void* token_data)
158 struct skin_token_list *llitem =
159 (struct skin_token_list *)skin_buffer_alloc(sizeof(struct skin_token_list));
160 if (!token)
161 token = (struct wps_token*)skin_buffer_alloc(sizeof(struct wps_token));
162 if (!llitem || !token)
163 return NULL;
164 llitem->next = NULL;
165 llitem->token = token;
166 if (token_data)
167 llitem->token->value.data = token_data;
168 return llitem;
171 static int parse_statusbar_tags(struct skin_element* element,
172 struct wps_token *token,
173 struct wps_data *wps_data)
175 (void)element;
176 if (token->type == SKIN_TOKEN_DRAW_INBUILTBAR)
178 token->value.data = (void*)&curr_vp->vp;
180 else
182 struct skin_element *def_vp = wps_data->tree;
183 struct skin_viewport *default_vp = def_vp->data;
184 if (def_vp->params_count == 0)
186 wps_data->wps_sb_tag = true;
187 wps_data->show_sb_on_wps = (token->type == SKIN_TOKEN_ENABLE_THEME);
189 if (wps_data->show_sb_on_wps)
191 viewport_set_defaults(&default_vp->vp, curr_screen);
193 else
195 viewport_set_fullscreen(&default_vp->vp, curr_screen);
197 #ifdef HAVE_REMOTE_LCD
198 /* viewport_set_defaults() sets the font to FONT_UI+curr_screen.
199 * This parser requires font 1 to always be the UI font,
200 * so force it back to FONT_UI and handle the screen number at the end */
201 default_vp->vp.font = FONT_UI;
202 #endif
204 return 0;
207 static int get_image_id(int c)
209 if(c >= 'a' && c <= 'z')
210 return c - 'a';
211 else if(c >= 'A' && c <= 'Z')
212 return c - 'A' + 26;
213 else
214 return -1;
217 char *get_image_filename(const char *start, const char* bmpdir,
218 char *buf, int buf_size)
220 snprintf(buf, buf_size, "%s/%s", bmpdir, start);
222 return buf;
225 static int parse_image_display(struct skin_element *element,
226 struct wps_token *token,
227 struct wps_data *wps_data)
229 char *text = element->params[0].data.text;
230 char label = text[0];
231 char sublabel = text[1];
232 int subimage;
233 struct gui_img *img;
235 /* sanity check */
236 img = find_image(label, wps_data);
237 if (!img)
239 token->value.i = label; /* so debug works */
240 return WPS_ERROR_INVALID_PARAM;
243 if ((subimage = get_image_id(sublabel)) != -1)
245 if (subimage >= img->num_subimages)
246 return WPS_ERROR_INVALID_PARAM;
248 /* Store sub-image number to display in high bits */
249 token->value.i = label | (subimage << 8);
250 return 4; /* We have consumed 2 bytes */
251 } else {
252 token->value.i = label;
253 return 3; /* We have consumed 1 byte */
257 static int parse_image_load(struct skin_element *element,
258 struct wps_token *token,
259 struct wps_data *wps_data)
261 const char* filename;
262 const char* id;
263 int x,y;
264 struct gui_img *img;
266 /* format: %x(n,filename.bmp,x,y)
267 or %xl(n,filename.bmp,x,y)
268 or %xl(n,filename.bmp,x,y,num_subimages)
271 id = element->params[0].data.text;
272 filename = element->params[1].data.text;
273 x = element->params[2].data.number;
274 y = element->params[3].data.number;
276 /* check the image number and load state */
277 if(find_image(*id, wps_data))
279 /* Invalid image ID */
280 return WPS_ERROR_INVALID_PARAM;
282 img = (struct gui_img*)skin_buffer_alloc(sizeof(struct gui_img));
283 if (!img)
284 return WPS_ERROR_INVALID_PARAM;
285 /* save a pointer to the filename */
286 img->bm.data = (char*)filename;
287 img->label = *id;
288 img->x = x;
289 img->y = y;
290 img->num_subimages = 1;
291 img->always_display = false;
292 // img->just_drawn = false;
293 img->display = -1;
295 /* save current viewport */
296 img->vp = &curr_vp->vp;
298 if (token->type == SKIN_TOKEN_IMAGE_DISPLAY)
300 img->always_display = true;
302 else if (element->params_count == 5)
304 img->num_subimages = element->params[4].data.number;
305 if (img->num_subimages <= 0)
306 return WPS_ERROR_INVALID_PARAM;
308 struct skin_token_list *item =
309 (struct skin_token_list *)new_skin_token_list_item(NULL, img);
310 if (!item)
311 return WPS_ERROR_INVALID_PARAM;
312 add_to_ll_chain(&wps_data->images, item);
314 return 0;
316 struct skin_font {
317 int id; /* the id from font_load */
318 char *name; /* filename without path and extension */
320 static struct skin_font skinfonts[MAXUSERFONTS];
321 static int parse_font_load(struct skin_element *element,
322 struct wps_token *token,
323 struct wps_data *wps_data)
325 (void)wps_data; (void)token;
326 int id = element->params[0].data.number;
327 char *filename = element->params[1].data.text;
328 char *ptr;
330 #if defined(DEBUG) || defined(SIMULATOR)
331 if (skinfonts[id-FONT_FIRSTUSERFONT].name != NULL)
333 DEBUGF("font id %d already being used\n", id);
335 #endif
336 /* make sure the filename contains .fnt,
337 * we dont actually use it, but require it anyway */
338 ptr = strchr(filename, '.');
339 if (!ptr || strncmp(ptr, ".fnt", 4))
340 return WPS_ERROR_INVALID_PARAM;
341 skinfonts[id-FONT_FIRSTUSERFONT].id = -1;
342 skinfonts[id-FONT_FIRSTUSERFONT].name = filename;
344 return 0;
348 #ifdef HAVE_LCD_BITMAP
350 static int parse_playlistview(struct skin_element *element,
351 struct wps_token *token,
352 struct wps_data *wps_data)
354 (void)wps_data;
355 struct playlistviewer *viewer =
356 (struct playlistviewer *)skin_buffer_alloc(sizeof(struct playlistviewer));
357 if (!viewer)
358 return WPS_ERROR_INVALID_PARAM;
359 viewer->vp = &curr_vp->vp;
360 viewer->show_icons = true;
361 viewer->start_offset = element->params[0].data.number;
362 viewer->lines[0] = element->params[1].data.code;
363 viewer->lines[1] = element->params[2].data.code;
365 token->value.data = (void*)viewer;
367 return 0;
369 #endif
371 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
373 static int parse_viewportcolour(struct skin_element *element,
374 struct wps_token *token,
375 struct wps_data *wps_data)
377 (void)wps_data;
378 struct skin_tag_parameter *param = element->params;
379 struct viewport_colour *colour =
380 (struct viewport_colour *)skin_buffer_alloc(sizeof(struct viewport_colour));
381 if (!colour)
382 return -1;
383 if (isdefault(param))
385 colour->colour = get_viewport_default_colour(curr_screen,
386 token->type == SKIN_TOKEN_VIEWPORT_FGCOLOUR);
388 else
390 if (!parse_color(param->data.text, &colour->colour))
391 return -1;
393 colour->vp = &curr_vp->vp;
394 token->value.data = colour;
395 if (element->line == curr_viewport_element->line)
397 if (token->type == SKIN_TOKEN_VIEWPORT_FGCOLOUR)
399 curr_vp->start_fgcolour = colour->colour;
400 curr_vp->vp.fg_pattern = colour->colour;
402 else
404 curr_vp->start_bgcolour = colour->colour;
405 curr_vp->vp.bg_pattern = colour->colour;
408 return 0;
411 static int parse_image_special(struct skin_element *element,
412 struct wps_token *token,
413 struct wps_data *wps_data)
415 (void)wps_data; /* kill warning */
416 (void)token;
417 bool error = false;
419 #if LCD_DEPTH > 1
420 if (token->type == SKIN_TOKEN_IMAGE_BACKDROP)
422 char *filename = element->params[0].data.text;
423 /* format: %X|filename.bmp| or %Xd */
424 if (!strcmp(filename, "d"))
426 wps_data->backdrop = NULL;
427 return 0;
429 else if (!error)
431 wps_data->backdrop = filename;
434 #endif
435 /* Skip the rest of the line */
436 return error ? WPS_ERROR_INVALID_PARAM : 0;
438 #endif
440 #endif /* HAVE_LCD_BITMAP */
442 static int parse_setting_and_lang(struct skin_element *element,
443 struct wps_token *token,
444 struct wps_data *wps_data)
446 /* NOTE: both the string validations that happen in here will
447 * automatically PASS on checkwps because its too hard to get
448 * settings_list.c and english.lang built for it.
449 * If that ever changes remove the #ifndef __PCTOOL__'s here
451 (void)wps_data;
452 char *temp = element->params[0].data.text;
453 int i;
455 if (token->type == SKIN_TOKEN_TRANSLATEDSTRING)
457 #ifndef __PCTOOL__
458 i = lang_english_to_id(temp);
459 if (i < 0)
460 return WPS_ERROR_INVALID_PARAM;
461 #endif
463 else
465 /* Find the setting */
466 for (i=0; i<nb_settings; i++)
467 if (settings[i].cfg_name &&
468 !strcmp(settings[i].cfg_name, temp))
469 break;
470 #ifndef __PCTOOL__
471 if (i == nb_settings)
472 return WPS_ERROR_INVALID_PARAM;
473 #endif
475 /* Store the setting number */
476 token->value.i = i;
477 return 0;
480 static int parse_timeout_tag(struct skin_element *element,
481 struct wps_token *token,
482 struct wps_data *wps_data)
484 (void)wps_data;
485 int val = 0;
486 if (element->params_count == 0)
488 switch (token->type)
490 case SKIN_TOKEN_SUBLINE_TIMEOUT:
491 return -1;
492 case SKIN_TOKEN_BUTTON_VOLUME:
493 case SKIN_TOKEN_TRACK_STARTING:
494 case SKIN_TOKEN_TRACK_ENDING:
495 case SKIN_TOKEN_LASTTOUCH:
496 val = 10;
497 break;
498 default:
499 break;
502 else
503 val = element->params[0].data.number;
504 token->value.i = val * TIMEOUT_UNIT;
505 return 0;
508 static int parse_progressbar_tag(struct skin_element* element,
509 struct wps_token *token,
510 struct wps_data *wps_data)
512 #ifdef HAVE_LCD_BITMAP
513 struct progressbar *pb;
514 struct skin_token_list *item;
515 struct viewport *vp = &curr_vp->vp;
516 struct skin_tag_parameter *param = element->params;
518 if (element->params_count == 0 &&
519 element->tag->type != SKIN_TOKEN_PROGRESSBAR)
520 return 0; /* nothing to do */
521 pb = (struct progressbar*)skin_buffer_alloc(sizeof(struct progressbar));
523 token->value.data = pb;
525 if (!pb)
526 return WPS_ERROR_INVALID_PARAM;
527 pb->vp = vp;
528 pb->have_bitmap_pb = false;
529 pb->bm.data = NULL; /* no bitmap specified */
530 pb->follow_lang_direction = follow_lang_direction > 0;
532 if (element->params_count == 0)
534 pb->x = 0;
535 pb->width = vp->width;
536 pb->height = SYSFONT_HEIGHT-2;
537 pb->y = -1; /* Will be computed during the rendering */
538 pb->type = element->tag->type;
539 return 0;
542 item = new_skin_token_list_item(token, pb);
543 if (!item)
544 return -1;
545 add_to_ll_chain(&wps_data->progressbars, item);
547 /* (x,y,width,height,filename) */
548 if (!isdefault(param))
549 pb->x = param->data.number;
550 else
551 pb->x = vp->x;
552 param++;
554 if (!isdefault(param))
555 pb->y = param->data.number;
556 else
557 pb->y = -1; /* computed at rendering */
558 param++;
560 if (!isdefault(param))
561 pb->width = param->data.number;
562 else
563 pb->width = vp->width - pb->x;
564 param++;
566 if (!isdefault(param))
568 /* A zero height makes no sense - reject it */
569 if (param->data.number == 0)
570 return WPS_ERROR_INVALID_PARAM;
572 pb->height = param->data.number;
574 else
576 if (vp->font > FONT_UI)
577 pb->height = -1; /* calculate at display time */
578 else
580 #ifndef __PCTOOL__
581 pb->height = font_get(vp->font)->height;
582 #else
583 pb->height = 8;
584 #endif
587 param++;
588 if (!isdefault(param))
589 pb->bm.data = param->data.text;
592 if (token->type == SKIN_TOKEN_VOLUME)
593 token->type = SKIN_TOKEN_VOLUMEBAR;
594 else if (token->type == SKIN_TOKEN_BATTERY_PERCENT)
595 token->type = SKIN_TOKEN_BATTERY_PERCENTBAR;
596 pb->type = token->type;
598 return 0;
600 #else
601 (void)element;
602 if (token->type == SKIN_TOKEN_PROGRESSBAR ||
603 token->type == SKIN_TOKEN_PLAYER_PROGRESSBAR)
605 wps_data->full_line_progressbar =
606 token->type == SKIN_TOKEN_PLAYER_PROGRESSBAR;
608 return 0;
610 #endif
613 #ifdef HAVE_ALBUMART
614 static int parse_albumart_load(struct skin_element* element,
615 struct wps_token *token,
616 struct wps_data *wps_data)
618 struct dim dimensions;
619 int albumart_slot;
620 bool swap_for_rtl = lang_is_rtl() && follow_lang_direction;
621 struct skin_albumart *aa =
622 (struct skin_albumart *)skin_buffer_alloc(sizeof(struct skin_albumart));
623 (void)token; /* silence warning */
624 if (!aa)
625 return -1;
627 /* reset albumart info in wps */
628 aa->width = -1;
629 aa->height = -1;
630 aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
631 aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
633 aa->x = element->params[0].data.number;
634 aa->y = element->params[1].data.number;
635 aa->width = element->params[2].data.number;
636 aa->height = element->params[3].data.number;
638 aa->vp = &curr_vp->vp;
639 aa->draw_handle = -1;
641 /* if we got here, we parsed everything ok .. ! */
642 if (aa->width < 0)
643 aa->width = 0;
644 else if (aa->width > LCD_WIDTH)
645 aa->width = LCD_WIDTH;
647 if (aa->height < 0)
648 aa->height = 0;
649 else if (aa->height > LCD_HEIGHT)
650 aa->height = LCD_HEIGHT;
652 if (swap_for_rtl)
653 aa->x = LCD_WIDTH - (aa->x + aa->width);
655 aa->state = WPS_ALBUMART_LOAD;
656 wps_data->albumart = aa;
658 dimensions.width = aa->width;
659 dimensions.height = aa->height;
661 albumart_slot = playback_claim_aa_slot(&dimensions);
663 if (0 <= albumart_slot)
664 wps_data->playback_aa_slot = albumart_slot;
666 if (element->params_count > 4 && !isdefault(&element->params[4]))
668 switch (*element->params[4].data.text)
670 case 'l':
671 case 'L':
672 if (swap_for_rtl)
673 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
674 else
675 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
676 break;
677 case 'c':
678 case 'C':
679 aa->xalign = WPS_ALBUMART_ALIGN_CENTER;
680 break;
681 case 'r':
682 case 'R':
683 if (swap_for_rtl)
684 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
685 else
686 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
687 break;
690 if (element->params_count > 5 && !isdefault(&element->params[5]))
692 switch (*element->params[5].data.text)
694 case 't':
695 case 'T':
696 aa->yalign = WPS_ALBUMART_ALIGN_TOP;
697 break;
698 case 'c':
699 case 'C':
700 aa->yalign = WPS_ALBUMART_ALIGN_CENTER;
701 break;
702 case 'b':
703 case 'B':
704 aa->yalign = WPS_ALBUMART_ALIGN_BOTTOM;
705 break;
708 return 0;
711 #endif /* HAVE_ALBUMART */
713 #ifdef HAVE_TOUCHSCREEN
715 struct touchaction {const char* s; int action;};
716 static const struct touchaction touchactions[] = {
717 /* generic actions, convert to screen actions on use */
718 {"prev", ACTION_STD_PREV }, {"next", ACTION_STD_NEXT },
719 {"rwd", ACTION_STD_PREVREPEAT }, {"ffwd", ACTION_STD_NEXTREPEAT },
720 {"hotkey", ACTION_STD_HOTKEY}, {"select", ACTION_STD_OK },
721 {"menu", ACTION_STD_MENU }, {"cancel", ACTION_STD_CANCEL },
722 {"contextmenu", ACTION_STD_CONTEXT},{"quickscreen", ACTION_STD_QUICKSCREEN },
723 /* not really WPS specific, but no equivilant ACTION_STD_* */
724 {"voldown", ACTION_WPS_VOLDOWN}, {"volup", ACTION_WPS_VOLUP},
726 /* WPS specific actions */
727 {"browse", ACTION_WPS_BROWSE },
728 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
729 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
730 {"pitch", ACTION_WPS_PITCHSCREEN}, {"playlist", ACTION_WPS_VIEW_PLAYLIST },
732 #if CONFIG_TUNER
733 /* FM screen actions */
734 /* Also allow browse, play, stop from WPS codes */
735 {"mode", ACTION_FM_MODE }, {"record", ACTION_FM_RECORD },
736 {"presets", ACTION_FM_PRESET},
737 #endif
740 static int parse_touchregion(struct skin_element *element,
741 struct wps_token *token,
742 struct wps_data *wps_data)
744 (void)token;
745 unsigned i, imax;
746 struct touchregion *region = NULL;
747 const char *action;
748 const char pb_string[] = "progressbar";
749 const char vol_string[] = "volume";
750 char temp[20];
752 /* format: %T(x,y,width,height,action)
753 * if action starts with & the area must be held to happen
754 * action is one of:
755 * play - play/pause playback
756 * stop - stop playback, exit the wps
757 * prev - prev track
758 * next - next track
759 * ffwd - seek forward
760 * rwd - seek backwards
761 * menu - go back to the main menu
762 * browse - go back to the file/db browser
763 * shuffle - toggle shuffle mode
764 * repmode - cycle the repeat mode
765 * quickscreen - go into the quickscreen
766 * contextmenu - open the context menu
767 * playlist - go into the playlist
768 * pitch - go into the pitchscreen
769 * volup - increase volume by one step
770 * voldown - decrease volume by one step
774 region = (struct touchregion*)skin_buffer_alloc(sizeof(struct touchregion));
775 if (!region)
776 return WPS_ERROR_INVALID_PARAM;
778 /* should probably do some bounds checking here with the viewport... but later */
779 region->action = ACTION_NONE;
780 region->x = element->params[0].data.number;
781 region->y = element->params[1].data.number;
782 region->width = element->params[2].data.number;
783 region->height = element->params[3].data.number;
784 region->wvp = curr_vp;
785 region->armed = false;
786 region->reverse_bar = false;
787 action = element->params[4].data.text;
789 strcpy(temp, action);
790 action = temp;
792 if (*action == '!')
794 region->reverse_bar = true;
795 action++;
798 if(!strcmp(pb_string, action))
799 region->type = WPS_TOUCHREGION_SCROLLBAR;
800 else if(!strcmp(vol_string, action))
801 region->type = WPS_TOUCHREGION_VOLUME;
802 else
804 region->type = WPS_TOUCHREGION_ACTION;
806 if (*action == '&')
808 action++;
809 region->repeat = true;
811 else
812 region->repeat = false;
814 imax = ARRAYLEN(touchactions);
815 for (i = 0; i < imax; i++)
817 /* try to match with one of our touchregion screens */
818 if (!strcmp(touchactions[i].s, action))
820 region->action = touchactions[i].action;
821 break;
824 if (region->action == ACTION_NONE)
825 return WPS_ERROR_INVALID_PARAM;
827 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
828 if (!item)
829 return WPS_ERROR_INVALID_PARAM;
830 add_to_ll_chain(&wps_data->touchregions, item);
831 return 0;
833 #endif
835 static bool check_feature_tag(const int type)
837 switch (type)
839 case SKIN_TOKEN_RTC_PRESENT:
840 #if CONFIG_RTC
841 return true;
842 #else
843 return false;
844 #endif
845 case SKIN_TOKEN_HAVE_RECORDING:
846 #ifdef HAVE_RECORDING
847 return true;
848 #else
849 return false;
850 #endif
851 case SKIN_TOKEN_HAVE_TUNER:
852 #if CONFIG_TUNER
853 if (radio_hardware_present())
854 return true;
855 #endif
856 return false;
858 #if CONFIG_TUNER
859 case SKIN_TOKEN_HAVE_RDS:
860 #ifdef HAVE_RDS_CAP
861 return true;
862 #else
863 return false;
864 #endif /* HAVE_RDS_CAP */
865 #endif /* CONFIG_TUNER */
866 default: /* not a tag we care about, just don't skip */
867 return true;
872 * initial setup of wps_data; does reset everything
873 * except fields which need to survive, i.e.
876 static void skin_data_reset(struct wps_data *wps_data)
878 wps_data->tree = NULL;
879 #ifdef HAVE_LCD_BITMAP
880 wps_data->images = NULL;
881 wps_data->progressbars = NULL;
882 #endif
883 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
884 wps_data->backdrop = NULL;
885 #endif
886 #ifdef HAVE_TOUCHSCREEN
887 wps_data->touchregions = NULL;
888 #endif
889 #ifdef HAVE_ALBUMART
890 wps_data->albumart = NULL;
891 if (wps_data->playback_aa_slot >= 0)
893 playback_release_aa_slot(wps_data->playback_aa_slot);
894 wps_data->playback_aa_slot = -1;
896 #endif
898 #ifdef HAVE_LCD_BITMAP
899 wps_data->peak_meter_enabled = false;
900 wps_data->wps_sb_tag = false;
901 wps_data->show_sb_on_wps = false;
902 #else /* HAVE_LCD_CHARCELLS */
903 /* progress bars */
904 int i;
905 for (i = 0; i < 8; i++)
907 wps_data->wps_progress_pat[i] = 0;
909 wps_data->full_line_progressbar = false;
910 #endif
911 wps_data->wps_loaded = false;
914 #ifdef HAVE_LCD_BITMAP
915 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
917 (void)wps_data; /* only needed for remote targets */
918 char img_path[MAX_PATH];
919 int fd;
920 get_image_filename(bitmap->data, bmpdir,
921 img_path, sizeof(img_path));
923 /* load the image */
924 int format;
925 #ifdef HAVE_REMOTE_LCD
926 if (curr_screen == SCREEN_REMOTE)
927 format = FORMAT_ANY|FORMAT_REMOTE;
928 else
929 #endif
930 format = FORMAT_ANY|FORMAT_TRANSPARENT;
932 fd = open(img_path, O_RDONLY);
933 if (fd < 0)
934 return false;
935 size_t buf_size = read_bmp_file(img_path, bitmap, 0,
936 format|FORMAT_RETURN_SIZE, NULL);
937 char* imgbuf = (char*)skin_buffer_alloc(buf_size);
938 if (!imgbuf)
940 close(fd);
941 return NULL;
943 lseek(fd, 0, SEEK_SET);
944 bitmap->data = imgbuf;
945 int ret = read_bmp_fd(fd, bitmap, buf_size, format, NULL);
947 close(fd);
948 if (ret > 0)
950 return true;
952 else
954 /* Abort if we can't load an image */
955 DEBUGF("Couldn't load '%s'\n", img_path);
956 return false;
960 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
962 struct skin_token_list *list;
963 bool retval = true; /* return false if a single image failed to load */
964 /* do the progressbars */
965 list = wps_data->progressbars;
966 while (list)
968 struct progressbar *pb = (struct progressbar*)list->token->value.data;
969 if (pb->bm.data)
971 pb->have_bitmap_pb = load_skin_bmp(wps_data, &pb->bm, bmpdir);
972 if (!pb->have_bitmap_pb) /* no success */
973 retval = false;
975 list = list->next;
977 /* regular images */
978 list = wps_data->images;
979 while (list)
981 struct gui_img *img = (struct gui_img*)list->token->value.data;
982 if (img->bm.data)
984 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
985 if (img->loaded)
986 img->subimage_height = img->bm.height / img->num_subimages;
987 else
988 retval = false;
990 list = list->next;
993 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
994 /* Backdrop load scheme:
995 * 1) %X|filename|
996 * 2) load the backdrop from settings
998 if (wps_data->backdrop)
1000 if (screens[curr_screen].depth == 1)
1002 wps_data->backdrop = NULL;
1003 return retval;
1005 bool needed = wps_data->backdrop[0] != '-';
1006 wps_data->backdrop = skin_backdrop_load(wps_data->backdrop,
1007 bmpdir, curr_screen);
1008 if (!wps_data->backdrop && needed)
1009 retval = false;
1011 #endif /* has backdrop support */
1013 return retval;
1016 static bool skin_load_fonts(struct wps_data *data)
1018 /* don't spit out after the first failue to aid debugging */
1019 bool success = true;
1020 struct skin_element *vp_list;
1021 int font_id;
1022 /* walk though each viewport and assign its font */
1023 for(vp_list = data->tree; vp_list; vp_list = vp_list->next)
1025 /* first, find the viewports that have a non-sys/ui-font font */
1026 struct skin_viewport *skin_vp =
1027 (struct skin_viewport*)vp_list->data;
1028 struct viewport *vp = &skin_vp->vp;
1031 if (vp->font <= FONT_UI)
1032 { /* the usual case -> built-in fonts */
1033 #ifdef HAVE_REMOTE_LCD
1034 if (vp->font == FONT_UI)
1035 vp->font += curr_screen;
1036 #endif
1037 continue;
1039 font_id = vp->font;
1041 /* now find the corresponding skin_font */
1042 struct skin_font *font = &skinfonts[font_id-FONT_FIRSTUSERFONT];
1043 if (!font->name)
1045 if (success)
1047 DEBUGF("font %d not specified\n", font_id);
1049 success = false;
1050 continue;
1053 /* load the font - will handle loading the same font again if
1054 * multiple viewports use the same */
1055 if (font->id < 0)
1057 char *dot = strchr(font->name, '.');
1058 *dot = '\0';
1059 font->id = skin_font_load(font->name);
1062 if (font->id < 0)
1064 DEBUGF("Unable to load font %d: '%s.fnt'\n",
1065 font_id, font->name);
1066 font->name = NULL; /* to stop trying to load it again if we fail */
1067 success = false;
1068 font->name = NULL;
1069 continue;
1072 /* finally, assign the font_id to the viewport */
1073 vp->font = font->id;
1075 return success;
1078 #endif /* HAVE_LCD_BITMAP */
1079 static int convert_viewport(struct wps_data *data, struct skin_element* element)
1081 struct skin_viewport *skin_vp =
1082 (struct skin_viewport *)skin_buffer_alloc(sizeof(struct skin_viewport));
1083 struct screen *display = &screens[curr_screen];
1085 if (!skin_vp)
1086 return CALLBACK_ERROR;
1088 skin_vp->hidden_flags = 0;
1089 skin_vp->label = VP_NO_LABEL;
1090 element->data = skin_vp;
1091 curr_vp = skin_vp;
1092 curr_viewport_element = element;
1094 viewport_set_defaults(&skin_vp->vp, curr_screen);
1095 #ifdef HAVE_REMOTE_LCD
1096 /* viewport_set_defaults() sets the font to FONT_UI+curr_screen.
1097 * This parser requires font 1 to always be the UI font,
1098 * so force it back to FONT_UI and handle the screen number at the end */
1099 skin_vp->vp.font = FONT_UI;
1100 #endif
1102 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1103 skin_vp->start_fgcolour = skin_vp->vp.fg_pattern;
1104 skin_vp->start_bgcolour = skin_vp->vp.bg_pattern;
1105 #endif
1108 struct skin_tag_parameter *param = element->params;
1109 if (element->params_count == 0) /* default viewport */
1111 if (!data->tree) /* first viewport in the skin */
1112 data->tree = element;
1113 skin_vp->label = VP_DEFAULT_LABEL;
1114 return CALLBACK_OK;
1117 if (element->params_count == 6)
1119 if (element->tag->type == SKIN_TOKEN_UIVIEWPORT_LOAD)
1121 if (isdefault(param))
1123 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
1124 skin_vp->label = VP_INFO_LABEL|VP_DEFAULT_LABEL;
1126 else
1128 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
1129 skin_vp->label = VP_INFO_LABEL|param->data.text[0];
1132 else
1134 skin_vp->hidden_flags = VP_DRAW_HIDEABLE|VP_DRAW_HIDDEN;
1135 skin_vp->label = param->data.text[0];
1137 param++;
1139 /* x */
1140 if (!isdefault(param))
1142 skin_vp->vp.x = param->data.number;
1143 if (param->data.number < 0)
1144 skin_vp->vp.x += display->lcdwidth;
1146 param++;
1147 /* y */
1148 if (!isdefault(param))
1150 skin_vp->vp.y = param->data.number;
1151 if (param->data.number < 0)
1152 skin_vp->vp.y += display->lcdheight;
1154 param++;
1155 /* width */
1156 if (!isdefault(param))
1158 skin_vp->vp.width = param->data.number;
1159 if (param->data.number < 0)
1160 skin_vp->vp.width = (skin_vp->vp.width + display->lcdwidth) - skin_vp->vp.x;
1162 else
1164 skin_vp->vp.width = display->lcdwidth - skin_vp->vp.x;
1166 param++;
1167 /* height */
1168 if (!isdefault(param))
1170 skin_vp->vp.height = param->data.number;
1171 if (param->data.number < 0)
1172 skin_vp->vp.height = (skin_vp->vp.height + display->lcdheight) - skin_vp->vp.y;
1174 else
1176 skin_vp->vp.height = display->lcdheight - skin_vp->vp.y;
1178 param++;
1179 #ifdef HAVE_LCD_BITMAP
1180 /* font */
1181 if (!isdefault(param))
1183 skin_vp->vp.font = param->data.number;
1185 #endif
1187 return CALLBACK_OK;
1191 static int skin_element_callback(struct skin_element* element, void* data)
1193 struct wps_data *wps_data = (struct wps_data *)data;
1194 struct wps_token *token;
1195 parse_function function = NULL;
1197 switch (element->type)
1199 /* IMPORTANT: element params are shared, so copy them if needed
1200 * or use then NOW, dont presume they have a long lifespan
1202 case TAG:
1204 token = (struct wps_token*)skin_buffer_alloc(sizeof(struct wps_token));
1205 memset(token, 0, sizeof(*token));
1206 token->type = element->tag->type;
1208 if ((element->tag->flags&SKIN_REFRESH_ALL) == SKIN_RTC_REFRESH)
1210 #ifdef CONFIG_RTC
1211 curr_line->update_mode |= SKIN_REFRESH_DYNAMIC;
1212 #else
1213 curr_line->update_mode |= SKIN_REFRESH_STATIC;
1214 #endif
1216 else
1217 curr_line->update_mode |= element->tag->flags&SKIN_REFRESH_ALL;
1219 element->data = token;
1221 /* Some tags need special handling for the tag, so add them here */
1222 switch (token->type)
1224 case SKIN_TOKEN_ALIGN_LANGDIRECTION:
1225 follow_lang_direction = 2;
1226 break;
1227 case SKIN_TOKEN_PROGRESSBAR:
1228 case SKIN_TOKEN_VOLUME:
1229 case SKIN_TOKEN_BATTERY_PERCENT:
1230 case SKIN_TOKEN_PLAYER_PROGRESSBAR:
1231 function = parse_progressbar_tag;
1232 break;
1233 case SKIN_TOKEN_SUBLINE_TIMEOUT:
1234 case SKIN_TOKEN_BUTTON_VOLUME:
1235 case SKIN_TOKEN_TRACK_STARTING:
1236 case SKIN_TOKEN_TRACK_ENDING:
1237 case SKIN_TOKEN_LASTTOUCH:
1238 function = parse_timeout_tag;
1239 break;
1240 #ifdef HAVE_LCD_BITMAP
1241 case SKIN_TOKEN_DISABLE_THEME:
1242 case SKIN_TOKEN_ENABLE_THEME:
1243 case SKIN_TOKEN_DRAW_INBUILTBAR:
1244 function = parse_statusbar_tags;
1245 break;
1246 #endif
1247 case SKIN_TOKEN_FILE_DIRECTORY:
1248 token->value.i = element->params[0].data.number;
1249 break;
1250 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1251 case SKIN_TOKEN_VIEWPORT_FGCOLOUR:
1252 case SKIN_TOKEN_VIEWPORT_BGCOLOUR:
1253 function = parse_viewportcolour;
1254 break;
1255 case SKIN_TOKEN_IMAGE_BACKDROP:
1256 function = parse_image_special;
1257 break;
1258 #endif
1259 case SKIN_TOKEN_TRANSLATEDSTRING:
1260 case SKIN_TOKEN_SETTING:
1261 function = parse_setting_and_lang;
1262 break;
1263 #ifdef HAVE_LCD_BITMAP
1264 case SKIN_TOKEN_VIEWPORT_CUSTOMLIST:
1265 function = parse_playlistview;
1266 break;
1267 case SKIN_TOKEN_LOAD_FONT:
1268 function = parse_font_load;
1269 break;
1270 case SKIN_TOKEN_VIEWPORT_ENABLE:
1271 case SKIN_TOKEN_UIVIEWPORT_ENABLE:
1272 token->value.i = element->params[0].data.text[0];
1273 break;
1274 case SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY:
1275 function = parse_image_display;
1276 break;
1277 case SKIN_TOKEN_IMAGE_PRELOAD:
1278 case SKIN_TOKEN_IMAGE_DISPLAY:
1279 function = parse_image_load;
1280 break;
1281 #endif
1282 #ifdef HAVE_TOUCHSCREEN
1283 case SKIN_TOKEN_TOUCHREGION:
1284 function = parse_touchregion;
1285 break;
1286 #endif
1287 #ifdef HAVE_ALBUMART
1288 case SKIN_TOKEN_ALBUMART_DISPLAY:
1289 if (wps_data->albumart)
1290 wps_data->albumart->vp = &curr_vp->vp;
1291 break;
1292 case SKIN_TOKEN_ALBUMART_LOAD:
1293 function = parse_albumart_load;
1294 break;
1295 #endif
1296 default:
1297 break;
1299 if (function)
1301 if (function(element, token, wps_data) < 0)
1302 return CALLBACK_ERROR;
1304 /* tags that start with 'F', 'I' or 'D' are for the next file */
1305 if ( *(element->tag->name) == 'I' || *(element->tag->name) == 'F' ||
1306 *(element->tag->name) == 'D')
1307 token->next = true;
1308 if (follow_lang_direction > 0 )
1309 follow_lang_direction--;
1310 break;
1312 case VIEWPORT:
1313 return convert_viewport(wps_data, element);
1314 case LINE:
1316 struct line *line =
1317 (struct line *)skin_buffer_alloc(sizeof(struct line));
1318 line->update_mode = SKIN_REFRESH_STATIC;
1319 line->timeout = DEFAULT_SUBLINE_TIME_MULTIPLIER * TIMEOUT_UNIT;
1320 curr_line = line;
1321 element->data = line;
1323 break;
1324 case LINE_ALTERNATOR:
1326 struct line_alternator *alternator =
1327 (struct line_alternator *)skin_buffer_alloc(sizeof(struct line_alternator));
1328 alternator->current_line = 0;
1329 #ifndef __PCTOOL__
1330 alternator->last_change_tick = current_tick;
1331 #endif
1332 element->data = alternator;
1334 break;
1335 case CONDITIONAL:
1337 struct conditional *conditional =
1338 (struct conditional *)skin_buffer_alloc(sizeof(struct conditional));
1339 conditional->last_value = -1;
1340 conditional->token = element->data;
1341 element->data = conditional;
1342 if (!check_feature_tag(element->tag->type))
1344 return FEATURE_NOT_AVAILABLE;
1346 return CALLBACK_OK;
1348 case TEXT:
1349 curr_line->update_mode |= SKIN_REFRESH_STATIC;
1350 break;
1351 default:
1352 break;
1354 return CALLBACK_OK;
1357 /* to setup up the wps-data from a format-buffer (isfile = false)
1358 from a (wps-)file (isfile = true)*/
1359 bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
1360 const char *buf, bool isfile)
1362 char *wps_buffer = NULL;
1363 if (!wps_data || !buf)
1364 return false;
1365 #ifdef HAVE_ALBUMART
1366 int status;
1367 struct mp3entry *curtrack;
1368 long offset;
1369 struct skin_albumart old_aa = {.state = WPS_ALBUMART_NONE};
1370 if (wps_data->albumart)
1372 old_aa.state = wps_data->albumart->state;
1373 old_aa.height = wps_data->albumart->height;
1374 old_aa.width = wps_data->albumart->width;
1376 #endif
1377 #ifdef HAVE_LCD_BITMAP
1378 int i;
1379 for (i=0;i<MAXUSERFONTS;i++)
1381 skinfonts[i].id = -1;
1382 skinfonts[i].name = NULL;
1384 #endif
1385 #ifdef DEBUG_SKIN_ENGINE
1386 if (isfile && debug_wps)
1388 DEBUGF("\n=====================\nLoading '%s'\n=====================\n", buf);
1390 #endif
1393 skin_data_reset(wps_data);
1394 wps_data->wps_loaded = false;
1395 curr_screen = screen;
1396 curr_line = NULL;
1397 curr_vp = NULL;
1398 curr_viewport_element = NULL;
1400 if (isfile)
1402 int fd = open_utf8(buf, O_RDONLY);
1404 if (fd < 0)
1405 return false;
1407 /* get buffer space from the plugin buffer */
1408 size_t buffersize = 0;
1409 wps_buffer = (char *)plugin_get_buffer(&buffersize);
1411 if (!wps_buffer)
1412 return false;
1414 /* copy the file's content to the buffer for parsing,
1415 ensuring that every line ends with a newline char. */
1416 unsigned int start = 0;
1417 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
1419 start += strlen(wps_buffer + start);
1420 if (start < buffersize - 1)
1422 wps_buffer[start++] = '\n';
1423 wps_buffer[start] = 0;
1426 close(fd);
1427 if (start <= 0)
1428 return false;
1430 else
1432 wps_buffer = (char*)buf;
1434 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1435 wps_data->backdrop = "-";
1436 #endif
1437 /* parse the skin source */
1438 skin_buffer_save_position();
1439 wps_data->tree = skin_parse(wps_buffer, skin_element_callback, wps_data);
1440 if (!wps_data->tree) {
1441 skin_data_reset(wps_data);
1442 skin_buffer_restore_position();
1443 return false;
1446 #ifdef HAVE_LCD_BITMAP
1447 char bmpdir[MAX_PATH];
1448 if (isfile)
1450 /* get the bitmap dir */
1451 char *dot = strrchr(buf, '.');
1452 strlcpy(bmpdir, buf, dot - buf + 1);
1454 else
1455 { /* fall back to backdrop dir for built-in themes */
1456 /* no get_user_file_path(), assuming we ship bmps for built-in themes */
1457 snprintf(bmpdir, MAX_PATH, "%s", BACKDROP_DIR);
1459 /* load the bitmaps that were found by the parsing */
1460 if (!load_skin_bitmaps(wps_data, bmpdir) ||
1461 !skin_load_fonts(wps_data))
1463 skin_data_reset(wps_data);
1464 skin_buffer_restore_position();
1465 return false;
1467 #endif
1468 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
1469 status = audio_status();
1470 if (status & AUDIO_STATUS_PLAY)
1472 struct skin_albumart *aa = wps_data->albumart;
1473 if (aa && ((aa->state && !old_aa.state) ||
1474 (aa->state &&
1475 (((old_aa.height != aa->height) ||
1476 (old_aa.width != aa->width))))))
1478 curtrack = audio_current_track();
1479 offset = curtrack->offset;
1480 audio_stop();
1481 if (!(status & AUDIO_STATUS_PAUSE))
1482 audio_play(offset);
1485 #endif
1486 wps_data->wps_loaded = true;
1487 #ifdef DEBUG_SKIN_ENGINE
1488 // if (isfile && debug_wps)
1489 // debug_skin_usage();
1490 #endif
1491 return true;