fix touchscreen errors
[maemo-rb.git] / apps / gui / skin_engine / skin_parser.c
blobd81ccf62b3ec6d3e576f91fc93811064f166915b
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 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);
198 return 0;
201 static int get_image_id(int c)
203 if(c >= 'a' && c <= 'z')
204 return c - 'a';
205 else if(c >= 'A' && c <= 'Z')
206 return c - 'A' + 26;
207 else
208 return -1;
211 char *get_image_filename(const char *start, const char* bmpdir,
212 char *buf, int buf_size)
214 snprintf(buf, buf_size, "%s/%s", bmpdir, start);
216 return buf;
219 static int parse_image_display(struct skin_element *element,
220 struct wps_token *token,
221 struct wps_data *wps_data)
223 char *text = element->params[0].data.text;
224 char label = text[0];
225 char sublabel = text[1];
226 int subimage;
227 struct gui_img *img;
229 /* sanity check */
230 img = find_image(label, wps_data);
231 if (!img)
233 token->value.i = label; /* so debug works */
234 return WPS_ERROR_INVALID_PARAM;
237 if ((subimage = get_image_id(sublabel)) != -1)
239 if (subimage >= img->num_subimages)
240 return WPS_ERROR_INVALID_PARAM;
242 /* Store sub-image number to display in high bits */
243 token->value.i = label | (subimage << 8);
244 return 4; /* We have consumed 2 bytes */
245 } else {
246 token->value.i = label;
247 return 3; /* We have consumed 1 byte */
251 static int parse_image_load(struct skin_element *element,
252 struct wps_token *token,
253 struct wps_data *wps_data)
255 const char* filename;
256 const char* id;
257 int x,y;
258 struct gui_img *img;
260 /* format: %x(n,filename.bmp,x,y)
261 or %xl(n,filename.bmp,x,y)
262 or %xl(n,filename.bmp,x,y,num_subimages)
265 id = element->params[0].data.text;
266 filename = element->params[1].data.text;
267 x = element->params[2].data.number;
268 y = element->params[3].data.number;
270 /* check the image number and load state */
271 if(find_image(*id, wps_data))
273 /* Invalid image ID */
274 return WPS_ERROR_INVALID_PARAM;
276 img = (struct gui_img*)skin_buffer_alloc(sizeof(struct gui_img));
277 if (!img)
278 return WPS_ERROR_INVALID_PARAM;
279 /* save a pointer to the filename */
280 img->bm.data = (char*)filename;
281 img->label = *id;
282 img->x = x;
283 img->y = y;
284 img->num_subimages = 1;
285 img->always_display = false;
286 // img->just_drawn = false;
287 img->display = -1;
289 /* save current viewport */
290 img->vp = &curr_vp->vp;
292 if (token->type == SKIN_TOKEN_IMAGE_DISPLAY)
294 img->always_display = true;
296 else if (element->params_count == 5)
298 img->num_subimages = element->params[4].data.number;
299 if (img->num_subimages <= 0)
300 return WPS_ERROR_INVALID_PARAM;
302 struct skin_token_list *item =
303 (struct skin_token_list *)new_skin_token_list_item(NULL, img);
304 if (!item)
305 return WPS_ERROR_INVALID_PARAM;
306 add_to_ll_chain(&wps_data->images, item);
308 return 0;
310 struct skin_font {
311 int id; /* the id from font_load */
312 char *name; /* filename without path and extension */
314 static struct skin_font skinfonts[MAXUSERFONTS];
315 static int parse_font_load(struct skin_element *element,
316 struct wps_token *token,
317 struct wps_data *wps_data)
319 (void)wps_data; (void)token;
320 int id = element->params[0].data.number;
321 char *filename = element->params[1].data.text;
322 char *ptr;
324 #if defined(DEBUG) || defined(SIMULATOR)
325 if (skinfonts[id-FONT_FIRSTUSERFONT].name != NULL)
327 DEBUGF("font id %d already being used\n", id);
329 #endif
330 /* make sure the filename contains .fnt,
331 * we dont actually use it, but require it anyway */
332 ptr = strchr(filename, '.');
333 if (!ptr || strncmp(ptr, ".fnt", 4))
334 return WPS_ERROR_INVALID_PARAM;
335 skinfonts[id-FONT_FIRSTUSERFONT].id = -1;
336 skinfonts[id-FONT_FIRSTUSERFONT].name = filename;
338 return 0;
342 #ifdef HAVE_LCD_BITMAP
344 static int parse_playlistview(struct skin_element *element,
345 struct wps_token *token,
346 struct wps_data *wps_data)
348 (void)wps_data;
349 struct playlistviewer *viewer =
350 (struct playlistviewer *)skin_buffer_alloc(sizeof(struct playlistviewer));
351 if (!viewer)
352 return WPS_ERROR_INVALID_PARAM;
353 viewer->vp = &curr_vp->vp;
354 viewer->show_icons = true;
355 viewer->start_offset = element->params[0].data.number;
356 viewer->lines[0] = element->params[1].data.code;
357 viewer->lines[1] = element->params[2].data.code;
359 token->value.data = (void*)viewer;
361 return 0;
363 #endif
365 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
367 static int parse_viewportcolour(struct skin_element *element,
368 struct wps_token *token,
369 struct wps_data *wps_data)
371 (void)wps_data;
372 struct skin_tag_parameter *param = element->params;
373 struct viewport_colour *colour =
374 (struct viewport_colour *)skin_buffer_alloc(sizeof(struct viewport_colour));
375 if (!colour)
376 return -1;
377 if (isdefault(param))
379 colour->colour = get_viewport_default_colour(curr_screen,
380 token->type == SKIN_TOKEN_VIEWPORT_FGCOLOUR);
382 else
384 if (!parse_color(param->data.text, &colour->colour))
385 return -1;
387 colour->vp = &curr_vp->vp;
388 token->value.data = colour;
389 if (element->line == curr_viewport_element->line)
391 if (token->type == SKIN_TOKEN_VIEWPORT_FGCOLOUR)
393 curr_vp->start_fgcolour = colour->colour;
394 curr_vp->vp.fg_pattern = colour->colour;
396 else
398 curr_vp->start_bgcolour = colour->colour;
399 curr_vp->vp.bg_pattern = colour->colour;
402 return 0;
405 static int parse_image_special(struct skin_element *element,
406 struct wps_token *token,
407 struct wps_data *wps_data)
409 (void)wps_data; /* kill warning */
410 (void)token;
411 bool error = false;
413 #if LCD_DEPTH > 1
414 if (token->type == SKIN_TOKEN_IMAGE_BACKDROP)
416 char *filename = element->params[0].data.text;
417 /* format: %X|filename.bmp| or %Xd */
418 if (!strcmp(filename, "d"))
420 wps_data->backdrop = NULL;
421 return 0;
423 else if (!error)
425 wps_data->backdrop = filename;
428 #endif
429 /* Skip the rest of the line */
430 return error ? WPS_ERROR_INVALID_PARAM : 0;
432 #endif
434 #endif /* HAVE_LCD_BITMAP */
436 static int parse_setting_and_lang(struct skin_element *element,
437 struct wps_token *token,
438 struct wps_data *wps_data)
440 /* NOTE: both the string validations that happen in here will
441 * automatically PASS on checkwps because its too hard to get
442 * settings_list.c and english.lang built for it.
443 * If that ever changes remove the #ifndef __PCTOOL__'s here
445 (void)wps_data;
446 char *temp = element->params[0].data.text;
447 int i;
449 if (token->type == SKIN_TOKEN_TRANSLATEDSTRING)
451 #ifndef __PCTOOL__
452 i = lang_english_to_id(temp);
453 if (i < 0)
454 return WPS_ERROR_INVALID_PARAM;
455 #endif
457 else
459 /* Find the setting */
460 for (i=0; i<nb_settings; i++)
461 if (settings[i].cfg_name &&
462 !strcmp(settings[i].cfg_name, temp))
463 break;
464 #ifndef __PCTOOL__
465 if (i == nb_settings)
466 return WPS_ERROR_INVALID_PARAM;
467 #endif
469 /* Store the setting number */
470 token->value.i = i;
471 return 0;
474 static int parse_timeout_tag(struct skin_element *element,
475 struct wps_token *token,
476 struct wps_data *wps_data)
478 (void)wps_data;
479 int val = 0;
480 if (element->params_count == 0)
482 switch (token->type)
484 case SKIN_TOKEN_SUBLINE_TIMEOUT:
485 return -1;
486 case SKIN_TOKEN_BUTTON_VOLUME:
487 case SKIN_TOKEN_TRACK_STARTING:
488 case SKIN_TOKEN_TRACK_ENDING:
489 case SKIN_TOKEN_LASTTOUCH:
490 val = 10;
491 break;
492 default:
493 break;
496 else
497 val = element->params[0].data.number;
498 token->value.i = val;
499 if (token->type == SKIN_TOKEN_SUBLINE_TIMEOUT)
500 curr_line->timeout = val * TIMEOUT_UNIT;
501 return 0;
504 static int parse_progressbar_tag(struct skin_element* element,
505 struct wps_token *token,
506 struct wps_data *wps_data)
508 #ifdef HAVE_LCD_BITMAP
509 struct progressbar *pb;
510 struct skin_token_list *item;
511 struct viewport *vp = &curr_vp->vp;
512 struct skin_tag_parameter *param = element->params;
514 if (element->params_count == 0 &&
515 element->tag->type != SKIN_TOKEN_PROGRESSBAR)
516 return 0; /* nothing to do */
517 pb = (struct progressbar*)skin_buffer_alloc(sizeof(struct progressbar));
519 token->value.data = pb;
521 if (!pb)
522 return WPS_ERROR_INVALID_PARAM;
523 pb->vp = vp;
524 pb->have_bitmap_pb = false;
525 pb->bm.data = NULL; /* no bitmap specified */
526 pb->follow_lang_direction = follow_lang_direction > 0;
528 if (element->params_count == 0)
530 pb->x = 0;
531 pb->width = vp->width;
532 pb->height = SYSFONT_HEIGHT-2;
533 pb->y = -1; /* Will be computed during the rendering */
534 pb->type = element->tag->type;
535 return 0;
538 item = new_skin_token_list_item(token, pb);
539 if (!item)
540 return -1;
541 add_to_ll_chain(&wps_data->progressbars, item);
543 /* (x,y,width,height,filename) */
544 if (!isdefault(param))
545 pb->x = param->data.number;
546 else
547 pb->x = vp->x;
548 param++;
550 if (!isdefault(param))
551 pb->y = param->data.number;
552 else
553 pb->y = -1; /* computed at rendering */
554 param++;
556 if (!isdefault(param))
557 pb->width = param->data.number;
558 else
559 pb->width = vp->width - pb->x;
560 param++;
562 if (!isdefault(param))
564 /* A zero height makes no sense - reject it */
565 if (param->data.number == 0)
566 return WPS_ERROR_INVALID_PARAM;
568 pb->height = param->data.number;
570 else
572 if (vp->font > FONT_UI)
573 pb->height = -1; /* calculate at display time */
574 else
576 #ifndef __PCTOOL__
577 pb->height = font_get(vp->font)->height;
578 #else
579 pb->height = 8;
580 #endif
583 param++;
584 if (!isdefault(param))
585 pb->bm.data = param->data.text;
588 if (token->type == SKIN_TOKEN_VOLUME)
589 token->type = SKIN_TOKEN_VOLUMEBAR;
590 else if (token->type == SKIN_TOKEN_BATTERY_PERCENT)
591 token->type = SKIN_TOKEN_BATTERY_PERCENTBAR;
592 pb->type = token->type;
594 return 0;
596 #else
597 (void)element;
598 if (token->type == SKIN_TOKEN_PROGRESSBAR ||
599 token->type == SKIN_TOKEN_PLAYER_PROGRESSBAR)
601 wps_data->full_line_progressbar =
602 token->type == SKIN_TOKEN_PLAYER_PROGRESSBAR;
604 return 0;
606 #endif
609 #ifdef HAVE_ALBUMART
610 static int parse_albumart_load(struct skin_element* element,
611 struct wps_token *token,
612 struct wps_data *wps_data)
614 struct dim dimensions;
615 int albumart_slot;
616 bool swap_for_rtl = lang_is_rtl() && follow_lang_direction;
617 struct skin_albumart *aa =
618 (struct skin_albumart *)skin_buffer_alloc(sizeof(struct skin_albumart));
619 (void)token; /* silence warning */
620 if (!aa)
621 return -1;
623 /* reset albumart info in wps */
624 aa->width = -1;
625 aa->height = -1;
626 aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
627 aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
629 aa->x = element->params[0].data.number;
630 aa->y = element->params[1].data.number;
631 aa->width = element->params[2].data.number;
632 aa->height = element->params[3].data.number;
634 aa->vp = &curr_vp->vp;
635 aa->draw_handle = -1;
637 /* if we got here, we parsed everything ok .. ! */
638 if (aa->width < 0)
639 aa->width = 0;
640 else if (aa->width > LCD_WIDTH)
641 aa->width = LCD_WIDTH;
643 if (aa->height < 0)
644 aa->height = 0;
645 else if (aa->height > LCD_HEIGHT)
646 aa->height = LCD_HEIGHT;
648 if (swap_for_rtl)
649 aa->x = LCD_WIDTH - (aa->x + aa->width);
651 aa->state = WPS_ALBUMART_LOAD;
652 wps_data->albumart = aa;
654 dimensions.width = aa->width;
655 dimensions.height = aa->height;
657 albumart_slot = playback_claim_aa_slot(&dimensions);
659 if (0 <= albumart_slot)
660 wps_data->playback_aa_slot = albumart_slot;
662 if (element->params_count > 4 && !isdefault(&element->params[4]))
664 switch (*element->params[4].data.text)
666 case 'l':
667 case 'L':
668 if (swap_for_rtl)
669 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
670 else
671 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
672 break;
673 case 'c':
674 case 'C':
675 aa->xalign = WPS_ALBUMART_ALIGN_CENTER;
676 break;
677 case 'r':
678 case 'R':
679 if (swap_for_rtl)
680 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
681 else
682 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
683 break;
686 if (element->params_count > 5 && !isdefault(&element->params[5]))
688 switch (*element->params[5].data.text)
690 case 't':
691 case 'T':
692 aa->yalign = WPS_ALBUMART_ALIGN_TOP;
693 break;
694 case 'c':
695 case 'C':
696 aa->yalign = WPS_ALBUMART_ALIGN_CENTER;
697 break;
698 case 'b':
699 case 'B':
700 aa->yalign = WPS_ALBUMART_ALIGN_BOTTOM;
701 break;
704 return 0;
707 #endif /* HAVE_ALBUMART */
709 #ifdef HAVE_TOUCHSCREEN
711 struct touchaction {const char* s; int action;};
712 static const struct touchaction touchactions[] = {
713 /* generic actions, convert to screen actions on use */
714 {"prev", ACTION_STD_PREV }, {"next", ACTION_STD_NEXT },
715 {"rwd", ACTION_STD_PREVREPEAT }, {"ffwd", ACTION_STD_NEXTREPEAT },
716 {"hotkey", ACTION_STD_HOTKEY}, {"select", ACTION_STD_OK },
717 {"menu", ACTION_STD_MENU }, {"cancel", ACTION_STD_CANCEL },
718 {"contextmenu", ACTION_STD_CONTEXT},{"quickscreen", ACTION_STD_QUICKSCREEN },
719 /* not really WPS specific, but no equivilant ACTION_STD_* */
720 {"voldown", ACTION_WPS_VOLDOWN}, {"volup", ACTION_WPS_VOLUP},
722 /* WPS specific actions */
723 {"browse", ACTION_WPS_BROWSE },
724 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
725 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
726 {"pitch", ACTION_WPS_PITCHSCREEN}, {"playlist", ACTION_WPS_VIEW_PLAYLIST },
728 #if CONFIG_TUNER
729 /* FM screen actions */
730 /* Also allow browse, play, stop from WPS codes */
731 {"mode", ACTION_FM_MODE }, {"record", ACTION_FM_RECORD },
732 {"presets", ACTION_FM_PRESET},
733 #endif
736 static int parse_touchregion(struct skin_element *element,
737 struct wps_token *token,
738 struct wps_data *wps_data)
740 (void)token;
741 unsigned i, imax;
742 struct touchregion *region = NULL;
743 const char *action;
744 const char pb_string[] = "progressbar";
745 const char vol_string[] = "volume";
746 char temp[20];
748 /* format: %T(x,y,width,height,action)
749 * if action starts with & the area must be held to happen
750 * action is one of:
751 * play - play/pause playback
752 * stop - stop playback, exit the wps
753 * prev - prev track
754 * next - next track
755 * ffwd - seek forward
756 * rwd - seek backwards
757 * menu - go back to the main menu
758 * browse - go back to the file/db browser
759 * shuffle - toggle shuffle mode
760 * repmode - cycle the repeat mode
761 * quickscreen - go into the quickscreen
762 * contextmenu - open the context menu
763 * playlist - go into the playlist
764 * pitch - go into the pitchscreen
765 * volup - increase volume by one step
766 * voldown - decrease volume by one step
770 region = (struct touchregion*)skin_buffer_alloc(sizeof(struct touchregion));
771 if (!region)
772 return WPS_ERROR_INVALID_PARAM;
774 /* should probably do some bounds checking here with the viewport... but later */
775 region->action = ACTION_NONE;
776 region->x = element->params[0].data.number;
777 region->y = element->params[1].data.number;
778 region->width = element->params[2].data.number;
779 region->height = element->params[3].data.number;
780 region->wvp = curr_vp;
781 region->armed = false;
782 region->reverse_bar = false;
783 action = element->params[4].data.text;
785 strcpy(temp, action);
786 action = temp;
788 if (*action == '!')
790 region->reverse_bar = true;
791 action++;
794 if(!strcmp(pb_string, action))
795 region->type = WPS_TOUCHREGION_SCROLLBAR;
796 else if(!strcmp(vol_string, action))
797 region->type = WPS_TOUCHREGION_VOLUME;
798 else
800 region->type = WPS_TOUCHREGION_ACTION;
802 if (*action == '&')
804 action++;
805 region->repeat = true;
807 else
808 region->repeat = false;
810 imax = ARRAYLEN(touchactions);
811 for (i = 0; i < imax; i++)
813 /* try to match with one of our touchregion screens */
814 if (!strcmp(touchactions[i].s, action))
816 region->action = touchactions[i].action;
817 break;
820 if (region->action == ACTION_NONE)
821 return WPS_ERROR_INVALID_PARAM;
823 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
824 if (!item)
825 return WPS_ERROR_INVALID_PARAM;
826 add_to_ll_chain(&wps_data->touchregions, item);
827 return 0;
829 #endif
831 static bool check_feature_tag(const int type)
833 switch (type)
835 case SKIN_TOKEN_RTC_PRESENT:
836 #if CONFIG_RTC
837 return true;
838 #else
839 return false;
840 #endif
841 case SKIN_TOKEN_HAVE_RECORDING:
842 #ifdef HAVE_RECORDING
843 return true;
844 #else
845 return false;
846 #endif
847 case SKIN_TOKEN_HAVE_TUNER:
848 #if CONFIG_TUNER
849 if (radio_hardware_present())
850 return true;
851 #endif
852 return false;
854 #if CONFIG_TUNER
855 case SKIN_TOKEN_HAVE_RDS:
856 #ifdef HAVE_RDS_CAP
857 return true;
858 #else
859 return false;
860 #endif /* HAVE_RDS_CAP */
861 #endif /* CONFIG_TUNER */
862 default: /* not a tag we care about, just don't skip */
863 return true;
868 * initial setup of wps_data; does reset everything
869 * except fields which need to survive, i.e.
872 static void skin_data_reset(struct wps_data *wps_data)
874 wps_data->tree = NULL;
875 #ifdef HAVE_LCD_BITMAP
876 wps_data->images = NULL;
877 wps_data->progressbars = NULL;
878 #endif
879 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
880 wps_data->backdrop = NULL;
881 #endif
882 #ifdef HAVE_TOUCHSCREEN
883 wps_data->touchregions = NULL;
884 #endif
885 #ifdef HAVE_ALBUMART
886 wps_data->albumart = NULL;
887 if (wps_data->playback_aa_slot >= 0)
889 playback_release_aa_slot(wps_data->playback_aa_slot);
890 wps_data->playback_aa_slot = -1;
892 #endif
894 #ifdef HAVE_LCD_BITMAP
895 wps_data->peak_meter_enabled = false;
896 wps_data->wps_sb_tag = false;
897 wps_data->show_sb_on_wps = false;
898 #else /* HAVE_LCD_CHARCELLS */
899 /* progress bars */
900 int i;
901 for (i = 0; i < 8; i++)
903 wps_data->wps_progress_pat[i] = 0;
905 wps_data->full_line_progressbar = false;
906 #endif
907 wps_data->wps_loaded = false;
910 #ifdef HAVE_LCD_BITMAP
911 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
913 (void)wps_data; /* only needed for remote targets */
914 char img_path[MAX_PATH];
915 int fd;
916 get_image_filename(bitmap->data, bmpdir,
917 img_path, sizeof(img_path));
919 /* load the image */
920 int format;
921 #ifdef HAVE_REMOTE_LCD
922 if (curr_screen == SCREEN_REMOTE)
923 format = FORMAT_ANY|FORMAT_REMOTE;
924 else
925 #endif
926 format = FORMAT_ANY|FORMAT_TRANSPARENT;
928 fd = open(img_path, O_RDONLY);
929 if (fd < 0)
930 return false;
931 size_t buf_size = read_bmp_file(img_path, bitmap, 0,
932 format|FORMAT_RETURN_SIZE, NULL);
933 char* imgbuf = (char*)skin_buffer_alloc(buf_size);
934 if (!imgbuf)
936 close(fd);
937 return NULL;
939 lseek(fd, 0, SEEK_SET);
940 bitmap->data = imgbuf;
941 int ret = read_bmp_fd(fd, bitmap, buf_size, format, NULL);
943 close(fd);
944 if (ret > 0)
946 return true;
948 else
950 /* Abort if we can't load an image */
951 DEBUGF("Couldn't load '%s'\n", img_path);
952 return false;
956 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
958 struct skin_token_list *list;
959 bool retval = true; /* return false if a single image failed to load */
960 /* do the progressbars */
961 list = wps_data->progressbars;
962 while (list)
964 struct progressbar *pb = (struct progressbar*)list->token->value.data;
965 if (pb->bm.data)
967 pb->have_bitmap_pb = load_skin_bmp(wps_data, &pb->bm, bmpdir);
968 if (!pb->have_bitmap_pb) /* no success */
969 retval = false;
971 list = list->next;
973 /* regular images */
974 list = wps_data->images;
975 while (list)
977 struct gui_img *img = (struct gui_img*)list->token->value.data;
978 if (img->bm.data)
980 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
981 if (img->loaded)
982 img->subimage_height = img->bm.height / img->num_subimages;
983 else
984 retval = false;
986 list = list->next;
989 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
990 /* Backdrop load scheme:
991 * 1) %X|filename|
992 * 2) load the backdrop from settings
994 if (wps_data->backdrop)
996 bool needed = wps_data->backdrop[0] != '-';
997 wps_data->backdrop = skin_backdrop_load(wps_data->backdrop,
998 bmpdir, curr_screen);
999 if (!wps_data->backdrop && needed)
1000 retval = false;
1002 #endif /* has backdrop support */
1004 return retval;
1007 static bool skin_load_fonts(struct wps_data *data)
1009 /* don't spit out after the first failue to aid debugging */
1010 bool success = true;
1011 struct skin_element *vp_list;
1012 int font_id;
1013 /* walk though each viewport and assign its font */
1014 for(vp_list = data->tree; vp_list; vp_list = vp_list->next)
1016 /* first, find the viewports that have a non-sys/ui-font font */
1017 struct skin_viewport *skin_vp =
1018 (struct skin_viewport*)vp_list->data;
1019 struct viewport *vp = &skin_vp->vp;
1022 if (vp->font <= FONT_UI)
1023 { /* the usual case -> built-in fonts */
1024 #ifdef HAVE_REMOTE_LCD
1025 if (vp->font == FONT_UI)
1026 vp->font += curr_screen;
1027 #endif
1028 continue;
1030 font_id = vp->font;
1032 /* now find the corresponding skin_font */
1033 struct skin_font *font = &skinfonts[font_id-FONT_FIRSTUSERFONT];
1034 if (!font->name)
1036 if (success)
1038 DEBUGF("font %d not specified\n", font_id);
1040 success = false;
1041 continue;
1044 /* load the font - will handle loading the same font again if
1045 * multiple viewports use the same */
1046 if (font->id < 0)
1048 char *dot = strchr(font->name, '.');
1049 *dot = '\0';
1050 font->id = skin_font_load(font->name);
1053 if (font->id < 0)
1055 DEBUGF("Unable to load font %d: '%s.fnt'\n",
1056 font_id, font->name);
1057 font->name = NULL; /* to stop trying to load it again if we fail */
1058 success = false;
1059 font->name = NULL;
1060 continue;
1063 /* finally, assign the font_id to the viewport */
1064 vp->font = font->id;
1066 return success;
1069 #endif /* HAVE_LCD_BITMAP */
1070 static int convert_viewport(struct wps_data *data, struct skin_element* element)
1072 struct skin_viewport *skin_vp =
1073 (struct skin_viewport *)skin_buffer_alloc(sizeof(struct skin_viewport));
1074 struct screen *display = &screens[curr_screen];
1076 if (!skin_vp)
1077 return CALLBACK_ERROR;
1079 skin_vp->hidden_flags = 0;
1080 skin_vp->label = VP_NO_LABEL;
1081 element->data = skin_vp;
1082 curr_vp = skin_vp;
1083 curr_viewport_element = element;
1085 viewport_set_defaults(&skin_vp->vp, curr_screen);
1087 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1088 skin_vp->start_fgcolour = skin_vp->vp.fg_pattern;
1089 skin_vp->start_bgcolour = skin_vp->vp.bg_pattern;
1090 #endif
1093 struct skin_tag_parameter *param = element->params;
1094 if (element->params_count == 0) /* default viewport */
1096 if (!data->tree) /* first viewport in the skin */
1097 data->tree = element;
1098 skin_vp->label = VP_DEFAULT_LABEL;
1099 return CALLBACK_OK;
1102 if (element->params_count == 6)
1104 if (element->tag->type == SKIN_TOKEN_UIVIEWPORT_LOAD)
1106 if (isdefault(param))
1108 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
1109 skin_vp->label = VP_INFO_LABEL|VP_DEFAULT_LABEL;
1111 else
1113 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
1114 skin_vp->label = VP_INFO_LABEL|param->data.text[0];
1117 else
1119 skin_vp->hidden_flags = VP_DRAW_HIDEABLE|VP_DRAW_HIDDEN;
1120 skin_vp->label = param->data.text[0];
1122 param++;
1124 /* x */
1125 if (!isdefault(param))
1127 skin_vp->vp.x = param->data.number;
1128 if (param->data.number < 0)
1129 skin_vp->vp.x += display->lcdwidth;
1131 param++;
1132 /* y */
1133 if (!isdefault(param))
1135 skin_vp->vp.y = param->data.number;
1136 if (param->data.number < 0)
1137 skin_vp->vp.y += display->lcdheight;
1139 param++;
1140 /* width */
1141 if (!isdefault(param))
1143 skin_vp->vp.width = param->data.number;
1144 if (param->data.number < 0)
1145 skin_vp->vp.width = (skin_vp->vp.width + display->lcdwidth) - skin_vp->vp.x;
1147 else
1149 skin_vp->vp.width = display->lcdwidth - skin_vp->vp.x;
1151 param++;
1152 /* height */
1153 if (!isdefault(param))
1155 skin_vp->vp.height = param->data.number;
1156 if (param->data.number < 0)
1157 skin_vp->vp.height = (skin_vp->vp.height + display->lcdheight) - skin_vp->vp.y;
1159 else
1161 skin_vp->vp.height = display->lcdheight - skin_vp->vp.y;
1163 param++;
1164 #ifdef HAVE_LCD_BITMAP
1165 /* font */
1166 if (!isdefault(param))
1168 skin_vp->vp.font = param->data.number;
1170 #endif
1172 return CALLBACK_OK;
1176 int skin_element_callback(struct skin_element* element, void* data)
1178 struct wps_data *wps_data = (struct wps_data *)data;
1179 struct wps_token *token;
1180 parse_function function = NULL;
1182 switch (element->type)
1184 /* IMPORTANT: element params are shared, so copy them if needed
1185 * or use then NOW, dont presume they have a long lifespan
1187 case TAG:
1189 token = (struct wps_token*)skin_buffer_alloc(sizeof(struct wps_token));
1190 memset(token, 0, sizeof(*token));
1191 token->type = element->tag->type;
1193 if ((element->tag->flags&SKIN_REFRESH_ALL) == SKIN_RTC_REFRESH)
1195 #ifdef CONFIG_RTC
1196 curr_line->update_mode |= SKIN_REFRESH_DYNAMIC;
1197 #else
1198 curr_line->update_mode |= SKIN_REFRESH_STATIC;
1199 #endif
1201 else
1202 curr_line->update_mode |= element->tag->flags&SKIN_REFRESH_ALL;
1204 element->data = token;
1206 /* Some tags need special handling for the tag, so add them here */
1207 switch (token->type)
1209 case SKIN_TOKEN_ALIGN_LANGDIRECTION:
1210 follow_lang_direction = 2;
1211 break;
1212 case SKIN_TOKEN_PROGRESSBAR:
1213 case SKIN_TOKEN_VOLUME:
1214 case SKIN_TOKEN_BATTERY_PERCENT:
1215 case SKIN_TOKEN_PLAYER_PROGRESSBAR:
1216 function = parse_progressbar_tag;
1217 break;
1218 case SKIN_TOKEN_SUBLINE_TIMEOUT:
1219 case SKIN_TOKEN_BUTTON_VOLUME:
1220 case SKIN_TOKEN_TRACK_STARTING:
1221 case SKIN_TOKEN_TRACK_ENDING:
1222 case SKIN_TOKEN_LASTTOUCH:
1223 function = parse_timeout_tag;
1224 break;
1225 #ifdef HAVE_LCD_BITMAP
1226 case SKIN_TOKEN_DISABLE_THEME:
1227 case SKIN_TOKEN_ENABLE_THEME:
1228 case SKIN_TOKEN_DRAW_INBUILTBAR:
1229 function = parse_statusbar_tags;
1230 break;
1231 #endif
1232 case SKIN_TOKEN_FILE_DIRECTORY:
1233 token->value.i = element->params[0].data.number;
1234 break;
1235 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1236 case SKIN_TOKEN_VIEWPORT_FGCOLOUR:
1237 case SKIN_TOKEN_VIEWPORT_BGCOLOUR:
1238 function = parse_viewportcolour;
1239 break;
1240 case SKIN_TOKEN_IMAGE_BACKDROP:
1241 function = parse_image_special;
1242 break;
1243 #endif
1244 case SKIN_TOKEN_TRANSLATEDSTRING:
1245 case SKIN_TOKEN_SETTING:
1246 function = parse_setting_and_lang;
1247 break;
1248 #ifdef HAVE_LCD_BITMAP
1249 case SKIN_TOKEN_VIEWPORT_CUSTOMLIST:
1250 function = parse_playlistview;
1251 break;
1252 case SKIN_TOKEN_LOAD_FONT:
1253 function = parse_font_load;
1254 break;
1255 case SKIN_TOKEN_VIEWPORT_ENABLE:
1256 case SKIN_TOKEN_UIVIEWPORT_ENABLE:
1257 token->value.i = element->params[0].data.text[0];
1258 break;
1259 case SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY:
1260 function = parse_image_display;
1261 break;
1262 case SKIN_TOKEN_IMAGE_PRELOAD:
1263 case SKIN_TOKEN_IMAGE_DISPLAY:
1264 function = parse_image_load;
1265 break;
1266 #endif
1267 #ifdef HAVE_TOUCHSCREEN
1268 case SKIN_TOKEN_TOUCHREGION:
1269 function = parse_touchregion;
1270 break;
1271 #endif
1272 #ifdef HAVE_ALBUMART
1273 case SKIN_TOKEN_ALBUMART_DISPLAY:
1274 if (wps_data->albumart)
1275 wps_data->albumart->vp = &curr_vp->vp;
1276 break;
1277 case SKIN_TOKEN_ALBUMART_LOAD:
1278 function = parse_albumart_load;
1279 break;
1280 #endif
1281 default:
1282 break;
1284 if (function)
1286 if (function(element, token, wps_data) < 0)
1287 return CALLBACK_ERROR;
1289 /* tags that start with 'F', 'I' or 'D' are for the next file */
1290 if ( *(element->tag->name) == 'I' || *(element->tag->name) == 'F' ||
1291 *(element->tag->name) == 'D')
1292 token->next = true;
1293 if (follow_lang_direction > 0 )
1294 follow_lang_direction--;
1295 break;
1297 case VIEWPORT:
1298 return convert_viewport(wps_data, element);
1299 case LINE:
1301 struct line *line =
1302 (struct line *)skin_buffer_alloc(sizeof(struct line));
1303 line->update_mode = SKIN_REFRESH_STATIC;
1304 line->timeout = DEFAULT_SUBLINE_TIME_MULTIPLIER * TIMEOUT_UNIT;
1305 curr_line = line;
1306 element->data = line;
1308 break;
1309 case LINE_ALTERNATOR:
1311 struct line_alternator *alternator =
1312 (struct line_alternator *)skin_buffer_alloc(sizeof(struct line_alternator));
1313 alternator->current_line = 0;
1314 #ifndef __PCTOOL__
1315 alternator->last_change_tick = current_tick;
1316 #endif
1317 element->data = alternator;
1319 break;
1320 case CONDITIONAL:
1322 struct conditional *conditional =
1323 (struct conditional *)skin_buffer_alloc(sizeof(struct conditional));
1324 conditional->last_value = -1;
1325 conditional->token = element->data;
1326 element->data = conditional;
1327 if (!check_feature_tag(element->tag->type))
1329 return FEATURE_NOT_AVAILABLE;
1331 return CALLBACK_OK;
1333 case TEXT:
1334 curr_line->update_mode |= SKIN_REFRESH_STATIC;
1335 break;
1336 default:
1337 break;
1339 return CALLBACK_OK;
1342 /* to setup up the wps-data from a format-buffer (isfile = false)
1343 from a (wps-)file (isfile = true)*/
1344 bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
1345 const char *buf, bool isfile)
1347 char *wps_buffer = NULL;
1348 if (!wps_data || !buf)
1349 return false;
1350 #ifdef HAVE_ALBUMART
1351 int status;
1352 struct mp3entry *curtrack;
1353 long offset;
1354 struct skin_albumart old_aa = {.state = WPS_ALBUMART_NONE};
1355 if (wps_data->albumart)
1357 old_aa.state = wps_data->albumart->state;
1358 old_aa.height = wps_data->albumart->height;
1359 old_aa.width = wps_data->albumart->width;
1361 #endif
1362 #ifdef HAVE_LCD_BITMAP
1363 int i;
1364 for (i=0;i<MAXUSERFONTS;i++)
1366 skinfonts[i].id = -1;
1367 skinfonts[i].name = NULL;
1369 #endif
1370 #ifdef DEBUG_SKIN_ENGINE
1371 if (isfile && debug_wps)
1373 DEBUGF("\n=====================\nLoading '%s'\n=====================\n", buf);
1375 #endif
1378 skin_data_reset(wps_data);
1379 wps_data->wps_loaded = false;
1380 curr_screen = screen;
1381 curr_line = NULL;
1382 curr_vp = NULL;
1383 curr_viewport_element = NULL;
1385 if (isfile)
1387 int fd = open_utf8(buf, O_RDONLY);
1389 if (fd < 0)
1390 return false;
1392 /* get buffer space from the plugin buffer */
1393 size_t buffersize = 0;
1394 wps_buffer = (char *)plugin_get_buffer(&buffersize);
1396 if (!wps_buffer)
1397 return false;
1399 /* copy the file's content to the buffer for parsing,
1400 ensuring that every line ends with a newline char. */
1401 unsigned int start = 0;
1402 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
1404 start += strlen(wps_buffer + start);
1405 if (start < buffersize - 1)
1407 wps_buffer[start++] = '\n';
1408 wps_buffer[start] = 0;
1411 close(fd);
1412 if (start <= 0)
1413 return false;
1415 else
1417 wps_buffer = (char*)buf;
1419 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1420 wps_data->backdrop = "-";
1421 #endif
1422 /* parse the skin source */
1423 skin_buffer_save_position();
1424 wps_data->tree = skin_parse(wps_buffer, skin_element_callback, wps_data);
1425 if (!wps_data->tree) {
1426 skin_data_reset(wps_data);
1427 skin_buffer_restore_position();
1428 return false;
1431 #ifdef HAVE_LCD_BITMAP
1432 char bmpdir[MAX_PATH];
1433 if (isfile)
1435 /* get the bitmap dir */
1436 char *dot = strrchr(buf, '.');
1437 strlcpy(bmpdir, buf, dot - buf + 1);
1439 else
1441 snprintf(bmpdir, MAX_PATH, "%s", BACKDROP_DIR);
1443 /* load the bitmaps that were found by the parsing */
1444 if (!load_skin_bitmaps(wps_data, bmpdir) ||
1445 !skin_load_fonts(wps_data))
1447 skin_data_reset(wps_data);
1448 skin_buffer_restore_position();
1449 return false;
1451 #endif
1452 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
1453 status = audio_status();
1454 if (status & AUDIO_STATUS_PLAY)
1456 struct skin_albumart *aa = wps_data->albumart;
1457 if (aa && ((aa->state && !old_aa.state) ||
1458 (aa->state &&
1459 (((old_aa.height != aa->height) ||
1460 (old_aa.width != aa->width))))))
1462 curtrack = audio_current_track();
1463 offset = curtrack->offset;
1464 audio_stop();
1465 if (!(status & AUDIO_STATUS_PAUSE))
1466 audio_play(offset);
1469 #endif
1470 wps_data->wps_loaded = true;
1471 #ifdef DEBUG_SKIN_ENGINE
1472 // if (isfile && debug_wps)
1473 // debug_skin_usage();
1474 #endif
1475 return true;