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 if (img
->using_preloaded_icons
)
253 token
->type
= SKIN_TOKEN_IMAGE_DISPLAY_LISTICON
;
256 if (element
->params_count
> 1)
258 if (element
->params
[1].type
== CODE
)
259 id
->token
= element
->params
[1].data
.code
->data
;
260 /* specify a number. 1 being the first subimage (i.e top) NOT 0 */
261 else if (element
->params
[1].type
== INTEGER
)
262 id
->subimage
= element
->params
[1].data
.number
- 1;
263 if (element
->params_count
> 2)
264 id
->offset
= element
->params
[2].data
.number
;
268 if ((subimage
= get_image_id(sublabel
)) != -1)
270 if (subimage
>= img
->num_subimages
)
271 return WPS_ERROR_INVALID_PARAM
;
272 id
->subimage
= subimage
;
277 token
->value
.data
= id
;
281 static int parse_image_load(struct skin_element
*element
,
282 struct wps_token
*token
,
283 struct wps_data
*wps_data
)
285 const char* filename
;
290 /* format: %x(n,filename.bmp,x,y)
291 or %xl(n,filename.bmp,x,y)
292 or %xl(n,filename.bmp,x,y,num_subimages)
295 id
= element
->params
[0].data
.text
;
296 filename
= element
->params
[1].data
.text
;
297 x
= element
->params
[2].data
.number
;
298 y
= element
->params
[3].data
.number
;
300 /* check the image number and load state */
301 if(find_image(id
, wps_data
))
303 /* Invalid image ID */
304 return WPS_ERROR_INVALID_PARAM
;
306 img
= (struct gui_img
*)skin_buffer_alloc(sizeof(struct gui_img
));
308 return WPS_ERROR_INVALID_PARAM
;
309 /* save a pointer to the filename */
310 img
->bm
.data
= (char*)filename
;
314 img
->num_subimages
= 1;
315 img
->always_display
= false;
317 img
->using_preloaded_icons
= false;
319 /* save current viewport */
320 img
->vp
= &curr_vp
->vp
;
322 if (token
->type
== SKIN_TOKEN_IMAGE_DISPLAY
)
324 img
->always_display
= true;
326 else if (element
->params_count
== 5)
328 img
->num_subimages
= element
->params
[4].data
.number
;
329 if (img
->num_subimages
<= 0)
330 return WPS_ERROR_INVALID_PARAM
;
333 if (!strcmp(img
->bm
.data
, "__list_icons__"))
335 img
->num_subimages
= Icon_Last_Themeable
;
336 img
->using_preloaded_icons
= true;
339 struct skin_token_list
*item
=
340 (struct skin_token_list
*)new_skin_token_list_item(NULL
, img
);
342 return WPS_ERROR_INVALID_PARAM
;
343 add_to_ll_chain(&wps_data
->images
, item
);
348 int id
; /* the id from font_load */
349 char *name
; /* filename without path and extension */
350 int glyphs
; /* how many glyphs to reserve room for */
352 static struct skin_font skinfonts
[MAXUSERFONTS
];
353 static int parse_font_load(struct skin_element
*element
,
354 struct wps_token
*token
,
355 struct wps_data
*wps_data
)
357 (void)wps_data
; (void)token
;
358 int id
= element
->params
[0].data
.number
;
359 char *filename
= element
->params
[1].data
.text
;
363 if(element
->params_count
> 2)
364 glyphs
= element
->params
[2].data
.number
;
366 glyphs
= GLYPHS_TO_CACHE
;
367 #if defined(DEBUG) || defined(SIMULATOR)
368 if (skinfonts
[id
-FONT_FIRSTUSERFONT
].name
!= NULL
)
370 DEBUGF("font id %d already being used\n", id
);
373 /* make sure the filename contains .fnt,
374 * we dont actually use it, but require it anyway */
375 ptr
= strchr(filename
, '.');
376 if (!ptr
|| strncmp(ptr
, ".fnt", 4))
377 return WPS_ERROR_INVALID_PARAM
;
378 skinfonts
[id
-FONT_FIRSTUSERFONT
].id
= -1;
379 skinfonts
[id
-FONT_FIRSTUSERFONT
].name
= filename
;
380 skinfonts
[id
-FONT_FIRSTUSERFONT
].glyphs
= glyphs
;
386 #ifdef HAVE_LCD_BITMAP
388 static int parse_playlistview(struct skin_element
*element
,
389 struct wps_token
*token
,
390 struct wps_data
*wps_data
)
393 struct playlistviewer
*viewer
=
394 (struct playlistviewer
*)skin_buffer_alloc(sizeof(struct playlistviewer
));
396 return WPS_ERROR_INVALID_PARAM
;
397 viewer
->vp
= &curr_vp
->vp
;
398 viewer
->show_icons
= true;
399 viewer
->start_offset
= element
->params
[0].data
.number
;
400 viewer
->line
= element
->params
[1].data
.code
;
402 token
->value
.data
= (void*)viewer
;
408 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
410 static int parse_viewportcolour(struct skin_element
*element
,
411 struct wps_token
*token
,
412 struct wps_data
*wps_data
)
415 struct skin_tag_parameter
*param
= element
->params
;
416 struct viewport_colour
*colour
=
417 (struct viewport_colour
*)skin_buffer_alloc(sizeof(struct viewport_colour
));
420 if (isdefault(param
))
422 colour
->colour
= get_viewport_default_colour(curr_screen
,
423 token
->type
== SKIN_TOKEN_VIEWPORT_FGCOLOUR
);
427 if (!parse_color(curr_screen
, param
->data
.text
, &colour
->colour
))
430 colour
->vp
= &curr_vp
->vp
;
431 token
->value
.data
= colour
;
432 if (element
->line
== curr_viewport_element
->line
)
434 if (token
->type
== SKIN_TOKEN_VIEWPORT_FGCOLOUR
)
436 curr_vp
->start_fgcolour
= colour
->colour
;
437 curr_vp
->vp
.fg_pattern
= colour
->colour
;
441 curr_vp
->start_bgcolour
= colour
->colour
;
442 curr_vp
->vp
.bg_pattern
= colour
->colour
;
448 static int parse_image_special(struct skin_element
*element
,
449 struct wps_token
*token
,
450 struct wps_data
*wps_data
)
452 (void)wps_data
; /* kill warning */
457 if (token
->type
== SKIN_TOKEN_IMAGE_BACKDROP
)
459 if (isdefault(&element
->params
[0]))
465 filename
= element
->params
[0].data
.text
;
466 /* format: %X(filename.bmp) or %X(d) */
467 if (!strcmp(filename
, "d"))
470 wps_data
->backdrop
= filename
;
478 #endif /* HAVE_LCD_BITMAP */
480 static int parse_setting_and_lang(struct skin_element
*element
,
481 struct wps_token
*token
,
482 struct wps_data
*wps_data
)
484 /* NOTE: both the string validations that happen in here will
485 * automatically PASS on checkwps because its too hard to get
486 * settings_list.c and english.lang built for it.
487 * If that ever changes remove the #ifndef __PCTOOL__'s here
490 char *temp
= element
->params
[0].data
.text
;
493 if (token
->type
== SKIN_TOKEN_TRANSLATEDSTRING
)
496 i
= lang_english_to_id(temp
);
498 return WPS_ERROR_INVALID_PARAM
;
503 /* Find the setting */
504 for (i
=0; i
<nb_settings
; i
++)
505 if (settings
[i
].cfg_name
&&
506 !strcmp(settings
[i
].cfg_name
, temp
))
509 if (i
== nb_settings
)
510 return WPS_ERROR_INVALID_PARAM
;
513 /* Store the setting number */
517 static int parse_logical_if(struct skin_element
*element
,
518 struct wps_token
*token
,
519 struct wps_data
*wps_data
)
522 char *op
= element
->params
[1].data
.text
;
523 struct logical_if
*lif
= skin_buffer_alloc(sizeof(struct logical_if
));
526 token
->value
.data
= lif
;
527 lif
->token
= element
->params
[0].data
.code
->data
;
529 if (!strncmp(op
, "=", 1))
531 else if (!strncmp(op
, "!=", 2))
532 lif
->op
= IF_NOTEQUALS
;
533 else if (!strncmp(op
, ">=", 2))
534 lif
->op
= IF_GREATERTHAN_EQ
;
535 else if (!strncmp(op
, "<=", 2))
536 lif
->op
= IF_LESSTHAN_EQ
;
537 else if (!strncmp(op
, ">", 2))
538 lif
->op
= IF_GREATERTHAN
;
539 else if (!strncmp(op
, "<", 1))
540 lif
->op
= IF_LESSTHAN
;
542 memcpy(&lif
->operand
, &element
->params
[2], sizeof(lif
->operand
));
543 if (element
->params_count
> 3)
544 lif
->num_options
= element
->params
[3].data
.number
;
546 lif
->num_options
= TOKEN_VALUE_ONLY
;
550 static int parse_timeout_tag(struct skin_element
*element
,
551 struct wps_token
*token
,
552 struct wps_data
*wps_data
)
556 if (element
->params_count
== 0)
560 case SKIN_TOKEN_SUBLINE_TIMEOUT
:
562 case SKIN_TOKEN_BUTTON_VOLUME
:
563 case SKIN_TOKEN_TRACK_STARTING
:
564 case SKIN_TOKEN_TRACK_ENDING
:
565 case SKIN_TOKEN_LASTTOUCH
:
573 val
= element
->params
[0].data
.number
;
574 token
->value
.i
= val
* TIMEOUT_UNIT
;
578 static int parse_progressbar_tag(struct skin_element
* element
,
579 struct wps_token
*token
,
580 struct wps_data
*wps_data
)
582 #ifdef HAVE_LCD_BITMAP
583 struct progressbar
*pb
;
584 struct viewport
*vp
= &curr_vp
->vp
;
585 struct skin_tag_parameter
*param
= element
->params
;
587 char *image_filename
= NULL
;
589 if (element
->params_count
== 0 &&
590 element
->tag
->type
!= SKIN_TOKEN_PROGRESSBAR
)
591 return 0; /* nothing to do */
592 pb
= (struct progressbar
*)skin_buffer_alloc(sizeof(struct progressbar
));
594 token
->value
.data
= pb
;
597 return WPS_ERROR_INVALID_PARAM
;
599 pb
->follow_lang_direction
= follow_lang_direction
> 0;
605 pb
->invert_fill_direction
= false;
606 pb
->horizontal
= true;
608 if (element
->params_count
== 0)
611 pb
->width
= vp
->width
;
612 pb
->height
= SYSFONT_HEIGHT
-2;
613 pb
->y
= -1; /* Will be computed during the rendering */
614 pb
->type
= element
->tag
->type
;
618 /* (x, y, width, height, ...) */
619 if (!isdefault(param
))
620 pb
->x
= param
->data
.number
;
625 if (!isdefault(param
))
626 pb
->y
= param
->data
.number
;
628 pb
->y
= -1; /* computed at rendering */
631 if (!isdefault(param
))
632 pb
->width
= param
->data
.number
;
634 pb
->width
= vp
->width
- pb
->x
;
637 if (!isdefault(param
))
639 /* A zero height makes no sense - reject it */
640 if (param
->data
.number
== 0)
641 return WPS_ERROR_INVALID_PARAM
;
643 pb
->height
= param
->data
.number
;
647 if (vp
->font
> FONT_UI
)
648 pb
->height
= -1; /* calculate at display time */
652 pb
->height
= font_get(vp
->font
)->height
;
658 /* optional params, first is the image filename if it isnt recognised as a keyword */
661 if (isdefault(&element
->params
[curr_param
]))
667 pb
->horizontal
= pb
->width
> pb
->height
;
668 while (curr_param
< element
->params_count
)
671 if (!strcmp(param
->data
.text
, "invert"))
672 pb
->invert_fill_direction
= true;
673 else if (!strcmp(param
->data
.text
, "nofill"))
675 else if (!strcmp(param
->data
.text
, "nobar"))
677 else if (!strcmp(param
->data
.text
, "slider"))
679 if (curr_param
+1 < element
->params_count
)
683 pb
->slider
= find_image(param
->data
.text
, wps_data
);
685 else /* option needs the next param */
688 else if (!strcmp(param
->data
.text
, "image"))
690 if (curr_param
+1 < element
->params_count
)
694 image_filename
= param
->data
.text
;
697 else /* option needs the next param */
700 else if (!strcmp(param
->data
.text
, "backdrop"))
702 if (curr_param
+1 < element
->params_count
)
706 pb
->backdrop
= find_image(param
->data
.text
, wps_data
);
709 else /* option needs the next param */
712 else if (!strcmp(param
->data
.text
, "vertical"))
714 pb
->horizontal
= false;
715 if (isdefault(&element
->params
[3]))
716 pb
->height
= vp
->height
- pb
->y
;
718 else if (!strcmp(param
->data
.text
, "horizontal"))
719 pb
->horizontal
= true;
720 else if (curr_param
== 4)
721 image_filename
= param
->data
.text
;
728 pb
->image
= find_image(image_filename
, wps_data
);
729 if (!pb
->image
) /* load later */
731 struct gui_img
* img
= (struct gui_img
*)skin_buffer_alloc(sizeof(struct gui_img
));
733 return WPS_ERROR_INVALID_PARAM
;
734 /* save a pointer to the filename */
735 img
->bm
.data
= (char*)image_filename
;
736 img
->label
= image_filename
;
739 img
->num_subimages
= 1;
740 img
->always_display
= false;
742 img
->using_preloaded_icons
= false;
743 img
->vp
= &curr_vp
->vp
;
744 struct skin_token_list
*item
=
745 (struct skin_token_list
*)new_skin_token_list_item(NULL
, img
);
747 return WPS_ERROR_INVALID_PARAM
;
748 add_to_ll_chain(&wps_data
->images
, item
);
753 if (token
->type
== SKIN_TOKEN_VOLUME
)
754 token
->type
= SKIN_TOKEN_VOLUMEBAR
;
755 else if (token
->type
== SKIN_TOKEN_BATTERY_PERCENT
)
756 token
->type
= SKIN_TOKEN_BATTERY_PERCENTBAR
;
757 else if (token
->type
== SKIN_TOKEN_TUNER_RSSI
)
758 token
->type
= SKIN_TOKEN_TUNER_RSSI_BAR
;
759 else if (token
->type
== SKIN_TOKEN_PEAKMETER_LEFT
)
760 token
->type
= SKIN_TOKEN_PEAKMETER_LEFTBAR
;
761 else if (token
->type
== SKIN_TOKEN_PEAKMETER_RIGHT
)
762 token
->type
= SKIN_TOKEN_PEAKMETER_RIGHTBAR
;
763 pb
->type
= token
->type
;
769 if (token
->type
== SKIN_TOKEN_PROGRESSBAR
||
770 token
->type
== SKIN_TOKEN_PLAYER_PROGRESSBAR
)
772 wps_data
->full_line_progressbar
=
773 token
->type
== SKIN_TOKEN_PLAYER_PROGRESSBAR
;
781 static int parse_albumart_load(struct skin_element
* element
,
782 struct wps_token
*token
,
783 struct wps_data
*wps_data
)
785 struct dim dimensions
;
787 bool swap_for_rtl
= lang_is_rtl() && follow_lang_direction
;
788 struct skin_albumart
*aa
=
789 (struct skin_albumart
*)skin_buffer_alloc(sizeof(struct skin_albumart
));
790 (void)token
; /* silence warning */
794 /* reset albumart info in wps */
797 aa
->xalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
798 aa
->yalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
800 aa
->x
= element
->params
[0].data
.number
;
801 aa
->y
= element
->params
[1].data
.number
;
802 aa
->width
= element
->params
[2].data
.number
;
803 aa
->height
= element
->params
[3].data
.number
;
805 aa
->vp
= &curr_vp
->vp
;
806 aa
->draw_handle
= -1;
808 /* if we got here, we parsed everything ok .. ! */
811 else if (aa
->width
> LCD_WIDTH
)
812 aa
->width
= LCD_WIDTH
;
816 else if (aa
->height
> LCD_HEIGHT
)
817 aa
->height
= LCD_HEIGHT
;
820 aa
->x
= LCD_WIDTH
- (aa
->x
+ aa
->width
);
822 aa
->state
= WPS_ALBUMART_LOAD
;
823 wps_data
->albumart
= aa
;
825 dimensions
.width
= aa
->width
;
826 dimensions
.height
= aa
->height
;
828 albumart_slot
= playback_claim_aa_slot(&dimensions
);
830 if (0 <= albumart_slot
)
831 wps_data
->playback_aa_slot
= albumart_slot
;
833 if (element
->params_count
> 4 && !isdefault(&element
->params
[4]))
835 switch (*element
->params
[4].data
.text
)
840 aa
->xalign
= WPS_ALBUMART_ALIGN_RIGHT
;
842 aa
->xalign
= WPS_ALBUMART_ALIGN_LEFT
;
846 aa
->xalign
= WPS_ALBUMART_ALIGN_CENTER
;
851 aa
->xalign
= WPS_ALBUMART_ALIGN_LEFT
;
853 aa
->xalign
= WPS_ALBUMART_ALIGN_RIGHT
;
857 if (element
->params_count
> 5 && !isdefault(&element
->params
[5]))
859 switch (*element
->params
[5].data
.text
)
863 aa
->yalign
= WPS_ALBUMART_ALIGN_TOP
;
867 aa
->yalign
= WPS_ALBUMART_ALIGN_CENTER
;
871 aa
->yalign
= WPS_ALBUMART_ALIGN_BOTTOM
;
878 #endif /* HAVE_ALBUMART */
880 #ifdef HAVE_TOUCHSCREEN
882 struct touchaction
{const char* s
; int action
;};
883 static const struct touchaction touchactions
[] = {
884 /* generic actions, convert to screen actions on use */
885 {"prev", ACTION_STD_PREV
}, {"next", ACTION_STD_NEXT
},
886 {"rwd", ACTION_STD_PREVREPEAT
}, {"ffwd", ACTION_STD_NEXTREPEAT
},
887 {"hotkey", ACTION_STD_HOTKEY
}, {"select", ACTION_STD_OK
},
888 {"menu", ACTION_STD_MENU
}, {"cancel", ACTION_STD_CANCEL
},
889 {"contextmenu", ACTION_STD_CONTEXT
},{"quickscreen", ACTION_STD_QUICKSCREEN
},
891 /* list/tree actions */
892 { "resumeplayback", ACTION_TREE_WPS
}, /* returns to previous music, WPS/FM */
893 /* not really WPS specific, but no equivilant ACTION_STD_* */
894 {"voldown", ACTION_WPS_VOLDOWN
}, {"volup", ACTION_WPS_VOLUP
},
895 {"mute", ACTION_TOUCH_MUTE
},
897 /* generic settings changers */
898 {"setting_inc", ACTION_SETTINGS_INC
}, {"setting_dec", ACTION_SETTINGS_DEC
},
900 /* WPS specific actions */
901 {"browse", ACTION_WPS_BROWSE
},
902 {"play", ACTION_WPS_PLAY
}, {"stop", ACTION_WPS_STOP
},
903 {"shuffle", ACTION_TOUCH_SHUFFLE
}, {"repmode", ACTION_TOUCH_REPMODE
},
904 {"pitch", ACTION_WPS_PITCHSCREEN
}, {"playlist", ACTION_WPS_VIEW_PLAYLIST
},
907 /* FM screen actions */
908 /* Also allow browse, play, stop from WPS codes */
909 {"mode", ACTION_FM_MODE
}, {"record", ACTION_FM_RECORD
},
910 {"presets", ACTION_FM_PRESET
},
914 static int parse_touchregion(struct skin_element
*element
,
915 struct wps_token
*token
,
916 struct wps_data
*wps_data
)
920 struct touchregion
*region
= NULL
;
922 const char pb_string
[] = "progressbar";
923 const char vol_string
[] = "volume";
926 /* format: %T(x,y,width,height,action)
927 * if action starts with & the area must be held to happen
929 * play - play/pause playback
930 * stop - stop playback, exit the wps
933 * ffwd - seek forward
934 * rwd - seek backwards
935 * menu - go back to the main menu
936 * browse - go back to the file/db browser
937 * shuffle - toggle shuffle mode
938 * repmode - cycle the repeat mode
939 * quickscreen - go into the quickscreen
940 * contextmenu - open the context menu
941 * playlist - go into the playlist
942 * pitch - go into the pitchscreen
943 * volup - increase volume by one step
944 * voldown - decrease volume by one step
948 region
= (struct touchregion
*)skin_buffer_alloc(sizeof(struct touchregion
));
950 return WPS_ERROR_INVALID_PARAM
;
952 /* should probably do some bounds checking here with the viewport... but later */
953 region
->action
= ACTION_NONE
;
954 region
->x
= element
->params
[0].data
.number
;
955 region
->y
= element
->params
[1].data
.number
;
956 region
->width
= element
->params
[2].data
.number
;
957 region
->height
= element
->params
[3].data
.number
;
958 region
->wvp
= curr_vp
;
959 region
->armed
= false;
960 region
->reverse_bar
= false;
962 action
= element
->params
[4].data
.text
;
964 strcpy(temp
, action
);
969 region
->reverse_bar
= true;
973 if(!strcmp(pb_string
, action
))
974 region
->type
= WPS_TOUCHREGION_SCROLLBAR
;
975 else if(!strcmp(vol_string
, action
))
976 region
->type
= WPS_TOUCHREGION_VOLUME
;
979 region
->type
= WPS_TOUCHREGION_ACTION
;
984 region
->repeat
= true;
987 region
->repeat
= false;
989 imax
= ARRAYLEN(touchactions
);
990 for (i
= 0; i
< imax
; i
++)
992 /* try to match with one of our touchregion screens */
993 if (!strcmp(touchactions
[i
].s
, action
))
995 region
->action
= touchactions
[i
].action
;
996 if (region
->action
== ACTION_SETTINGS_INC
||
997 region
->action
== ACTION_SETTINGS_DEC
)
999 if (element
->params_count
< 6)
1001 return WPS_ERROR_INVALID_PARAM
;
1005 char *name
= element
->params
[5].data
.text
;
1007 /* Find the setting */
1008 for (j
=0; j
<nb_settings
; j
++)
1009 if (settings
[j
].cfg_name
&&
1010 !strcmp(settings
[j
].cfg_name
, name
))
1013 return WPS_ERROR_INVALID_PARAM
;
1014 region
->data
= (void*)&settings
[j
];
1020 if (region
->action
== ACTION_NONE
)
1021 return WPS_ERROR_INVALID_PARAM
;
1023 struct skin_token_list
*item
= new_skin_token_list_item(NULL
, region
);
1025 return WPS_ERROR_INVALID_PARAM
;
1026 add_to_ll_chain(&wps_data
->touchregions
, item
);
1028 if (region
->action
== ACTION_TOUCH_MUTE
)
1030 region
->value
= global_settings
.volume
;
1038 static bool check_feature_tag(const int type
)
1042 case SKIN_TOKEN_RTC_PRESENT
:
1048 case SKIN_TOKEN_HAVE_RECORDING
:
1049 #ifdef HAVE_RECORDING
1054 case SKIN_TOKEN_HAVE_TUNER
:
1056 if (radio_hardware_present())
1060 case SKIN_TOKEN_HAVE_TOUCH
:
1061 #ifdef HAVE_TOUCHSCREEN
1068 case SKIN_TOKEN_HAVE_RDS
:
1073 #endif /* HAVE_RDS_CAP */
1074 #endif /* CONFIG_TUNER */
1075 default: /* not a tag we care about, just don't skip */
1081 * initial setup of wps_data; does reset everything
1082 * except fields which need to survive, i.e.
1085 static void skin_data_reset(struct wps_data
*wps_data
)
1087 wps_data
->tree
= NULL
;
1088 #ifdef HAVE_LCD_BITMAP
1089 wps_data
->images
= NULL
;
1091 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1092 if (wps_data
->backdrop_id
>= 0)
1093 skin_backdrop_unload(wps_data
->backdrop_id
);
1094 wps_data
->backdrop
= NULL
;
1096 #ifdef HAVE_TOUCHSCREEN
1097 wps_data
->touchregions
= NULL
;
1099 #ifdef HAVE_ALBUMART
1100 wps_data
->albumart
= NULL
;
1101 if (wps_data
->playback_aa_slot
>= 0)
1103 playback_release_aa_slot(wps_data
->playback_aa_slot
);
1104 wps_data
->playback_aa_slot
= -1;
1108 #ifdef HAVE_LCD_BITMAP
1109 wps_data
->peak_meter_enabled
= false;
1110 wps_data
->wps_sb_tag
= false;
1111 wps_data
->show_sb_on_wps
= false;
1112 #else /* HAVE_LCD_CHARCELLS */
1115 for (i
= 0; i
< 8; i
++)
1117 wps_data
->wps_progress_pat
[i
] = 0;
1119 wps_data
->full_line_progressbar
= false;
1121 wps_data
->wps_loaded
= false;
1124 #ifdef HAVE_LCD_BITMAP
1125 static bool load_skin_bmp(struct wps_data
*wps_data
, struct bitmap
*bitmap
, char* bmpdir
)
1127 (void)wps_data
; /* only needed for remote targets */
1128 char img_path
[MAX_PATH
];
1130 get_image_filename(bitmap
->data
, bmpdir
,
1131 img_path
, sizeof(img_path
));
1133 /* load the image */
1135 #ifdef HAVE_REMOTE_LCD
1136 if (curr_screen
== SCREEN_REMOTE
)
1137 format
= FORMAT_ANY
|FORMAT_REMOTE
;
1140 format
= FORMAT_ANY
|FORMAT_TRANSPARENT
;
1142 fd
= open(img_path
, O_RDONLY
);
1145 DEBUGF("Couldn't open %s\n", img_path
);
1148 size_t buf_size
= read_bmp_fd(fd
, bitmap
, 0,
1149 format
|FORMAT_RETURN_SIZE
, NULL
);
1150 char* imgbuf
= (char*)skin_buffer_alloc(buf_size
);
1154 DEBUGF("Not enough skin buffer: need %zd more.\n",
1155 buf_size
- skin_buffer_freespace());
1160 lseek(fd
, 0, SEEK_SET
);
1161 bitmap
->data
= imgbuf
;
1162 int ret
= read_bmp_fd(fd
, bitmap
, buf_size
, format
, NULL
);
1171 /* Abort if we can't load an image */
1172 DEBUGF("Couldn't load '%s'\n", img_path
);
1177 static bool load_skin_bitmaps(struct wps_data
*wps_data
, char *bmpdir
)
1179 struct skin_token_list
*list
;
1180 bool retval
= true; /* return false if a single image failed to load */
1182 /* regular images */
1183 list
= wps_data
->images
;
1186 struct gui_img
*img
= (struct gui_img
*)list
->token
->value
.data
;
1189 if (img
->using_preloaded_icons
)
1192 list
->token
->type
= SKIN_TOKEN_IMAGE_DISPLAY_LISTICON
;
1196 img
->loaded
= load_skin_bmp(wps_data
, &img
->bm
, bmpdir
);
1198 img
->subimage_height
= img
->bm
.height
/ img
->num_subimages
;
1206 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1207 wps_data
->backdrop_id
= skin_backdrop_assign(wps_data
->backdrop
, bmpdir
, curr_screen
);
1208 #endif /* has backdrop support */
1212 static bool skin_load_fonts(struct wps_data
*data
)
1214 /* don't spit out after the first failue to aid debugging */
1215 bool success
= true;
1216 struct skin_element
*vp_list
;
1218 /* walk though each viewport and assign its font */
1219 for(vp_list
= data
->tree
; vp_list
; vp_list
= vp_list
->next
)
1221 /* first, find the viewports that have a non-sys/ui-font font */
1222 struct skin_viewport
*skin_vp
=
1223 (struct skin_viewport
*)vp_list
->data
;
1224 struct viewport
*vp
= &skin_vp
->vp
;
1227 if (vp
->font
<= FONT_UI
)
1228 { /* the usual case -> built-in fonts */
1229 #ifdef HAVE_REMOTE_LCD
1230 if (vp
->font
== FONT_UI
)
1231 vp
->font
+= curr_screen
;
1237 /* now find the corresponding skin_font */
1238 struct skin_font
*font
= &skinfonts
[font_id
-FONT_FIRSTUSERFONT
];
1243 DEBUGF("font %d not specified\n", font_id
);
1249 /* load the font - will handle loading the same font again if
1250 * multiple viewports use the same */
1253 char *dot
= strchr(font
->name
, '.');
1255 font
->id
= skin_font_load(font
->name
,
1256 skinfonts
[font_id
-FONT_FIRSTUSERFONT
].glyphs
);
1261 DEBUGF("Unable to load font %d: '%s.fnt'\n",
1262 font_id
, font
->name
);
1263 font
->name
= NULL
; /* to stop trying to load it again if we fail */
1269 /* finally, assign the font_id to the viewport */
1270 vp
->font
= font
->id
;
1275 #endif /* HAVE_LCD_BITMAP */
1276 static int convert_viewport(struct wps_data
*data
, struct skin_element
* element
)
1278 struct skin_viewport
*skin_vp
=
1279 (struct skin_viewport
*)skin_buffer_alloc(sizeof(struct skin_viewport
));
1280 struct screen
*display
= &screens
[curr_screen
];
1283 return CALLBACK_ERROR
;
1285 skin_vp
->hidden_flags
= 0;
1286 skin_vp
->label
= NULL
;
1287 skin_vp
->is_infovp
= false;
1288 element
->data
= skin_vp
;
1290 curr_viewport_element
= element
;
1292 viewport_set_defaults(&skin_vp
->vp
, curr_screen
);
1293 #ifdef HAVE_REMOTE_LCD
1294 /* viewport_set_defaults() sets the font to FONT_UI+curr_screen.
1295 * This parser requires font 1 to always be the UI font,
1296 * so force it back to FONT_UI and handle the screen number at the end */
1297 skin_vp
->vp
.font
= FONT_UI
;
1300 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1301 skin_vp
->start_fgcolour
= skin_vp
->vp
.fg_pattern
;
1302 skin_vp
->start_bgcolour
= skin_vp
->vp
.bg_pattern
;
1306 struct skin_tag_parameter
*param
= element
->params
;
1307 if (element
->params_count
== 0) /* default viewport */
1309 if (!data
->tree
) /* first viewport in the skin */
1310 data
->tree
= element
;
1311 skin_vp
->label
= VP_DEFAULT_LABEL
;
1315 if (element
->params_count
== 6)
1317 if (element
->tag
->type
== SKIN_TOKEN_UIVIEWPORT_LOAD
)
1319 skin_vp
->is_infovp
= true;
1320 if (isdefault(param
))
1322 skin_vp
->hidden_flags
= VP_NEVER_VISIBLE
;
1323 skin_vp
->label
= VP_DEFAULT_LABEL
;
1327 skin_vp
->hidden_flags
= VP_NEVER_VISIBLE
;
1328 skin_vp
->label
= param
->data
.text
;
1333 skin_vp
->hidden_flags
= VP_DRAW_HIDEABLE
|VP_DRAW_HIDDEN
;
1334 skin_vp
->label
= param
->data
.text
;
1339 if (!isdefault(param
))
1341 skin_vp
->vp
.x
= param
->data
.number
;
1342 if (param
->data
.number
< 0)
1343 skin_vp
->vp
.x
+= display
->lcdwidth
;
1347 if (!isdefault(param
))
1349 skin_vp
->vp
.y
= param
->data
.number
;
1350 if (param
->data
.number
< 0)
1351 skin_vp
->vp
.y
+= display
->lcdheight
;
1355 if (!isdefault(param
))
1357 skin_vp
->vp
.width
= param
->data
.number
;
1358 if (param
->data
.number
< 0)
1359 skin_vp
->vp
.width
= (skin_vp
->vp
.width
+ display
->lcdwidth
) - skin_vp
->vp
.x
;
1363 skin_vp
->vp
.width
= display
->lcdwidth
- skin_vp
->vp
.x
;
1367 if (!isdefault(param
))
1369 skin_vp
->vp
.height
= param
->data
.number
;
1370 if (param
->data
.number
< 0)
1371 skin_vp
->vp
.height
= (skin_vp
->vp
.height
+ display
->lcdheight
) - skin_vp
->vp
.y
;
1375 skin_vp
->vp
.height
= display
->lcdheight
- skin_vp
->vp
.y
;
1378 #ifdef HAVE_LCD_BITMAP
1380 if (!isdefault(param
))
1382 skin_vp
->vp
.font
= param
->data
.number
;
1385 if ((unsigned) skin_vp
->vp
.x
>= (unsigned) display
->lcdwidth
||
1386 skin_vp
->vp
.width
+ skin_vp
->vp
.x
> display
->lcdwidth
||
1387 (unsigned) skin_vp
->vp
.y
>= (unsigned) display
->lcdheight
||
1388 skin_vp
->vp
.height
+ skin_vp
->vp
.y
> display
->lcdheight
)
1389 return CALLBACK_ERROR
;
1394 static int skin_element_callback(struct skin_element
* element
, void* data
)
1396 struct wps_data
*wps_data
= (struct wps_data
*)data
;
1397 struct wps_token
*token
;
1398 parse_function function
= NULL
;
1400 switch (element
->type
)
1402 /* IMPORTANT: element params are shared, so copy them if needed
1403 * or use then NOW, dont presume they have a long lifespan
1407 token
= (struct wps_token
*)skin_buffer_alloc(sizeof(struct wps_token
));
1408 memset(token
, 0, sizeof(*token
));
1409 token
->type
= element
->tag
->type
;
1411 if (element
->tag
->flags
&SKIN_RTC_REFRESH
)
1414 curr_line
->update_mode
|= SKIN_REFRESH_DYNAMIC
;
1416 curr_line
->update_mode
|= SKIN_REFRESH_STATIC
;
1420 curr_line
->update_mode
|= element
->tag
->flags
&SKIN_REFRESH_ALL
;
1422 element
->data
= token
;
1424 /* Some tags need special handling for the tag, so add them here */
1425 switch (token
->type
)
1427 case SKIN_TOKEN_ALIGN_LANGDIRECTION
:
1428 follow_lang_direction
= 2;
1430 case SKIN_TOKEN_LOGICAL_IF
:
1431 function
= parse_logical_if
;
1433 case SKIN_TOKEN_PROGRESSBAR
:
1434 case SKIN_TOKEN_VOLUME
:
1435 case SKIN_TOKEN_BATTERY_PERCENT
:
1436 case SKIN_TOKEN_PLAYER_PROGRESSBAR
:
1437 case SKIN_TOKEN_PEAKMETER_LEFT
:
1438 case SKIN_TOKEN_PEAKMETER_RIGHT
:
1439 #ifdef HAVE_RADIO_RSSI
1440 case SKIN_TOKEN_TUNER_RSSI
:
1442 function
= parse_progressbar_tag
;
1444 case SKIN_TOKEN_SUBLINE_TIMEOUT
:
1445 case SKIN_TOKEN_BUTTON_VOLUME
:
1446 case SKIN_TOKEN_TRACK_STARTING
:
1447 case SKIN_TOKEN_TRACK_ENDING
:
1448 case SKIN_TOKEN_LASTTOUCH
:
1449 function
= parse_timeout_tag
;
1451 #ifdef HAVE_LCD_BITMAP
1452 case SKIN_TOKEN_DISABLE_THEME
:
1453 case SKIN_TOKEN_ENABLE_THEME
:
1454 case SKIN_TOKEN_DRAW_INBUILTBAR
:
1455 function
= parse_statusbar_tags
;
1457 case SKIN_TOKEN_LIST_TITLE_TEXT
:
1459 sb_skin_has_title(curr_screen
);
1463 case SKIN_TOKEN_FILE_DIRECTORY
:
1464 token
->value
.i
= element
->params
[0].data
.number
;
1466 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1467 case SKIN_TOKEN_VIEWPORT_FGCOLOUR
:
1468 case SKIN_TOKEN_VIEWPORT_BGCOLOUR
:
1469 function
= parse_viewportcolour
;
1471 case SKIN_TOKEN_IMAGE_BACKDROP
:
1472 function
= parse_image_special
;
1475 case SKIN_TOKEN_TRANSLATEDSTRING
:
1476 case SKIN_TOKEN_SETTING
:
1477 function
= parse_setting_and_lang
;
1479 #ifdef HAVE_LCD_BITMAP
1480 case SKIN_TOKEN_VIEWPORT_CUSTOMLIST
:
1481 function
= parse_playlistview
;
1483 case SKIN_TOKEN_LOAD_FONT
:
1484 function
= parse_font_load
;
1486 case SKIN_TOKEN_VIEWPORT_ENABLE
:
1487 case SKIN_TOKEN_UIVIEWPORT_ENABLE
:
1488 token
->value
.data
= element
->params
[0].data
.text
;
1490 case SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY
:
1491 function
= parse_image_display
;
1493 case SKIN_TOKEN_IMAGE_PRELOAD
:
1494 case SKIN_TOKEN_IMAGE_DISPLAY
:
1495 function
= parse_image_load
;
1498 #ifdef HAVE_TOUCHSCREEN
1499 case SKIN_TOKEN_TOUCHREGION
:
1500 function
= parse_touchregion
;
1503 #ifdef HAVE_ALBUMART
1504 case SKIN_TOKEN_ALBUMART_DISPLAY
:
1505 if (wps_data
->albumart
)
1506 wps_data
->albumart
->vp
= &curr_vp
->vp
;
1508 case SKIN_TOKEN_ALBUMART_LOAD
:
1509 function
= parse_albumart_load
;
1517 if (function(element
, token
, wps_data
) < 0)
1518 return CALLBACK_ERROR
;
1520 /* tags that start with 'F', 'I' or 'D' are for the next file */
1521 if ( *(element
->tag
->name
) == 'I' || *(element
->tag
->name
) == 'F' ||
1522 *(element
->tag
->name
) == 'D')
1524 if (follow_lang_direction
> 0 )
1525 follow_lang_direction
--;
1529 return convert_viewport(wps_data
, element
);
1533 (struct line
*)skin_buffer_alloc(sizeof(struct line
));
1534 line
->update_mode
= SKIN_REFRESH_STATIC
;
1536 element
->data
= line
;
1539 case LINE_ALTERNATOR
:
1541 struct line_alternator
*alternator
=
1542 (struct line_alternator
*)skin_buffer_alloc(sizeof(struct line_alternator
));
1543 alternator
->current_line
= 0;
1545 alternator
->next_change_tick
= current_tick
;
1547 element
->data
= alternator
;
1552 struct conditional
*conditional
=
1553 (struct conditional
*)skin_buffer_alloc(sizeof(struct conditional
));
1554 conditional
->last_value
= -1;
1555 conditional
->token
= element
->data
;
1556 element
->data
= conditional
;
1557 if (!check_feature_tag(element
->tag
->type
))
1559 return FEATURE_NOT_AVAILABLE
;
1564 curr_line
->update_mode
|= SKIN_REFRESH_STATIC
;
1572 /* to setup up the wps-data from a format-buffer (isfile = false)
1573 from a (wps-)file (isfile = true)*/
1574 bool skin_data_load(enum screen_type screen
, struct wps_data
*wps_data
,
1575 const char *buf
, bool isfile
)
1577 char *wps_buffer
= NULL
;
1578 if (!wps_data
|| !buf
)
1580 #ifdef HAVE_ALBUMART
1582 struct mp3entry
*curtrack
;
1584 struct skin_albumart old_aa
= {.state
= WPS_ALBUMART_NONE
};
1585 if (wps_data
->albumart
)
1587 old_aa
.state
= wps_data
->albumart
->state
;
1588 old_aa
.height
= wps_data
->albumart
->height
;
1589 old_aa
.width
= wps_data
->albumart
->width
;
1592 #ifdef HAVE_LCD_BITMAP
1594 for (i
=0;i
<MAXUSERFONTS
;i
++)
1596 skinfonts
[i
].id
= -1;
1597 skinfonts
[i
].name
= NULL
;
1600 #ifdef DEBUG_SKIN_ENGINE
1601 if (isfile
&& debug_wps
)
1603 DEBUGF("\n=====================\nLoading '%s'\n=====================\n", buf
);
1608 skin_data_reset(wps_data
);
1609 wps_data
->wps_loaded
= false;
1610 curr_screen
= screen
;
1613 curr_viewport_element
= NULL
;
1617 int fd
= open_utf8(buf
, O_RDONLY
);
1622 /* get buffer space from the plugin buffer */
1623 size_t buffersize
= 0;
1624 wps_buffer
= (char *)plugin_get_buffer(&buffersize
);
1629 /* copy the file's content to the buffer for parsing,
1630 ensuring that every line ends with a newline char. */
1631 unsigned int start
= 0;
1632 while(read_line(fd
, wps_buffer
+ start
, buffersize
- start
) > 0)
1634 start
+= strlen(wps_buffer
+ start
);
1635 if (start
< buffersize
- 1)
1637 wps_buffer
[start
++] = '\n';
1638 wps_buffer
[start
] = 0;
1647 wps_buffer
= (char*)buf
;
1649 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1650 wps_data
->backdrop
= "-";
1651 wps_data
->backdrop_id
= -1;
1653 /* parse the skin source */
1655 skin_buffer_save_position();
1657 wps_data
->tree
= skin_parse(wps_buffer
, skin_element_callback
, wps_data
);
1658 if (!wps_data
->tree
) {
1659 skin_data_reset(wps_data
);
1661 skin_buffer_restore_position();
1666 #ifdef HAVE_LCD_BITMAP
1667 char bmpdir
[MAX_PATH
];
1670 /* get the bitmap dir */
1671 char *dot
= strrchr(buf
, '.');
1672 strlcpy(bmpdir
, buf
, dot
- buf
+ 1);
1676 snprintf(bmpdir
, MAX_PATH
, "%s", BACKDROP_DIR
);
1678 /* load the bitmaps that were found by the parsing */
1679 if (!load_skin_bitmaps(wps_data
, bmpdir
) ||
1680 !skin_load_fonts(wps_data
))
1682 skin_data_reset(wps_data
);
1684 skin_buffer_restore_position();
1689 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
1690 status
= audio_status();
1691 if (status
& AUDIO_STATUS_PLAY
)
1693 struct skin_albumart
*aa
= wps_data
->albumart
;
1694 if (aa
&& ((aa
->state
&& !old_aa
.state
) ||
1696 (((old_aa
.height
!= aa
->height
) ||
1697 (old_aa
.width
!= aa
->width
))))))
1699 curtrack
= audio_current_track();
1700 offset
= curtrack
->offset
;
1702 if (!(status
& AUDIO_STATUS_PAUSE
))
1707 wps_data
->wps_loaded
= true;
1708 #ifdef DEBUG_SKIN_ENGINE
1709 // if (isfile && debug_wps)
1710 // debug_skin_usage();