1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
32 #include "skin_buffer.h"
33 #include "skin_parser.h"
34 #include "tag_table.h"
44 #define lang_is_rtl() (false)
56 #include "wps_internals.h"
57 #include "skin_engine.h"
59 #include "settings_list.h"
64 #include "skin_fonts.h"
66 #ifdef HAVE_LCD_BITMAP
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
)
111 struct skin_token_list
*t
= *list
;
118 /* traverse the image linked-list for an image */
119 struct gui_img
* find_image(const char *label
, struct wps_data
*data
)
121 struct skin_token_list
*list
= data
->images
;
124 struct gui_img
*img
= (struct gui_img
*)list
->token
->value
.data
;
125 if (!strcmp(img
->label
,label
))
134 /* traverse the viewport linked list for a viewport */
135 struct skin_viewport
* find_viewport(const char *label
, bool uivp
, struct wps_data
*data
)
137 struct skin_element
*list
= data
->tree
;
140 struct skin_viewport
*vp
= (struct skin_viewport
*)list
->data
;
141 if (vp
->label
&& (vp
->is_infovp
== uivp
) &&
142 !strcmp(vp
->label
, label
))
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
,
159 struct skin_token_list
*llitem
=
160 (struct skin_token_list
*)skin_buffer_alloc(sizeof(struct skin_token_list
));
162 token
= (struct wps_token
*)skin_buffer_alloc(sizeof(struct wps_token
));
163 if (!llitem
|| !token
)
166 llitem
->token
= token
;
168 llitem
->token
->value
.data
= token_data
;
172 static int parse_statusbar_tags(struct skin_element
* element
,
173 struct wps_token
*token
,
174 struct wps_data
*wps_data
)
177 if (token
->type
== SKIN_TOKEN_DRAW_INBUILTBAR
)
179 token
->value
.data
= (void*)&curr_vp
->vp
;
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
);
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
;
208 static int get_image_id(int c
)
210 if(c
>= 'a' && c
<= 'z')
212 else if(c
>= 'A' && c
<= 'Z')
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
);
226 static int parse_image_display(struct skin_element
*element
,
227 struct wps_token
*token
,
228 struct wps_data
*wps_data
)
230 char *label
= element
->params
[0].data
.text
;
231 char sublabel
= '\0';
234 struct image_display
*id
= skin_buffer_alloc(sizeof(struct image_display
));
236 if (element
->params_count
== 1 && strlen(label
) <= 2)
238 /* backwards compatability. Allow %xd(Aa) to still work */
243 img
= find_image(label
, wps_data
);
246 return WPS_ERROR_INVALID_PARAM
;
251 img
->using_preloaded_icons
= false;
252 if (!strcmp(img
->bm
.data
, "__list_icons__"))
254 img
->num_subimages
= Icon_Last_Themeable
;
255 img
->using_preloaded_icons
= true;
256 token
->type
= SKIN_TOKEN_IMAGE_DISPLAY_LISTICON
;
259 if (element
->params_count
> 1)
261 if (element
->params
[1].type
== CODE
)
262 id
->token
= element
->params
[1].data
.code
->data
;
263 /* specify a number. 1 being the first subimage (i.e top) NOT 0 */
264 else if (element
->params
[1].type
== INTEGER
)
265 id
->subimage
= element
->params
[1].data
.number
- 1;
266 if (element
->params_count
> 2)
267 id
->offset
= element
->params
[2].data
.number
;
271 if ((subimage
= get_image_id(sublabel
)) != -1)
273 if (subimage
>= img
->num_subimages
)
274 return WPS_ERROR_INVALID_PARAM
;
275 id
->subimage
= subimage
;
280 token
->value
.data
= id
;
284 static int parse_image_load(struct skin_element
*element
,
285 struct wps_token
*token
,
286 struct wps_data
*wps_data
)
288 const char* filename
;
293 /* format: %x(n,filename.bmp,x,y)
294 or %xl(n,filename.bmp,x,y)
295 or %xl(n,filename.bmp,x,y,num_subimages)
298 id
= element
->params
[0].data
.text
;
299 filename
= element
->params
[1].data
.text
;
300 x
= element
->params
[2].data
.number
;
301 y
= element
->params
[3].data
.number
;
303 /* check the image number and load state */
304 if(find_image(id
, wps_data
))
306 /* Invalid image ID */
307 return WPS_ERROR_INVALID_PARAM
;
309 img
= (struct gui_img
*)skin_buffer_alloc(sizeof(struct gui_img
));
311 return WPS_ERROR_INVALID_PARAM
;
312 /* save a pointer to the filename */
313 img
->bm
.data
= (char*)filename
;
317 img
->num_subimages
= 1;
318 img
->always_display
= false;
321 /* save current viewport */
322 img
->vp
= &curr_vp
->vp
;
324 if (token
->type
== SKIN_TOKEN_IMAGE_DISPLAY
)
326 img
->always_display
= true;
328 else if (element
->params_count
== 5)
330 img
->num_subimages
= element
->params
[4].data
.number
;
331 if (img
->num_subimages
<= 0)
332 return WPS_ERROR_INVALID_PARAM
;
335 struct skin_token_list
*item
=
336 (struct skin_token_list
*)new_skin_token_list_item(NULL
, img
);
338 return WPS_ERROR_INVALID_PARAM
;
339 add_to_ll_chain(&wps_data
->images
, item
);
344 int id
; /* the id from font_load */
345 char *name
; /* filename without path and extension */
346 int glyphs
; /* how many glyphs to reserve room for */
348 static struct skin_font skinfonts
[MAXUSERFONTS
];
349 static int parse_font_load(struct skin_element
*element
,
350 struct wps_token
*token
,
351 struct wps_data
*wps_data
)
353 (void)wps_data
; (void)token
;
354 int id
= element
->params
[0].data
.number
;
355 char *filename
= element
->params
[1].data
.text
;
359 if(element
->params_count
> 2)
360 glyphs
= element
->params
[2].data
.number
;
362 glyphs
= GLYPHS_TO_CACHE
;
363 #if defined(DEBUG) || defined(SIMULATOR)
364 if (skinfonts
[id
-FONT_FIRSTUSERFONT
].name
!= NULL
)
366 DEBUGF("font id %d already being used\n", id
);
369 /* make sure the filename contains .fnt,
370 * we dont actually use it, but require it anyway */
371 ptr
= strchr(filename
, '.');
372 if (!ptr
|| strncmp(ptr
, ".fnt", 4))
373 return WPS_ERROR_INVALID_PARAM
;
374 skinfonts
[id
-FONT_FIRSTUSERFONT
].id
= -1;
375 skinfonts
[id
-FONT_FIRSTUSERFONT
].name
= filename
;
376 skinfonts
[id
-FONT_FIRSTUSERFONT
].glyphs
= glyphs
;
382 #ifdef HAVE_LCD_BITMAP
384 static int parse_playlistview(struct skin_element
*element
,
385 struct wps_token
*token
,
386 struct wps_data
*wps_data
)
389 struct playlistviewer
*viewer
=
390 (struct playlistviewer
*)skin_buffer_alloc(sizeof(struct playlistviewer
));
392 return WPS_ERROR_INVALID_PARAM
;
393 viewer
->vp
= &curr_vp
->vp
;
394 viewer
->show_icons
= true;
395 viewer
->start_offset
= element
->params
[0].data
.number
;
396 viewer
->line
= element
->params
[1].data
.code
;
398 token
->value
.data
= (void*)viewer
;
404 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
406 static int parse_viewportcolour(struct skin_element
*element
,
407 struct wps_token
*token
,
408 struct wps_data
*wps_data
)
411 struct skin_tag_parameter
*param
= element
->params
;
412 struct viewport_colour
*colour
=
413 (struct viewport_colour
*)skin_buffer_alloc(sizeof(struct viewport_colour
));
416 if (isdefault(param
))
418 colour
->colour
= get_viewport_default_colour(curr_screen
,
419 token
->type
== SKIN_TOKEN_VIEWPORT_FGCOLOUR
);
423 if (!parse_color(curr_screen
, param
->data
.text
, &colour
->colour
))
426 colour
->vp
= &curr_vp
->vp
;
427 token
->value
.data
= colour
;
428 if (element
->line
== curr_viewport_element
->line
)
430 if (token
->type
== SKIN_TOKEN_VIEWPORT_FGCOLOUR
)
432 curr_vp
->start_fgcolour
= colour
->colour
;
433 curr_vp
->vp
.fg_pattern
= colour
->colour
;
437 curr_vp
->start_bgcolour
= colour
->colour
;
438 curr_vp
->vp
.bg_pattern
= colour
->colour
;
444 static int parse_image_special(struct skin_element
*element
,
445 struct wps_token
*token
,
446 struct wps_data
*wps_data
)
448 (void)wps_data
; /* kill warning */
453 if (token
->type
== SKIN_TOKEN_IMAGE_BACKDROP
)
455 if (isdefault(&element
->params
[0]))
461 filename
= element
->params
[0].data
.text
;
462 /* format: %X(filename.bmp) or %X(d) */
463 if (!strcmp(filename
, "d"))
466 wps_data
->backdrop
= filename
;
474 #endif /* HAVE_LCD_BITMAP */
476 static int parse_setting_and_lang(struct skin_element
*element
,
477 struct wps_token
*token
,
478 struct wps_data
*wps_data
)
480 /* NOTE: both the string validations that happen in here will
481 * automatically PASS on checkwps because its too hard to get
482 * settings_list.c and english.lang built for it.
483 * If that ever changes remove the #ifndef __PCTOOL__'s here
486 char *temp
= element
->params
[0].data
.text
;
489 if (token
->type
== SKIN_TOKEN_TRANSLATEDSTRING
)
492 i
= lang_english_to_id(temp
);
494 return WPS_ERROR_INVALID_PARAM
;
499 /* Find the setting */
500 for (i
=0; i
<nb_settings
; i
++)
501 if (settings
[i
].cfg_name
&&
502 !strcmp(settings
[i
].cfg_name
, temp
))
505 if (i
== nb_settings
)
506 return WPS_ERROR_INVALID_PARAM
;
509 /* Store the setting number */
513 static int parse_logical_if(struct skin_element
*element
,
514 struct wps_token
*token
,
515 struct wps_data
*wps_data
)
518 char *op
= element
->params
[1].data
.text
;
519 struct logical_if
*lif
= skin_buffer_alloc(sizeof(struct logical_if
));
522 token
->value
.data
= lif
;
523 lif
->token
= element
->params
[0].data
.code
->data
;
525 if (!strcmp(op
, "="))
527 if (!strcmp(op
, "!="))
528 lif
->op
= IF_NOTEQUALS
;
529 if (!strcmp(op
, "<"))
530 lif
->op
= IF_LESSTHAN
;
531 if (!strcmp(op
, "<="))
532 lif
->op
= IF_LESSTHAN_EQ
;
533 if (!strcmp(op
, ">"))
534 lif
->op
= IF_GREATERTHAN
;
535 if (!strcmp(op
, ">="))
536 lif
->op
= IF_GREATERTHAN_EQ
;
538 memcpy(&lif
->operand
, &element
->params
[2], sizeof(lif
->operand
));
539 if (element
->params_count
> 3)
540 lif
->num_options
= element
->params
[3].data
.number
;
542 lif
->num_options
= TOKEN_VALUE_ONLY
;
546 static int parse_timeout_tag(struct skin_element
*element
,
547 struct wps_token
*token
,
548 struct wps_data
*wps_data
)
552 if (element
->params_count
== 0)
556 case SKIN_TOKEN_SUBLINE_TIMEOUT
:
558 case SKIN_TOKEN_BUTTON_VOLUME
:
559 case SKIN_TOKEN_TRACK_STARTING
:
560 case SKIN_TOKEN_TRACK_ENDING
:
561 case SKIN_TOKEN_LASTTOUCH
:
569 val
= element
->params
[0].data
.number
;
570 token
->value
.i
= val
* TIMEOUT_UNIT
;
571 if (token
->type
== SKIN_TOKEN_SUBLINE_TIMEOUT
)
572 curr_line
->timeout
= token
->value
.i
;
576 static int parse_progressbar_tag(struct skin_element
* element
,
577 struct wps_token
*token
,
578 struct wps_data
*wps_data
)
580 #ifdef HAVE_LCD_BITMAP
581 struct progressbar
*pb
;
582 struct skin_token_list
*item
;
583 struct viewport
*vp
= &curr_vp
->vp
;
584 struct skin_tag_parameter
*param
= element
->params
;
587 if (element
->params_count
== 0 &&
588 element
->tag
->type
!= SKIN_TOKEN_PROGRESSBAR
)
589 return 0; /* nothing to do */
590 pb
= (struct progressbar
*)skin_buffer_alloc(sizeof(struct progressbar
));
592 token
->value
.data
= pb
;
595 return WPS_ERROR_INVALID_PARAM
;
597 pb
->have_bitmap_pb
= false;
598 pb
->bm
.data
= NULL
; /* no bitmap specified */
599 pb
->follow_lang_direction
= follow_lang_direction
> 0;
602 pb
->invert_fill_direction
= false;
603 pb
->horizontal
= true;
605 if (element
->params_count
== 0)
608 pb
->width
= vp
->width
;
609 pb
->height
= SYSFONT_HEIGHT
-2;
610 pb
->y
= -1; /* Will be computed during the rendering */
611 pb
->type
= element
->tag
->type
;
615 item
= new_skin_token_list_item(token
, pb
);
618 add_to_ll_chain(&wps_data
->progressbars
, item
);
620 /* (x,y,width,height,filename) */
621 if (!isdefault(param
))
622 pb
->x
= param
->data
.number
;
627 if (!isdefault(param
))
628 pb
->y
= param
->data
.number
;
630 pb
->y
= -1; /* computed at rendering */
633 if (!isdefault(param
))
634 pb
->width
= param
->data
.number
;
636 pb
->width
= vp
->width
- pb
->x
;
639 if (!isdefault(param
))
641 /* A zero height makes no sense - reject it */
642 if (param
->data
.number
== 0)
643 return WPS_ERROR_INVALID_PARAM
;
645 pb
->height
= param
->data
.number
;
649 if (vp
->font
> FONT_UI
)
650 pb
->height
= -1; /* calculate at display time */
654 pb
->height
= font_get(vp
->font
)->height
;
661 if (!isdefault(param
))
662 pb
->bm
.data
= param
->data
.text
;
665 pb
->horizontal
= pb
->width
> pb
->height
;
666 while (curr_param
< element
->params_count
)
669 if (!strcmp(param
->data
.text
, "invert"))
670 pb
->invert_fill_direction
= true;
671 else if (!strcmp(param
->data
.text
, "nofill"))
673 else if (!strcmp(param
->data
.text
, "slider"))
675 if (curr_param
+1 < element
->params_count
)
679 pb
->slider
= find_image(param
->data
.text
, wps_data
);
684 else if (!strcmp(param
->data
.text
, "vertical"))
686 pb
->horizontal
= false;
687 if (isdefault(&element
->params
[3]))
688 pb
->height
= vp
->height
- pb
->x
;
690 else if (!strcmp(param
->data
.text
, "horizontal"))
691 pb
->horizontal
= true;
697 if (token
->type
== SKIN_TOKEN_VOLUME
)
698 token
->type
= SKIN_TOKEN_VOLUMEBAR
;
699 else if (token
->type
== SKIN_TOKEN_BATTERY_PERCENT
)
700 token
->type
= SKIN_TOKEN_BATTERY_PERCENTBAR
;
701 pb
->type
= token
->type
;
707 if (token
->type
== SKIN_TOKEN_PROGRESSBAR
||
708 token
->type
== SKIN_TOKEN_PLAYER_PROGRESSBAR
)
710 wps_data
->full_line_progressbar
=
711 token
->type
== SKIN_TOKEN_PLAYER_PROGRESSBAR
;
719 static int parse_albumart_load(struct skin_element
* element
,
720 struct wps_token
*token
,
721 struct wps_data
*wps_data
)
723 struct dim dimensions
;
725 bool swap_for_rtl
= lang_is_rtl() && follow_lang_direction
;
726 struct skin_albumart
*aa
=
727 (struct skin_albumart
*)skin_buffer_alloc(sizeof(struct skin_albumart
));
728 (void)token
; /* silence warning */
732 /* reset albumart info in wps */
735 aa
->xalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
736 aa
->yalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
738 aa
->x
= element
->params
[0].data
.number
;
739 aa
->y
= element
->params
[1].data
.number
;
740 aa
->width
= element
->params
[2].data
.number
;
741 aa
->height
= element
->params
[3].data
.number
;
743 aa
->vp
= &curr_vp
->vp
;
744 aa
->draw_handle
= -1;
746 /* if we got here, we parsed everything ok .. ! */
749 else if (aa
->width
> LCD_WIDTH
)
750 aa
->width
= LCD_WIDTH
;
754 else if (aa
->height
> LCD_HEIGHT
)
755 aa
->height
= LCD_HEIGHT
;
758 aa
->x
= LCD_WIDTH
- (aa
->x
+ aa
->width
);
760 aa
->state
= WPS_ALBUMART_LOAD
;
761 wps_data
->albumart
= aa
;
763 dimensions
.width
= aa
->width
;
764 dimensions
.height
= aa
->height
;
766 albumart_slot
= playback_claim_aa_slot(&dimensions
);
768 if (0 <= albumart_slot
)
769 wps_data
->playback_aa_slot
= albumart_slot
;
771 if (element
->params_count
> 4 && !isdefault(&element
->params
[4]))
773 switch (*element
->params
[4].data
.text
)
778 aa
->xalign
= WPS_ALBUMART_ALIGN_RIGHT
;
780 aa
->xalign
= WPS_ALBUMART_ALIGN_LEFT
;
784 aa
->xalign
= WPS_ALBUMART_ALIGN_CENTER
;
789 aa
->xalign
= WPS_ALBUMART_ALIGN_LEFT
;
791 aa
->xalign
= WPS_ALBUMART_ALIGN_RIGHT
;
795 if (element
->params_count
> 5 && !isdefault(&element
->params
[5]))
797 switch (*element
->params
[5].data
.text
)
801 aa
->yalign
= WPS_ALBUMART_ALIGN_TOP
;
805 aa
->yalign
= WPS_ALBUMART_ALIGN_CENTER
;
809 aa
->yalign
= WPS_ALBUMART_ALIGN_BOTTOM
;
816 #endif /* HAVE_ALBUMART */
818 #ifdef HAVE_TOUCHSCREEN
820 struct touchaction
{const char* s
; int action
;};
821 static const struct touchaction touchactions
[] = {
822 /* generic actions, convert to screen actions on use */
823 {"prev", ACTION_STD_PREV
}, {"next", ACTION_STD_NEXT
},
824 {"rwd", ACTION_STD_PREVREPEAT
}, {"ffwd", ACTION_STD_NEXTREPEAT
},
825 {"hotkey", ACTION_STD_HOTKEY
}, {"select", ACTION_STD_OK
},
826 {"menu", ACTION_STD_MENU
}, {"cancel", ACTION_STD_CANCEL
},
827 {"contextmenu", ACTION_STD_CONTEXT
},{"quickscreen", ACTION_STD_QUICKSCREEN
},
828 /* not really WPS specific, but no equivilant ACTION_STD_* */
829 {"voldown", ACTION_WPS_VOLDOWN
}, {"volup", ACTION_WPS_VOLUP
},
831 /* generic settings changers */
832 {"setting_inc", ACTION_SETTINGS_INC
}, {"setting_dec", ACTION_SETTINGS_DEC
},
834 /* WPS specific actions */
835 {"browse", ACTION_WPS_BROWSE
},
836 {"play", ACTION_WPS_PLAY
}, {"stop", ACTION_WPS_STOP
},
837 {"shuffle", ACTION_TOUCH_SHUFFLE
}, {"repmode", ACTION_TOUCH_REPMODE
},
838 {"pitch", ACTION_WPS_PITCHSCREEN
}, {"playlist", ACTION_WPS_VIEW_PLAYLIST
},
841 /* FM screen actions */
842 /* Also allow browse, play, stop from WPS codes */
843 {"mode", ACTION_FM_MODE
}, {"record", ACTION_FM_RECORD
},
844 {"presets", ACTION_FM_PRESET
},
848 static int parse_touchregion(struct skin_element
*element
,
849 struct wps_token
*token
,
850 struct wps_data
*wps_data
)
854 struct touchregion
*region
= NULL
;
856 const char pb_string
[] = "progressbar";
857 const char vol_string
[] = "volume";
860 /* format: %T(x,y,width,height,action)
861 * if action starts with & the area must be held to happen
863 * play - play/pause playback
864 * stop - stop playback, exit the wps
867 * ffwd - seek forward
868 * rwd - seek backwards
869 * menu - go back to the main menu
870 * browse - go back to the file/db browser
871 * shuffle - toggle shuffle mode
872 * repmode - cycle the repeat mode
873 * quickscreen - go into the quickscreen
874 * contextmenu - open the context menu
875 * playlist - go into the playlist
876 * pitch - go into the pitchscreen
877 * volup - increase volume by one step
878 * voldown - decrease volume by one step
882 region
= (struct touchregion
*)skin_buffer_alloc(sizeof(struct touchregion
));
884 return WPS_ERROR_INVALID_PARAM
;
886 /* should probably do some bounds checking here with the viewport... but later */
887 region
->action
= ACTION_NONE
;
888 region
->x
= element
->params
[0].data
.number
;
889 region
->y
= element
->params
[1].data
.number
;
890 region
->width
= element
->params
[2].data
.number
;
891 region
->height
= element
->params
[3].data
.number
;
892 region
->wvp
= curr_vp
;
893 region
->armed
= false;
894 region
->reverse_bar
= false;
895 region
->extradata
= NULL
;
896 action
= element
->params
[4].data
.text
;
898 strcpy(temp
, action
);
903 region
->reverse_bar
= true;
907 if(!strcmp(pb_string
, action
))
908 region
->type
= WPS_TOUCHREGION_SCROLLBAR
;
909 else if(!strcmp(vol_string
, action
))
910 region
->type
= WPS_TOUCHREGION_VOLUME
;
913 region
->type
= WPS_TOUCHREGION_ACTION
;
918 region
->repeat
= true;
921 region
->repeat
= false;
923 imax
= ARRAYLEN(touchactions
);
924 for (i
= 0; i
< imax
; i
++)
926 /* try to match with one of our touchregion screens */
927 if (!strcmp(touchactions
[i
].s
, action
))
929 region
->action
= touchactions
[i
].action
;
930 if (region
->action
== ACTION_SETTINGS_INC
||
931 region
->action
== ACTION_SETTINGS_DEC
)
933 if (element
->params_count
< 6)
935 return WPS_ERROR_INVALID_PARAM
;
939 char *name
= element
->params
[5].data
.text
;
941 /* Find the setting */
942 for (j
=0; j
<nb_settings
; j
++)
943 if (settings
[j
].cfg_name
&&
944 !strcmp(settings
[j
].cfg_name
, name
))
947 return WPS_ERROR_INVALID_PARAM
;
948 region
->extradata
= (void*)&settings
[j
];
954 if (region
->action
== ACTION_NONE
)
955 return WPS_ERROR_INVALID_PARAM
;
957 struct skin_token_list
*item
= new_skin_token_list_item(NULL
, region
);
959 return WPS_ERROR_INVALID_PARAM
;
960 add_to_ll_chain(&wps_data
->touchregions
, item
);
965 static bool check_feature_tag(const int type
)
969 case SKIN_TOKEN_RTC_PRESENT
:
975 case SKIN_TOKEN_HAVE_RECORDING
:
976 #ifdef HAVE_RECORDING
981 case SKIN_TOKEN_HAVE_TUNER
:
983 if (radio_hardware_present())
989 case SKIN_TOKEN_HAVE_RDS
:
994 #endif /* HAVE_RDS_CAP */
995 #endif /* CONFIG_TUNER */
996 default: /* not a tag we care about, just don't skip */
1002 * initial setup of wps_data; does reset everything
1003 * except fields which need to survive, i.e.
1006 static void skin_data_reset(struct wps_data
*wps_data
)
1008 wps_data
->tree
= NULL
;
1009 #ifdef HAVE_LCD_BITMAP
1010 wps_data
->images
= NULL
;
1011 wps_data
->progressbars
= NULL
;
1013 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1014 if (wps_data
->backdrop_id
>= 0)
1015 skin_backdrop_unload(wps_data
->backdrop_id
);
1016 wps_data
->backdrop
= NULL
;
1018 #ifdef HAVE_TOUCHSCREEN
1019 wps_data
->touchregions
= NULL
;
1021 #ifdef HAVE_ALBUMART
1022 wps_data
->albumart
= NULL
;
1023 if (wps_data
->playback_aa_slot
>= 0)
1025 playback_release_aa_slot(wps_data
->playback_aa_slot
);
1026 wps_data
->playback_aa_slot
= -1;
1030 #ifdef HAVE_LCD_BITMAP
1031 wps_data
->peak_meter_enabled
= false;
1032 wps_data
->wps_sb_tag
= false;
1033 wps_data
->show_sb_on_wps
= false;
1034 #else /* HAVE_LCD_CHARCELLS */
1037 for (i
= 0; i
< 8; i
++)
1039 wps_data
->wps_progress_pat
[i
] = 0;
1041 wps_data
->full_line_progressbar
= false;
1043 wps_data
->wps_loaded
= false;
1046 #ifdef HAVE_LCD_BITMAP
1047 static bool load_skin_bmp(struct wps_data
*wps_data
, struct bitmap
*bitmap
, char* bmpdir
)
1049 (void)wps_data
; /* only needed for remote targets */
1050 char img_path
[MAX_PATH
];
1052 get_image_filename(bitmap
->data
, bmpdir
,
1053 img_path
, sizeof(img_path
));
1055 /* load the image */
1057 #ifdef HAVE_REMOTE_LCD
1058 if (curr_screen
== SCREEN_REMOTE
)
1059 format
= FORMAT_ANY
|FORMAT_REMOTE
;
1062 format
= FORMAT_ANY
|FORMAT_TRANSPARENT
;
1064 fd
= open(img_path
, O_RDONLY
);
1067 size_t buf_size
= read_bmp_file(img_path
, bitmap
, 0,
1068 format
|FORMAT_RETURN_SIZE
, NULL
);
1069 char* imgbuf
= (char*)skin_buffer_alloc(buf_size
);
1075 lseek(fd
, 0, SEEK_SET
);
1076 bitmap
->data
= imgbuf
;
1077 int ret
= read_bmp_fd(fd
, bitmap
, buf_size
, format
, NULL
);
1086 /* Abort if we can't load an image */
1087 DEBUGF("Couldn't load '%s'\n", img_path
);
1092 static bool load_skin_bitmaps(struct wps_data
*wps_data
, char *bmpdir
)
1094 struct skin_token_list
*list
;
1095 bool retval
= true; /* return false if a single image failed to load */
1096 /* do the progressbars */
1097 list
= wps_data
->progressbars
;
1100 struct progressbar
*pb
= (struct progressbar
*)list
->token
->value
.data
;
1103 pb
->have_bitmap_pb
= load_skin_bmp(wps_data
, &pb
->bm
, bmpdir
);
1104 if (!pb
->have_bitmap_pb
) /* no success */
1109 /* regular images */
1110 list
= wps_data
->images
;
1113 struct gui_img
*img
= (struct gui_img
*)list
->token
->value
.data
;
1116 if (img
->using_preloaded_icons
)
1119 list
->token
->type
= SKIN_TOKEN_IMAGE_DISPLAY_LISTICON
;
1123 img
->loaded
= load_skin_bmp(wps_data
, &img
->bm
, bmpdir
);
1125 img
->subimage_height
= img
->bm
.height
/ img
->num_subimages
;
1133 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1134 wps_data
->backdrop_id
= skin_backdrop_assign(wps_data
->backdrop
, bmpdir
, curr_screen
);
1135 #endif /* has backdrop support */
1139 static bool skin_load_fonts(struct wps_data
*data
)
1141 /* don't spit out after the first failue to aid debugging */
1142 bool success
= true;
1143 struct skin_element
*vp_list
;
1145 /* walk though each viewport and assign its font */
1146 for(vp_list
= data
->tree
; vp_list
; vp_list
= vp_list
->next
)
1148 /* first, find the viewports that have a non-sys/ui-font font */
1149 struct skin_viewport
*skin_vp
=
1150 (struct skin_viewport
*)vp_list
->data
;
1151 struct viewport
*vp
= &skin_vp
->vp
;
1154 if (vp
->font
<= FONT_UI
)
1155 { /* the usual case -> built-in fonts */
1156 #ifdef HAVE_REMOTE_LCD
1157 if (vp
->font
== FONT_UI
)
1158 vp
->font
+= curr_screen
;
1164 /* now find the corresponding skin_font */
1165 struct skin_font
*font
= &skinfonts
[font_id
-FONT_FIRSTUSERFONT
];
1170 DEBUGF("font %d not specified\n", font_id
);
1176 /* load the font - will handle loading the same font again if
1177 * multiple viewports use the same */
1180 char *dot
= strchr(font
->name
, '.');
1182 font
->id
= skin_font_load(font
->name
,
1183 skinfonts
[font_id
-FONT_FIRSTUSERFONT
].glyphs
);
1188 DEBUGF("Unable to load font %d: '%s.fnt'\n",
1189 font_id
, font
->name
);
1190 font
->name
= NULL
; /* to stop trying to load it again if we fail */
1196 /* finally, assign the font_id to the viewport */
1197 vp
->font
= font
->id
;
1202 #endif /* HAVE_LCD_BITMAP */
1203 static int convert_viewport(struct wps_data
*data
, struct skin_element
* element
)
1205 struct skin_viewport
*skin_vp
=
1206 (struct skin_viewport
*)skin_buffer_alloc(sizeof(struct skin_viewport
));
1207 struct screen
*display
= &screens
[curr_screen
];
1210 return CALLBACK_ERROR
;
1212 skin_vp
->hidden_flags
= 0;
1213 skin_vp
->label
= NULL
;
1214 skin_vp
->is_infovp
= false;
1215 element
->data
= skin_vp
;
1217 curr_viewport_element
= element
;
1219 viewport_set_defaults(&skin_vp
->vp
, curr_screen
);
1220 #ifdef HAVE_REMOTE_LCD
1221 /* viewport_set_defaults() sets the font to FONT_UI+curr_screen.
1222 * This parser requires font 1 to always be the UI font,
1223 * so force it back to FONT_UI and handle the screen number at the end */
1224 skin_vp
->vp
.font
= FONT_UI
;
1227 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1228 skin_vp
->start_fgcolour
= skin_vp
->vp
.fg_pattern
;
1229 skin_vp
->start_bgcolour
= skin_vp
->vp
.bg_pattern
;
1233 struct skin_tag_parameter
*param
= element
->params
;
1234 if (element
->params_count
== 0) /* default viewport */
1236 if (!data
->tree
) /* first viewport in the skin */
1237 data
->tree
= element
;
1238 skin_vp
->label
= VP_DEFAULT_LABEL
;
1242 if (element
->params_count
== 6)
1244 if (element
->tag
->type
== SKIN_TOKEN_UIVIEWPORT_LOAD
)
1246 skin_vp
->is_infovp
= true;
1247 if (isdefault(param
))
1249 skin_vp
->hidden_flags
= VP_NEVER_VISIBLE
;
1250 skin_vp
->label
= VP_DEFAULT_LABEL
;
1254 skin_vp
->hidden_flags
= VP_NEVER_VISIBLE
;
1255 skin_vp
->label
= param
->data
.text
;
1260 skin_vp
->hidden_flags
= VP_DRAW_HIDEABLE
|VP_DRAW_HIDDEN
;
1261 skin_vp
->label
= param
->data
.text
;
1266 if (!isdefault(param
))
1268 skin_vp
->vp
.x
= param
->data
.number
;
1269 if (param
->data
.number
< 0)
1270 skin_vp
->vp
.x
+= display
->lcdwidth
;
1274 if (!isdefault(param
))
1276 skin_vp
->vp
.y
= param
->data
.number
;
1277 if (param
->data
.number
< 0)
1278 skin_vp
->vp
.y
+= display
->lcdheight
;
1282 if (!isdefault(param
))
1284 skin_vp
->vp
.width
= param
->data
.number
;
1285 if (param
->data
.number
< 0)
1286 skin_vp
->vp
.width
= (skin_vp
->vp
.width
+ display
->lcdwidth
) - skin_vp
->vp
.x
;
1290 skin_vp
->vp
.width
= display
->lcdwidth
- skin_vp
->vp
.x
;
1294 if (!isdefault(param
))
1296 skin_vp
->vp
.height
= param
->data
.number
;
1297 if (param
->data
.number
< 0)
1298 skin_vp
->vp
.height
= (skin_vp
->vp
.height
+ display
->lcdheight
) - skin_vp
->vp
.y
;
1302 skin_vp
->vp
.height
= display
->lcdheight
- skin_vp
->vp
.y
;
1305 #ifdef HAVE_LCD_BITMAP
1307 if (!isdefault(param
))
1309 skin_vp
->vp
.font
= param
->data
.number
;
1317 static int skin_element_callback(struct skin_element
* element
, void* data
)
1319 struct wps_data
*wps_data
= (struct wps_data
*)data
;
1320 struct wps_token
*token
;
1321 parse_function function
= NULL
;
1323 switch (element
->type
)
1325 /* IMPORTANT: element params are shared, so copy them if needed
1326 * or use then NOW, dont presume they have a long lifespan
1330 token
= (struct wps_token
*)skin_buffer_alloc(sizeof(struct wps_token
));
1331 memset(token
, 0, sizeof(*token
));
1332 token
->type
= element
->tag
->type
;
1334 if (element
->tag
->flags
&SKIN_RTC_REFRESH
)
1337 curr_line
->update_mode
|= SKIN_REFRESH_DYNAMIC
;
1339 curr_line
->update_mode
|= SKIN_REFRESH_STATIC
;
1343 curr_line
->update_mode
|= element
->tag
->flags
&SKIN_REFRESH_ALL
;
1345 element
->data
= token
;
1347 /* Some tags need special handling for the tag, so add them here */
1348 switch (token
->type
)
1350 case SKIN_TOKEN_ALIGN_LANGDIRECTION
:
1351 follow_lang_direction
= 2;
1353 case SKIN_TOKEN_LOGICAL_IF
:
1354 function
= parse_logical_if
;
1356 case SKIN_TOKEN_PROGRESSBAR
:
1357 case SKIN_TOKEN_VOLUME
:
1358 case SKIN_TOKEN_BATTERY_PERCENT
:
1359 case SKIN_TOKEN_PLAYER_PROGRESSBAR
:
1360 function
= parse_progressbar_tag
;
1362 case SKIN_TOKEN_SUBLINE_TIMEOUT
:
1363 case SKIN_TOKEN_BUTTON_VOLUME
:
1364 case SKIN_TOKEN_TRACK_STARTING
:
1365 case SKIN_TOKEN_TRACK_ENDING
:
1366 case SKIN_TOKEN_LASTTOUCH
:
1367 function
= parse_timeout_tag
;
1369 #ifdef HAVE_LCD_BITMAP
1370 case SKIN_TOKEN_DISABLE_THEME
:
1371 case SKIN_TOKEN_ENABLE_THEME
:
1372 case SKIN_TOKEN_DRAW_INBUILTBAR
:
1373 function
= parse_statusbar_tags
;
1375 case SKIN_TOKEN_LIST_TITLE_TEXT
:
1376 sb_skin_has_title(curr_screen
);
1379 case SKIN_TOKEN_FILE_DIRECTORY
:
1380 token
->value
.i
= element
->params
[0].data
.number
;
1382 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1383 case SKIN_TOKEN_VIEWPORT_FGCOLOUR
:
1384 case SKIN_TOKEN_VIEWPORT_BGCOLOUR
:
1385 function
= parse_viewportcolour
;
1387 case SKIN_TOKEN_IMAGE_BACKDROP
:
1388 function
= parse_image_special
;
1391 case SKIN_TOKEN_TRANSLATEDSTRING
:
1392 case SKIN_TOKEN_SETTING
:
1393 function
= parse_setting_and_lang
;
1395 #ifdef HAVE_LCD_BITMAP
1396 case SKIN_TOKEN_VIEWPORT_CUSTOMLIST
:
1397 function
= parse_playlistview
;
1399 case SKIN_TOKEN_LOAD_FONT
:
1400 function
= parse_font_load
;
1402 case SKIN_TOKEN_VIEWPORT_ENABLE
:
1403 case SKIN_TOKEN_UIVIEWPORT_ENABLE
:
1404 token
->value
.data
= element
->params
[0].data
.text
;
1406 case SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY
:
1407 function
= parse_image_display
;
1409 case SKIN_TOKEN_IMAGE_PRELOAD
:
1410 case SKIN_TOKEN_IMAGE_DISPLAY
:
1411 function
= parse_image_load
;
1414 #ifdef HAVE_TOUCHSCREEN
1415 case SKIN_TOKEN_TOUCHREGION
:
1416 function
= parse_touchregion
;
1419 #ifdef HAVE_ALBUMART
1420 case SKIN_TOKEN_ALBUMART_DISPLAY
:
1421 if (wps_data
->albumart
)
1422 wps_data
->albumart
->vp
= &curr_vp
->vp
;
1424 case SKIN_TOKEN_ALBUMART_LOAD
:
1425 function
= parse_albumart_load
;
1433 if (function(element
, token
, wps_data
) < 0)
1434 return CALLBACK_ERROR
;
1436 /* tags that start with 'F', 'I' or 'D' are for the next file */
1437 if ( *(element
->tag
->name
) == 'I' || *(element
->tag
->name
) == 'F' ||
1438 *(element
->tag
->name
) == 'D')
1440 if (follow_lang_direction
> 0 )
1441 follow_lang_direction
--;
1445 return convert_viewport(wps_data
, element
);
1449 (struct line
*)skin_buffer_alloc(sizeof(struct line
));
1450 line
->update_mode
= SKIN_REFRESH_STATIC
;
1451 line
->timeout
= DEFAULT_SUBLINE_TIME_MULTIPLIER
* TIMEOUT_UNIT
;
1453 element
->data
= line
;
1456 case LINE_ALTERNATOR
:
1458 struct line_alternator
*alternator
=
1459 (struct line_alternator
*)skin_buffer_alloc(sizeof(struct line_alternator
));
1460 alternator
->current_line
= 0;
1462 alternator
->last_change_tick
= current_tick
;
1464 element
->data
= alternator
;
1469 struct conditional
*conditional
=
1470 (struct conditional
*)skin_buffer_alloc(sizeof(struct conditional
));
1471 conditional
->last_value
= -1;
1472 conditional
->token
= element
->data
;
1473 element
->data
= conditional
;
1474 if (!check_feature_tag(element
->tag
->type
))
1476 return FEATURE_NOT_AVAILABLE
;
1481 curr_line
->update_mode
|= SKIN_REFRESH_STATIC
;
1489 /* to setup up the wps-data from a format-buffer (isfile = false)
1490 from a (wps-)file (isfile = true)*/
1491 bool skin_data_load(enum screen_type screen
, struct wps_data
*wps_data
,
1492 const char *buf
, bool isfile
)
1494 char *wps_buffer
= NULL
;
1495 if (!wps_data
|| !buf
)
1497 #ifdef HAVE_ALBUMART
1499 struct mp3entry
*curtrack
;
1501 struct skin_albumart old_aa
= {.state
= WPS_ALBUMART_NONE
};
1502 if (wps_data
->albumart
)
1504 old_aa
.state
= wps_data
->albumart
->state
;
1505 old_aa
.height
= wps_data
->albumart
->height
;
1506 old_aa
.width
= wps_data
->albumart
->width
;
1509 #ifdef HAVE_LCD_BITMAP
1511 for (i
=0;i
<MAXUSERFONTS
;i
++)
1513 skinfonts
[i
].id
= -1;
1514 skinfonts
[i
].name
= NULL
;
1517 #ifdef DEBUG_SKIN_ENGINE
1518 if (isfile
&& debug_wps
)
1520 DEBUGF("\n=====================\nLoading '%s'\n=====================\n", buf
);
1525 skin_data_reset(wps_data
);
1526 wps_data
->wps_loaded
= false;
1527 curr_screen
= screen
;
1530 curr_viewport_element
= NULL
;
1534 int fd
= open_utf8(buf
, O_RDONLY
);
1539 /* get buffer space from the plugin buffer */
1540 size_t buffersize
= 0;
1541 wps_buffer
= (char *)plugin_get_buffer(&buffersize
);
1546 /* copy the file's content to the buffer for parsing,
1547 ensuring that every line ends with a newline char. */
1548 unsigned int start
= 0;
1549 while(read_line(fd
, wps_buffer
+ start
, buffersize
- start
) > 0)
1551 start
+= strlen(wps_buffer
+ start
);
1552 if (start
< buffersize
- 1)
1554 wps_buffer
[start
++] = '\n';
1555 wps_buffer
[start
] = 0;
1564 wps_buffer
= (char*)buf
;
1566 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1567 wps_data
->backdrop
= "-";
1568 wps_data
->backdrop_id
= -1;
1570 /* parse the skin source */
1571 skin_buffer_save_position();
1572 wps_data
->tree
= skin_parse(wps_buffer
, skin_element_callback
, wps_data
);
1573 if (!wps_data
->tree
) {
1574 skin_data_reset(wps_data
);
1575 skin_buffer_restore_position();
1579 #ifdef HAVE_LCD_BITMAP
1580 char bmpdir
[MAX_PATH
];
1583 /* get the bitmap dir */
1584 char *dot
= strrchr(buf
, '.');
1585 strlcpy(bmpdir
, buf
, dot
- buf
+ 1);
1588 { /* fall back to backdrop dir for built-in themes */
1589 /* no get_user_file_path(), assuming we ship bmps for built-in themes */
1590 snprintf(bmpdir
, MAX_PATH
, "%s", BACKDROP_DIR
);
1592 /* load the bitmaps that were found by the parsing */
1593 if (!load_skin_bitmaps(wps_data
, bmpdir
) ||
1594 !skin_load_fonts(wps_data
))
1596 skin_data_reset(wps_data
);
1597 skin_buffer_restore_position();
1601 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
1602 status
= audio_status();
1603 if (status
& AUDIO_STATUS_PLAY
)
1605 struct skin_albumart
*aa
= wps_data
->albumart
;
1606 if (aa
&& ((aa
->state
&& !old_aa
.state
) ||
1608 (((old_aa
.height
!= aa
->height
) ||
1609 (old_aa
.width
!= aa
->width
))))))
1611 curtrack
= audio_current_track();
1612 offset
= curtrack
->offset
;
1614 if (!(status
& AUDIO_STATUS_PAUSE
))
1619 wps_data
->wps_loaded
= true;
1620 #ifdef DEBUG_SKIN_ENGINE
1621 // if (isfile && debug_wps)
1622 // debug_skin_usage();