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(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 (img
->label
== label
)
134 /* traverse the viewport linked list for a viewport */
135 struct skin_viewport
* find_viewport(char *label
, bool uivp
, struct wps_data
*data
)
137 struct skin_element
*list
= data
->tree
;
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 *text
= element
->params
[0].data
.text
;
231 char label
= text
[0];
232 char sublabel
= text
[1];
237 img
= find_image(label
, wps_data
);
240 token
->value
.i
= label
; /* so debug works */
241 return WPS_ERROR_INVALID_PARAM
;
244 if ((subimage
= get_image_id(sublabel
)) != -1)
246 if (subimage
>= img
->num_subimages
)
247 return WPS_ERROR_INVALID_PARAM
;
249 /* Store sub-image number to display in high bits */
250 token
->value
.i
= label
| (subimage
<< 8);
251 return 4; /* We have consumed 2 bytes */
253 token
->value
.i
= label
;
254 return 3; /* We have consumed 1 byte */
258 static int parse_image_load(struct skin_element
*element
,
259 struct wps_token
*token
,
260 struct wps_data
*wps_data
)
262 const char* filename
;
267 /* format: %x(n,filename.bmp,x,y)
268 or %xl(n,filename.bmp,x,y)
269 or %xl(n,filename.bmp,x,y,num_subimages)
272 id
= element
->params
[0].data
.text
;
273 filename
= element
->params
[1].data
.text
;
274 x
= element
->params
[2].data
.number
;
275 y
= element
->params
[3].data
.number
;
277 /* check the image number and load state */
278 if(find_image(*id
, wps_data
))
280 /* Invalid image ID */
281 return WPS_ERROR_INVALID_PARAM
;
283 img
= (struct gui_img
*)skin_buffer_alloc(sizeof(struct gui_img
));
285 return WPS_ERROR_INVALID_PARAM
;
286 /* save a pointer to the filename */
287 img
->bm
.data
= (char*)filename
;
291 img
->num_subimages
= 1;
292 img
->always_display
= false;
293 // img->just_drawn = false;
296 /* save current viewport */
297 img
->vp
= &curr_vp
->vp
;
299 if (token
->type
== SKIN_TOKEN_IMAGE_DISPLAY
)
301 img
->always_display
= true;
303 else if (element
->params_count
== 5)
305 img
->num_subimages
= element
->params
[4].data
.number
;
306 if (img
->num_subimages
<= 0)
307 return WPS_ERROR_INVALID_PARAM
;
309 struct skin_token_list
*item
=
310 (struct skin_token_list
*)new_skin_token_list_item(NULL
, img
);
312 return WPS_ERROR_INVALID_PARAM
;
313 add_to_ll_chain(&wps_data
->images
, item
);
318 int id
; /* the id from font_load */
319 char *name
; /* filename without path and extension */
321 static struct skin_font skinfonts
[MAXUSERFONTS
];
322 static int parse_font_load(struct skin_element
*element
,
323 struct wps_token
*token
,
324 struct wps_data
*wps_data
)
326 (void)wps_data
; (void)token
;
327 int id
= element
->params
[0].data
.number
;
328 char *filename
= element
->params
[1].data
.text
;
331 #if defined(DEBUG) || defined(SIMULATOR)
332 if (skinfonts
[id
-FONT_FIRSTUSERFONT
].name
!= NULL
)
334 DEBUGF("font id %d already being used\n", id
);
337 /* make sure the filename contains .fnt,
338 * we dont actually use it, but require it anyway */
339 ptr
= strchr(filename
, '.');
340 if (!ptr
|| strncmp(ptr
, ".fnt", 4))
341 return WPS_ERROR_INVALID_PARAM
;
342 skinfonts
[id
-FONT_FIRSTUSERFONT
].id
= -1;
343 skinfonts
[id
-FONT_FIRSTUSERFONT
].name
= filename
;
349 #ifdef HAVE_LCD_BITMAP
351 static int parse_playlistview(struct skin_element
*element
,
352 struct wps_token
*token
,
353 struct wps_data
*wps_data
)
356 struct playlistviewer
*viewer
=
357 (struct playlistviewer
*)skin_buffer_alloc(sizeof(struct playlistviewer
));
359 return WPS_ERROR_INVALID_PARAM
;
360 viewer
->vp
= &curr_vp
->vp
;
361 viewer
->show_icons
= true;
362 viewer
->start_offset
= element
->params
[0].data
.number
;
363 viewer
->lines
[0] = element
->params
[1].data
.code
;
364 viewer
->lines
[1] = element
->params
[2].data
.code
;
366 token
->value
.data
= (void*)viewer
;
372 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
374 static int parse_viewportcolour(struct skin_element
*element
,
375 struct wps_token
*token
,
376 struct wps_data
*wps_data
)
379 struct skin_tag_parameter
*param
= element
->params
;
380 struct viewport_colour
*colour
=
381 (struct viewport_colour
*)skin_buffer_alloc(sizeof(struct viewport_colour
));
384 if (isdefault(param
))
386 colour
->colour
= get_viewport_default_colour(curr_screen
,
387 token
->type
== SKIN_TOKEN_VIEWPORT_FGCOLOUR
);
391 if (!parse_color(param
->data
.text
, &colour
->colour
))
394 colour
->vp
= &curr_vp
->vp
;
395 token
->value
.data
= colour
;
396 if (element
->line
== curr_viewport_element
->line
)
398 if (token
->type
== SKIN_TOKEN_VIEWPORT_FGCOLOUR
)
400 curr_vp
->start_fgcolour
= colour
->colour
;
401 curr_vp
->vp
.fg_pattern
= colour
->colour
;
405 curr_vp
->start_bgcolour
= colour
->colour
;
406 curr_vp
->vp
.bg_pattern
= colour
->colour
;
412 static int parse_image_special(struct skin_element
*element
,
413 struct wps_token
*token
,
414 struct wps_data
*wps_data
)
416 (void)wps_data
; /* kill warning */
421 if (token
->type
== SKIN_TOKEN_IMAGE_BACKDROP
)
423 char *filename
= element
->params
[0].data
.text
;
424 /* format: %X|filename.bmp| or %Xd */
425 if (!strcmp(filename
, "d"))
427 wps_data
->backdrop
= NULL
;
432 wps_data
->backdrop
= filename
;
436 /* Skip the rest of the line */
437 return error
? WPS_ERROR_INVALID_PARAM
: 0;
441 #endif /* HAVE_LCD_BITMAP */
443 static int parse_setting_and_lang(struct skin_element
*element
,
444 struct wps_token
*token
,
445 struct wps_data
*wps_data
)
447 /* NOTE: both the string validations that happen in here will
448 * automatically PASS on checkwps because its too hard to get
449 * settings_list.c and english.lang built for it.
450 * If that ever changes remove the #ifndef __PCTOOL__'s here
453 char *temp
= element
->params
[0].data
.text
;
456 if (token
->type
== SKIN_TOKEN_TRANSLATEDSTRING
)
459 i
= lang_english_to_id(temp
);
461 return WPS_ERROR_INVALID_PARAM
;
466 /* Find the setting */
467 for (i
=0; i
<nb_settings
; i
++)
468 if (settings
[i
].cfg_name
&&
469 !strcmp(settings
[i
].cfg_name
, temp
))
472 if (i
== nb_settings
)
473 return WPS_ERROR_INVALID_PARAM
;
476 /* Store the setting number */
481 static int parse_timeout_tag(struct skin_element
*element
,
482 struct wps_token
*token
,
483 struct wps_data
*wps_data
)
487 if (element
->params_count
== 0)
491 case SKIN_TOKEN_SUBLINE_TIMEOUT
:
493 case SKIN_TOKEN_BUTTON_VOLUME
:
494 case SKIN_TOKEN_TRACK_STARTING
:
495 case SKIN_TOKEN_TRACK_ENDING
:
496 case SKIN_TOKEN_LASTTOUCH
:
504 val
= element
->params
[0].data
.number
;
505 token
->value
.i
= val
* TIMEOUT_UNIT
;
509 static int parse_progressbar_tag(struct skin_element
* element
,
510 struct wps_token
*token
,
511 struct wps_data
*wps_data
)
513 #ifdef HAVE_LCD_BITMAP
514 struct progressbar
*pb
;
515 struct skin_token_list
*item
;
516 struct viewport
*vp
= &curr_vp
->vp
;
517 struct skin_tag_parameter
*param
= element
->params
;
519 if (element
->params_count
== 0 &&
520 element
->tag
->type
!= SKIN_TOKEN_PROGRESSBAR
)
521 return 0; /* nothing to do */
522 pb
= (struct progressbar
*)skin_buffer_alloc(sizeof(struct progressbar
));
524 token
->value
.data
= pb
;
527 return WPS_ERROR_INVALID_PARAM
;
529 pb
->have_bitmap_pb
= false;
530 pb
->bm
.data
= NULL
; /* no bitmap specified */
531 pb
->follow_lang_direction
= follow_lang_direction
> 0;
533 if (element
->params_count
== 0)
536 pb
->width
= vp
->width
;
537 pb
->height
= SYSFONT_HEIGHT
-2;
538 pb
->y
= -1; /* Will be computed during the rendering */
539 pb
->type
= element
->tag
->type
;
543 item
= new_skin_token_list_item(token
, pb
);
546 add_to_ll_chain(&wps_data
->progressbars
, item
);
548 /* (x,y,width,height,filename) */
549 if (!isdefault(param
))
550 pb
->x
= param
->data
.number
;
555 if (!isdefault(param
))
556 pb
->y
= param
->data
.number
;
558 pb
->y
= -1; /* computed at rendering */
561 if (!isdefault(param
))
562 pb
->width
= param
->data
.number
;
564 pb
->width
= vp
->width
- pb
->x
;
567 if (!isdefault(param
))
569 /* A zero height makes no sense - reject it */
570 if (param
->data
.number
== 0)
571 return WPS_ERROR_INVALID_PARAM
;
573 pb
->height
= param
->data
.number
;
577 if (vp
->font
> FONT_UI
)
578 pb
->height
= -1; /* calculate at display time */
582 pb
->height
= font_get(vp
->font
)->height
;
589 if (!isdefault(param
))
590 pb
->bm
.data
= param
->data
.text
;
593 if (token
->type
== SKIN_TOKEN_VOLUME
)
594 token
->type
= SKIN_TOKEN_VOLUMEBAR
;
595 else if (token
->type
== SKIN_TOKEN_BATTERY_PERCENT
)
596 token
->type
= SKIN_TOKEN_BATTERY_PERCENTBAR
;
597 pb
->type
= token
->type
;
603 if (token
->type
== SKIN_TOKEN_PROGRESSBAR
||
604 token
->type
== SKIN_TOKEN_PLAYER_PROGRESSBAR
)
606 wps_data
->full_line_progressbar
=
607 token
->type
== SKIN_TOKEN_PLAYER_PROGRESSBAR
;
615 static int parse_albumart_load(struct skin_element
* element
,
616 struct wps_token
*token
,
617 struct wps_data
*wps_data
)
619 struct dim dimensions
;
621 bool swap_for_rtl
= lang_is_rtl() && follow_lang_direction
;
622 struct skin_albumart
*aa
=
623 (struct skin_albumart
*)skin_buffer_alloc(sizeof(struct skin_albumart
));
624 (void)token
; /* silence warning */
628 /* reset albumart info in wps */
631 aa
->xalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
632 aa
->yalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
634 aa
->x
= element
->params
[0].data
.number
;
635 aa
->y
= element
->params
[1].data
.number
;
636 aa
->width
= element
->params
[2].data
.number
;
637 aa
->height
= element
->params
[3].data
.number
;
639 aa
->vp
= &curr_vp
->vp
;
640 aa
->draw_handle
= -1;
642 /* if we got here, we parsed everything ok .. ! */
645 else if (aa
->width
> LCD_WIDTH
)
646 aa
->width
= LCD_WIDTH
;
650 else if (aa
->height
> LCD_HEIGHT
)
651 aa
->height
= LCD_HEIGHT
;
654 aa
->x
= LCD_WIDTH
- (aa
->x
+ aa
->width
);
656 aa
->state
= WPS_ALBUMART_LOAD
;
657 wps_data
->albumart
= aa
;
659 dimensions
.width
= aa
->width
;
660 dimensions
.height
= aa
->height
;
662 albumart_slot
= playback_claim_aa_slot(&dimensions
);
664 if (0 <= albumart_slot
)
665 wps_data
->playback_aa_slot
= albumart_slot
;
667 if (element
->params_count
> 4 && !isdefault(&element
->params
[4]))
669 switch (*element
->params
[4].data
.text
)
674 aa
->xalign
= WPS_ALBUMART_ALIGN_RIGHT
;
676 aa
->xalign
= WPS_ALBUMART_ALIGN_LEFT
;
680 aa
->xalign
= WPS_ALBUMART_ALIGN_CENTER
;
685 aa
->xalign
= WPS_ALBUMART_ALIGN_LEFT
;
687 aa
->xalign
= WPS_ALBUMART_ALIGN_RIGHT
;
691 if (element
->params_count
> 5 && !isdefault(&element
->params
[5]))
693 switch (*element
->params
[5].data
.text
)
697 aa
->yalign
= WPS_ALBUMART_ALIGN_TOP
;
701 aa
->yalign
= WPS_ALBUMART_ALIGN_CENTER
;
705 aa
->yalign
= WPS_ALBUMART_ALIGN_BOTTOM
;
712 #endif /* HAVE_ALBUMART */
714 #ifdef HAVE_TOUCHSCREEN
716 struct touchaction
{const char* s
; int action
;};
717 static const struct touchaction touchactions
[] = {
718 /* generic actions, convert to screen actions on use */
719 {"prev", ACTION_STD_PREV
}, {"next", ACTION_STD_NEXT
},
720 {"rwd", ACTION_STD_PREVREPEAT
}, {"ffwd", ACTION_STD_NEXTREPEAT
},
721 {"hotkey", ACTION_STD_HOTKEY
}, {"select", ACTION_STD_OK
},
722 {"menu", ACTION_STD_MENU
}, {"cancel", ACTION_STD_CANCEL
},
723 {"contextmenu", ACTION_STD_CONTEXT
},{"quickscreen", ACTION_STD_QUICKSCREEN
},
724 /* not really WPS specific, but no equivilant ACTION_STD_* */
725 {"voldown", ACTION_WPS_VOLDOWN
}, {"volup", ACTION_WPS_VOLUP
},
727 /* WPS specific actions */
728 {"browse", ACTION_WPS_BROWSE
},
729 {"play", ACTION_WPS_PLAY
}, {"stop", ACTION_WPS_STOP
},
730 {"shuffle", ACTION_TOUCH_SHUFFLE
}, {"repmode", ACTION_TOUCH_REPMODE
},
731 {"pitch", ACTION_WPS_PITCHSCREEN
}, {"playlist", ACTION_WPS_VIEW_PLAYLIST
},
734 /* FM screen actions */
735 /* Also allow browse, play, stop from WPS codes */
736 {"mode", ACTION_FM_MODE
}, {"record", ACTION_FM_RECORD
},
737 {"presets", ACTION_FM_PRESET
},
741 static int parse_touchregion(struct skin_element
*element
,
742 struct wps_token
*token
,
743 struct wps_data
*wps_data
)
747 struct touchregion
*region
= NULL
;
749 const char pb_string
[] = "progressbar";
750 const char vol_string
[] = "volume";
753 /* format: %T(x,y,width,height,action)
754 * if action starts with & the area must be held to happen
756 * play - play/pause playback
757 * stop - stop playback, exit the wps
760 * ffwd - seek forward
761 * rwd - seek backwards
762 * menu - go back to the main menu
763 * browse - go back to the file/db browser
764 * shuffle - toggle shuffle mode
765 * repmode - cycle the repeat mode
766 * quickscreen - go into the quickscreen
767 * contextmenu - open the context menu
768 * playlist - go into the playlist
769 * pitch - go into the pitchscreen
770 * volup - increase volume by one step
771 * voldown - decrease volume by one step
775 region
= (struct touchregion
*)skin_buffer_alloc(sizeof(struct touchregion
));
777 return WPS_ERROR_INVALID_PARAM
;
779 /* should probably do some bounds checking here with the viewport... but later */
780 region
->action
= ACTION_NONE
;
781 region
->x
= element
->params
[0].data
.number
;
782 region
->y
= element
->params
[1].data
.number
;
783 region
->width
= element
->params
[2].data
.number
;
784 region
->height
= element
->params
[3].data
.number
;
785 region
->wvp
= curr_vp
;
786 region
->armed
= false;
787 region
->reverse_bar
= false;
788 action
= element
->params
[4].data
.text
;
790 strcpy(temp
, action
);
795 region
->reverse_bar
= true;
799 if(!strcmp(pb_string
, action
))
800 region
->type
= WPS_TOUCHREGION_SCROLLBAR
;
801 else if(!strcmp(vol_string
, action
))
802 region
->type
= WPS_TOUCHREGION_VOLUME
;
805 region
->type
= WPS_TOUCHREGION_ACTION
;
810 region
->repeat
= true;
813 region
->repeat
= false;
815 imax
= ARRAYLEN(touchactions
);
816 for (i
= 0; i
< imax
; i
++)
818 /* try to match with one of our touchregion screens */
819 if (!strcmp(touchactions
[i
].s
, action
))
821 region
->action
= touchactions
[i
].action
;
825 if (region
->action
== ACTION_NONE
)
826 return WPS_ERROR_INVALID_PARAM
;
828 struct skin_token_list
*item
= new_skin_token_list_item(NULL
, region
);
830 return WPS_ERROR_INVALID_PARAM
;
831 add_to_ll_chain(&wps_data
->touchregions
, item
);
836 static bool check_feature_tag(const int type
)
840 case SKIN_TOKEN_RTC_PRESENT
:
846 case SKIN_TOKEN_HAVE_RECORDING
:
847 #ifdef HAVE_RECORDING
852 case SKIN_TOKEN_HAVE_TUNER
:
854 if (radio_hardware_present())
860 case SKIN_TOKEN_HAVE_RDS
:
865 #endif /* HAVE_RDS_CAP */
866 #endif /* CONFIG_TUNER */
867 default: /* not a tag we care about, just don't skip */
873 * initial setup of wps_data; does reset everything
874 * except fields which need to survive, i.e.
877 static void skin_data_reset(struct wps_data
*wps_data
)
879 wps_data
->tree
= NULL
;
880 #ifdef HAVE_LCD_BITMAP
881 wps_data
->images
= NULL
;
882 wps_data
->progressbars
= NULL
;
884 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
885 wps_data
->backdrop
= NULL
;
887 #ifdef HAVE_TOUCHSCREEN
888 wps_data
->touchregions
= NULL
;
891 wps_data
->albumart
= NULL
;
892 if (wps_data
->playback_aa_slot
>= 0)
894 playback_release_aa_slot(wps_data
->playback_aa_slot
);
895 wps_data
->playback_aa_slot
= -1;
899 #ifdef HAVE_LCD_BITMAP
900 wps_data
->peak_meter_enabled
= false;
901 wps_data
->wps_sb_tag
= false;
902 wps_data
->show_sb_on_wps
= false;
903 #else /* HAVE_LCD_CHARCELLS */
906 for (i
= 0; i
< 8; i
++)
908 wps_data
->wps_progress_pat
[i
] = 0;
910 wps_data
->full_line_progressbar
= false;
912 wps_data
->wps_loaded
= false;
915 #ifdef HAVE_LCD_BITMAP
916 static bool load_skin_bmp(struct wps_data
*wps_data
, struct bitmap
*bitmap
, char* bmpdir
)
918 (void)wps_data
; /* only needed for remote targets */
919 char img_path
[MAX_PATH
];
921 get_image_filename(bitmap
->data
, bmpdir
,
922 img_path
, sizeof(img_path
));
926 #ifdef HAVE_REMOTE_LCD
927 if (curr_screen
== SCREEN_REMOTE
)
928 format
= FORMAT_ANY
|FORMAT_REMOTE
;
931 format
= FORMAT_ANY
|FORMAT_TRANSPARENT
;
933 fd
= open(img_path
, O_RDONLY
);
936 size_t buf_size
= read_bmp_file(img_path
, bitmap
, 0,
937 format
|FORMAT_RETURN_SIZE
, NULL
);
938 char* imgbuf
= (char*)skin_buffer_alloc(buf_size
);
944 lseek(fd
, 0, SEEK_SET
);
945 bitmap
->data
= imgbuf
;
946 int ret
= read_bmp_fd(fd
, bitmap
, buf_size
, format
, NULL
);
955 /* Abort if we can't load an image */
956 DEBUGF("Couldn't load '%s'\n", img_path
);
961 static bool load_skin_bitmaps(struct wps_data
*wps_data
, char *bmpdir
)
963 struct skin_token_list
*list
;
964 bool retval
= true; /* return false if a single image failed to load */
965 /* do the progressbars */
966 list
= wps_data
->progressbars
;
969 struct progressbar
*pb
= (struct progressbar
*)list
->token
->value
.data
;
972 pb
->have_bitmap_pb
= load_skin_bmp(wps_data
, &pb
->bm
, bmpdir
);
973 if (!pb
->have_bitmap_pb
) /* no success */
979 list
= wps_data
->images
;
982 struct gui_img
*img
= (struct gui_img
*)list
->token
->value
.data
;
985 img
->loaded
= load_skin_bmp(wps_data
, &img
->bm
, bmpdir
);
987 img
->subimage_height
= img
->bm
.height
/ img
->num_subimages
;
994 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
995 /* Backdrop load scheme:
997 * 2) load the backdrop from settings
999 if (wps_data
->backdrop
)
1001 if (screens
[curr_screen
].depth
== 1)
1003 wps_data
->backdrop
= NULL
;
1006 bool needed
= wps_data
->backdrop
[0] != '-';
1007 wps_data
->backdrop
= skin_backdrop_load(wps_data
->backdrop
,
1008 bmpdir
, curr_screen
);
1009 if (!wps_data
->backdrop
&& needed
)
1012 #endif /* has backdrop support */
1017 static bool skin_load_fonts(struct wps_data
*data
)
1019 /* don't spit out after the first failue to aid debugging */
1020 bool success
= true;
1021 struct skin_element
*vp_list
;
1023 /* walk though each viewport and assign its font */
1024 for(vp_list
= data
->tree
; vp_list
; vp_list
= vp_list
->next
)
1026 /* first, find the viewports that have a non-sys/ui-font font */
1027 struct skin_viewport
*skin_vp
=
1028 (struct skin_viewport
*)vp_list
->data
;
1029 struct viewport
*vp
= &skin_vp
->vp
;
1032 if (vp
->font
<= FONT_UI
)
1033 { /* the usual case -> built-in fonts */
1034 #ifdef HAVE_REMOTE_LCD
1035 if (vp
->font
== FONT_UI
)
1036 vp
->font
+= curr_screen
;
1042 /* now find the corresponding skin_font */
1043 struct skin_font
*font
= &skinfonts
[font_id
-FONT_FIRSTUSERFONT
];
1048 DEBUGF("font %d not specified\n", font_id
);
1054 /* load the font - will handle loading the same font again if
1055 * multiple viewports use the same */
1058 char *dot
= strchr(font
->name
, '.');
1060 font
->id
= skin_font_load(font
->name
);
1065 DEBUGF("Unable to load font %d: '%s.fnt'\n",
1066 font_id
, font
->name
);
1067 font
->name
= NULL
; /* to stop trying to load it again if we fail */
1073 /* finally, assign the font_id to the viewport */
1074 vp
->font
= font
->id
;
1079 #endif /* HAVE_LCD_BITMAP */
1080 static int convert_viewport(struct wps_data
*data
, struct skin_element
* element
)
1082 struct skin_viewport
*skin_vp
=
1083 (struct skin_viewport
*)skin_buffer_alloc(sizeof(struct skin_viewport
));
1084 struct screen
*display
= &screens
[curr_screen
];
1087 return CALLBACK_ERROR
;
1089 skin_vp
->hidden_flags
= 0;
1090 skin_vp
->label
= NULL
;
1091 skin_vp
->is_infovp
= false;
1092 element
->data
= skin_vp
;
1094 curr_viewport_element
= element
;
1096 viewport_set_defaults(&skin_vp
->vp
, curr_screen
);
1097 #ifdef HAVE_REMOTE_LCD
1098 /* viewport_set_defaults() sets the font to FONT_UI+curr_screen.
1099 * This parser requires font 1 to always be the UI font,
1100 * so force it back to FONT_UI and handle the screen number at the end */
1101 skin_vp
->vp
.font
= FONT_UI
;
1104 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1105 skin_vp
->start_fgcolour
= skin_vp
->vp
.fg_pattern
;
1106 skin_vp
->start_bgcolour
= skin_vp
->vp
.bg_pattern
;
1110 struct skin_tag_parameter
*param
= element
->params
;
1111 if (element
->params_count
== 0) /* default viewport */
1113 if (!data
->tree
) /* first viewport in the skin */
1114 data
->tree
= element
;
1115 skin_vp
->label
= VP_DEFAULT_LABEL
;
1119 if (element
->params_count
== 6)
1121 if (element
->tag
->type
== SKIN_TOKEN_UIVIEWPORT_LOAD
)
1123 skin_vp
->is_infovp
= true;
1124 if (isdefault(param
))
1126 skin_vp
->hidden_flags
= VP_NEVER_VISIBLE
;
1127 skin_vp
->label
= VP_DEFAULT_LABEL
;
1131 skin_vp
->hidden_flags
= VP_NEVER_VISIBLE
;
1132 skin_vp
->label
= param
->data
.text
;
1137 skin_vp
->hidden_flags
= VP_DRAW_HIDEABLE
|VP_DRAW_HIDDEN
;
1138 skin_vp
->label
= param
->data
.text
;
1143 if (!isdefault(param
))
1145 skin_vp
->vp
.x
= param
->data
.number
;
1146 if (param
->data
.number
< 0)
1147 skin_vp
->vp
.x
+= display
->lcdwidth
;
1151 if (!isdefault(param
))
1153 skin_vp
->vp
.y
= param
->data
.number
;
1154 if (param
->data
.number
< 0)
1155 skin_vp
->vp
.y
+= display
->lcdheight
;
1159 if (!isdefault(param
))
1161 skin_vp
->vp
.width
= param
->data
.number
;
1162 if (param
->data
.number
< 0)
1163 skin_vp
->vp
.width
= (skin_vp
->vp
.width
+ display
->lcdwidth
) - skin_vp
->vp
.x
;
1167 skin_vp
->vp
.width
= display
->lcdwidth
- skin_vp
->vp
.x
;
1171 if (!isdefault(param
))
1173 skin_vp
->vp
.height
= param
->data
.number
;
1174 if (param
->data
.number
< 0)
1175 skin_vp
->vp
.height
= (skin_vp
->vp
.height
+ display
->lcdheight
) - skin_vp
->vp
.y
;
1179 skin_vp
->vp
.height
= display
->lcdheight
- skin_vp
->vp
.y
;
1182 #ifdef HAVE_LCD_BITMAP
1184 if (!isdefault(param
))
1186 skin_vp
->vp
.font
= param
->data
.number
;
1194 static int skin_element_callback(struct skin_element
* element
, void* data
)
1196 struct wps_data
*wps_data
= (struct wps_data
*)data
;
1197 struct wps_token
*token
;
1198 parse_function function
= NULL
;
1200 switch (element
->type
)
1202 /* IMPORTANT: element params are shared, so copy them if needed
1203 * or use then NOW, dont presume they have a long lifespan
1207 token
= (struct wps_token
*)skin_buffer_alloc(sizeof(struct wps_token
));
1208 memset(token
, 0, sizeof(*token
));
1209 token
->type
= element
->tag
->type
;
1211 if ((element
->tag
->flags
&SKIN_REFRESH_ALL
) == SKIN_RTC_REFRESH
)
1214 curr_line
->update_mode
|= SKIN_REFRESH_DYNAMIC
;
1216 curr_line
->update_mode
|= SKIN_REFRESH_STATIC
;
1220 curr_line
->update_mode
|= element
->tag
->flags
&SKIN_REFRESH_ALL
;
1222 element
->data
= token
;
1224 /* Some tags need special handling for the tag, so add them here */
1225 switch (token
->type
)
1227 case SKIN_TOKEN_ALIGN_LANGDIRECTION
:
1228 follow_lang_direction
= 2;
1230 case SKIN_TOKEN_PROGRESSBAR
:
1231 case SKIN_TOKEN_VOLUME
:
1232 case SKIN_TOKEN_BATTERY_PERCENT
:
1233 case SKIN_TOKEN_PLAYER_PROGRESSBAR
:
1234 function
= parse_progressbar_tag
;
1236 case SKIN_TOKEN_SUBLINE_TIMEOUT
:
1237 case SKIN_TOKEN_BUTTON_VOLUME
:
1238 case SKIN_TOKEN_TRACK_STARTING
:
1239 case SKIN_TOKEN_TRACK_ENDING
:
1240 case SKIN_TOKEN_LASTTOUCH
:
1241 function
= parse_timeout_tag
;
1243 #ifdef HAVE_LCD_BITMAP
1244 case SKIN_TOKEN_DISABLE_THEME
:
1245 case SKIN_TOKEN_ENABLE_THEME
:
1246 case SKIN_TOKEN_DRAW_INBUILTBAR
:
1247 function
= parse_statusbar_tags
;
1250 case SKIN_TOKEN_FILE_DIRECTORY
:
1251 token
->value
.i
= element
->params
[0].data
.number
;
1253 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1254 case SKIN_TOKEN_VIEWPORT_FGCOLOUR
:
1255 case SKIN_TOKEN_VIEWPORT_BGCOLOUR
:
1256 function
= parse_viewportcolour
;
1258 case SKIN_TOKEN_IMAGE_BACKDROP
:
1259 function
= parse_image_special
;
1262 case SKIN_TOKEN_TRANSLATEDSTRING
:
1263 case SKIN_TOKEN_SETTING
:
1264 function
= parse_setting_and_lang
;
1266 #ifdef HAVE_LCD_BITMAP
1267 case SKIN_TOKEN_VIEWPORT_CUSTOMLIST
:
1268 function
= parse_playlistview
;
1270 case SKIN_TOKEN_LOAD_FONT
:
1271 function
= parse_font_load
;
1273 case SKIN_TOKEN_VIEWPORT_ENABLE
:
1274 case SKIN_TOKEN_UIVIEWPORT_ENABLE
:
1275 token
->value
.data
= element
->params
[0].data
.text
;
1277 case SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY
:
1278 function
= parse_image_display
;
1280 case SKIN_TOKEN_IMAGE_PRELOAD
:
1281 case SKIN_TOKEN_IMAGE_DISPLAY
:
1282 function
= parse_image_load
;
1285 #ifdef HAVE_TOUCHSCREEN
1286 case SKIN_TOKEN_TOUCHREGION
:
1287 function
= parse_touchregion
;
1290 #ifdef HAVE_ALBUMART
1291 case SKIN_TOKEN_ALBUMART_DISPLAY
:
1292 if (wps_data
->albumart
)
1293 wps_data
->albumart
->vp
= &curr_vp
->vp
;
1295 case SKIN_TOKEN_ALBUMART_LOAD
:
1296 function
= parse_albumart_load
;
1304 if (function(element
, token
, wps_data
) < 0)
1305 return CALLBACK_ERROR
;
1307 /* tags that start with 'F', 'I' or 'D' are for the next file */
1308 if ( *(element
->tag
->name
) == 'I' || *(element
->tag
->name
) == 'F' ||
1309 *(element
->tag
->name
) == 'D')
1311 if (follow_lang_direction
> 0 )
1312 follow_lang_direction
--;
1316 return convert_viewport(wps_data
, element
);
1320 (struct line
*)skin_buffer_alloc(sizeof(struct line
));
1321 line
->update_mode
= SKIN_REFRESH_STATIC
;
1322 line
->timeout
= DEFAULT_SUBLINE_TIME_MULTIPLIER
* TIMEOUT_UNIT
;
1324 element
->data
= line
;
1327 case LINE_ALTERNATOR
:
1329 struct line_alternator
*alternator
=
1330 (struct line_alternator
*)skin_buffer_alloc(sizeof(struct line_alternator
));
1331 alternator
->current_line
= 0;
1333 alternator
->last_change_tick
= current_tick
;
1335 element
->data
= alternator
;
1340 struct conditional
*conditional
=
1341 (struct conditional
*)skin_buffer_alloc(sizeof(struct conditional
));
1342 conditional
->last_value
= -1;
1343 conditional
->token
= element
->data
;
1344 element
->data
= conditional
;
1345 if (!check_feature_tag(element
->tag
->type
))
1347 return FEATURE_NOT_AVAILABLE
;
1352 curr_line
->update_mode
|= SKIN_REFRESH_STATIC
;
1360 /* to setup up the wps-data from a format-buffer (isfile = false)
1361 from a (wps-)file (isfile = true)*/
1362 bool skin_data_load(enum screen_type screen
, struct wps_data
*wps_data
,
1363 const char *buf
, bool isfile
)
1365 char *wps_buffer
= NULL
;
1366 if (!wps_data
|| !buf
)
1368 #ifdef HAVE_ALBUMART
1370 struct mp3entry
*curtrack
;
1372 struct skin_albumart old_aa
= {.state
= WPS_ALBUMART_NONE
};
1373 if (wps_data
->albumart
)
1375 old_aa
.state
= wps_data
->albumart
->state
;
1376 old_aa
.height
= wps_data
->albumart
->height
;
1377 old_aa
.width
= wps_data
->albumart
->width
;
1380 #ifdef HAVE_LCD_BITMAP
1382 for (i
=0;i
<MAXUSERFONTS
;i
++)
1384 skinfonts
[i
].id
= -1;
1385 skinfonts
[i
].name
= NULL
;
1388 #ifdef DEBUG_SKIN_ENGINE
1389 if (isfile
&& debug_wps
)
1391 DEBUGF("\n=====================\nLoading '%s'\n=====================\n", buf
);
1396 skin_data_reset(wps_data
);
1397 wps_data
->wps_loaded
= false;
1398 curr_screen
= screen
;
1401 curr_viewport_element
= NULL
;
1405 int fd
= open_utf8(buf
, O_RDONLY
);
1410 /* get buffer space from the plugin buffer */
1411 size_t buffersize
= 0;
1412 wps_buffer
= (char *)plugin_get_buffer(&buffersize
);
1417 /* copy the file's content to the buffer for parsing,
1418 ensuring that every line ends with a newline char. */
1419 unsigned int start
= 0;
1420 while(read_line(fd
, wps_buffer
+ start
, buffersize
- start
) > 0)
1422 start
+= strlen(wps_buffer
+ start
);
1423 if (start
< buffersize
- 1)
1425 wps_buffer
[start
++] = '\n';
1426 wps_buffer
[start
] = 0;
1435 wps_buffer
= (char*)buf
;
1437 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1438 wps_data
->backdrop
= "-";
1440 /* parse the skin source */
1441 skin_buffer_save_position();
1442 wps_data
->tree
= skin_parse(wps_buffer
, skin_element_callback
, wps_data
);
1443 if (!wps_data
->tree
) {
1444 skin_data_reset(wps_data
);
1445 skin_buffer_restore_position();
1449 #ifdef HAVE_LCD_BITMAP
1450 char bmpdir
[MAX_PATH
];
1453 /* get the bitmap dir */
1454 char *dot
= strrchr(buf
, '.');
1455 strlcpy(bmpdir
, buf
, dot
- buf
+ 1);
1458 { /* fall back to backdrop dir for built-in themes */
1459 /* no get_user_file_path(), assuming we ship bmps for built-in themes */
1460 snprintf(bmpdir
, MAX_PATH
, "%s", BACKDROP_DIR
);
1462 /* load the bitmaps that were found by the parsing */
1463 if (!load_skin_bitmaps(wps_data
, bmpdir
) ||
1464 !skin_load_fonts(wps_data
))
1466 skin_data_reset(wps_data
);
1467 skin_buffer_restore_position();
1471 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
1472 status
= audio_status();
1473 if (status
& AUDIO_STATUS_PLAY
)
1475 struct skin_albumart
*aa
= wps_data
->albumart
;
1476 if (aa
&& ((aa
->state
&& !old_aa
.state
) ||
1478 (((old_aa
.height
!= aa
->height
) ||
1479 (old_aa
.width
!= aa
->width
))))))
1481 curtrack
= audio_current_track();
1482 offset
= curtrack
->offset
;
1484 if (!(status
& AUDIO_STATUS_PAUSE
))
1489 wps_data
->wps_loaded
= true;
1490 #ifdef DEBUG_SKIN_ENGINE
1491 // if (isfile && debug_wps)
1492 // debug_skin_usage();