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
, 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
== label
)
148 #ifdef HAVE_LCD_BITMAP
150 /* create and init a new wpsll item.
151 * passing NULL to token will alloc a new one.
152 * You should only pass NULL for the token when the token type (table above)
153 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
155 static struct skin_token_list
*new_skin_token_list_item(struct wps_token
*token
,
158 struct skin_token_list
*llitem
=
159 (struct skin_token_list
*)skin_buffer_alloc(sizeof(struct skin_token_list
));
161 token
= (struct wps_token
*)skin_buffer_alloc(sizeof(struct wps_token
));
162 if (!llitem
|| !token
)
165 llitem
->token
= token
;
167 llitem
->token
->value
.data
= token_data
;
171 static int parse_statusbar_tags(struct skin_element
* element
,
172 struct wps_token
*token
,
173 struct wps_data
*wps_data
)
176 if (token
->type
== SKIN_TOKEN_DRAW_INBUILTBAR
)
178 token
->value
.data
= (void*)&curr_vp
->vp
;
182 struct skin_element
*def_vp
= wps_data
->tree
;
183 struct skin_viewport
*default_vp
= def_vp
->data
;
184 if (def_vp
->params_count
== 0)
186 wps_data
->wps_sb_tag
= true;
187 wps_data
->show_sb_on_wps
= (token
->type
== SKIN_TOKEN_ENABLE_THEME
);
189 if (wps_data
->show_sb_on_wps
)
191 viewport_set_defaults(&default_vp
->vp
, curr_screen
);
195 viewport_set_fullscreen(&default_vp
->vp
, curr_screen
);
197 #ifdef HAVE_REMOTE_LCD
198 /* viewport_set_defaults() sets the font to FONT_UI+curr_screen.
199 * This parser requires font 1 to always be the UI font,
200 * so force it back to FONT_UI and handle the screen number at the end */
201 default_vp
->vp
.font
= FONT_UI
;
207 static int get_image_id(int c
)
209 if(c
>= 'a' && c
<= 'z')
211 else if(c
>= 'A' && c
<= 'Z')
217 char *get_image_filename(const char *start
, const char* bmpdir
,
218 char *buf
, int buf_size
)
220 snprintf(buf
, buf_size
, "%s/%s", bmpdir
, start
);
225 static int parse_image_display(struct skin_element
*element
,
226 struct wps_token
*token
,
227 struct wps_data
*wps_data
)
229 char *text
= element
->params
[0].data
.text
;
230 char label
= text
[0];
231 char sublabel
= text
[1];
236 img
= find_image(label
, wps_data
);
239 token
->value
.i
= label
; /* so debug works */
240 return WPS_ERROR_INVALID_PARAM
;
243 if ((subimage
= get_image_id(sublabel
)) != -1)
245 if (subimage
>= img
->num_subimages
)
246 return WPS_ERROR_INVALID_PARAM
;
248 /* Store sub-image number to display in high bits */
249 token
->value
.i
= label
| (subimage
<< 8);
250 return 4; /* We have consumed 2 bytes */
252 token
->value
.i
= label
;
253 return 3; /* We have consumed 1 byte */
257 static int parse_image_load(struct skin_element
*element
,
258 struct wps_token
*token
,
259 struct wps_data
*wps_data
)
261 const char* filename
;
266 /* format: %x(n,filename.bmp,x,y)
267 or %xl(n,filename.bmp,x,y)
268 or %xl(n,filename.bmp,x,y,num_subimages)
271 id
= element
->params
[0].data
.text
;
272 filename
= element
->params
[1].data
.text
;
273 x
= element
->params
[2].data
.number
;
274 y
= element
->params
[3].data
.number
;
276 /* check the image number and load state */
277 if(find_image(*id
, wps_data
))
279 /* Invalid image ID */
280 return WPS_ERROR_INVALID_PARAM
;
282 img
= (struct gui_img
*)skin_buffer_alloc(sizeof(struct gui_img
));
284 return WPS_ERROR_INVALID_PARAM
;
285 /* save a pointer to the filename */
286 img
->bm
.data
= (char*)filename
;
290 img
->num_subimages
= 1;
291 img
->always_display
= false;
292 // img->just_drawn = false;
295 /* save current viewport */
296 img
->vp
= &curr_vp
->vp
;
298 if (token
->type
== SKIN_TOKEN_IMAGE_DISPLAY
)
300 img
->always_display
= true;
302 else if (element
->params_count
== 5)
304 img
->num_subimages
= element
->params
[4].data
.number
;
305 if (img
->num_subimages
<= 0)
306 return WPS_ERROR_INVALID_PARAM
;
308 struct skin_token_list
*item
=
309 (struct skin_token_list
*)new_skin_token_list_item(NULL
, img
);
311 return WPS_ERROR_INVALID_PARAM
;
312 add_to_ll_chain(&wps_data
->images
, item
);
317 int id
; /* the id from font_load */
318 char *name
; /* filename without path and extension */
320 static struct skin_font skinfonts
[MAXUSERFONTS
];
321 static int parse_font_load(struct skin_element
*element
,
322 struct wps_token
*token
,
323 struct wps_data
*wps_data
)
325 (void)wps_data
; (void)token
;
326 int id
= element
->params
[0].data
.number
;
327 char *filename
= element
->params
[1].data
.text
;
330 #if defined(DEBUG) || defined(SIMULATOR)
331 if (skinfonts
[id
-FONT_FIRSTUSERFONT
].name
!= NULL
)
333 DEBUGF("font id %d already being used\n", id
);
336 /* make sure the filename contains .fnt,
337 * we dont actually use it, but require it anyway */
338 ptr
= strchr(filename
, '.');
339 if (!ptr
|| strncmp(ptr
, ".fnt", 4))
340 return WPS_ERROR_INVALID_PARAM
;
341 skinfonts
[id
-FONT_FIRSTUSERFONT
].id
= -1;
342 skinfonts
[id
-FONT_FIRSTUSERFONT
].name
= filename
;
348 #ifdef HAVE_LCD_BITMAP
350 static int parse_playlistview(struct skin_element
*element
,
351 struct wps_token
*token
,
352 struct wps_data
*wps_data
)
355 struct playlistviewer
*viewer
=
356 (struct playlistviewer
*)skin_buffer_alloc(sizeof(struct playlistviewer
));
358 return WPS_ERROR_INVALID_PARAM
;
359 viewer
->vp
= &curr_vp
->vp
;
360 viewer
->show_icons
= true;
361 viewer
->start_offset
= element
->params
[0].data
.number
;
362 viewer
->lines
[0] = element
->params
[1].data
.code
;
363 viewer
->lines
[1] = element
->params
[2].data
.code
;
365 token
->value
.data
= (void*)viewer
;
371 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
373 static int parse_viewportcolour(struct skin_element
*element
,
374 struct wps_token
*token
,
375 struct wps_data
*wps_data
)
378 struct skin_tag_parameter
*param
= element
->params
;
379 struct viewport_colour
*colour
=
380 (struct viewport_colour
*)skin_buffer_alloc(sizeof(struct viewport_colour
));
383 if (isdefault(param
))
385 colour
->colour
= get_viewport_default_colour(curr_screen
,
386 token
->type
== SKIN_TOKEN_VIEWPORT_FGCOLOUR
);
390 if (!parse_color(param
->data
.text
, &colour
->colour
))
393 colour
->vp
= &curr_vp
->vp
;
394 token
->value
.data
= colour
;
395 if (element
->line
== curr_viewport_element
->line
)
397 if (token
->type
== SKIN_TOKEN_VIEWPORT_FGCOLOUR
)
399 curr_vp
->start_fgcolour
= colour
->colour
;
400 curr_vp
->vp
.fg_pattern
= colour
->colour
;
404 curr_vp
->start_bgcolour
= colour
->colour
;
405 curr_vp
->vp
.bg_pattern
= colour
->colour
;
411 static int parse_image_special(struct skin_element
*element
,
412 struct wps_token
*token
,
413 struct wps_data
*wps_data
)
415 (void)wps_data
; /* kill warning */
420 if (token
->type
== SKIN_TOKEN_IMAGE_BACKDROP
)
422 char *filename
= element
->params
[0].data
.text
;
423 /* format: %X|filename.bmp| or %Xd */
424 if (!strcmp(filename
, "d"))
426 wps_data
->backdrop
= NULL
;
431 wps_data
->backdrop
= filename
;
435 /* Skip the rest of the line */
436 return error
? WPS_ERROR_INVALID_PARAM
: 0;
440 #endif /* HAVE_LCD_BITMAP */
442 static int parse_setting_and_lang(struct skin_element
*element
,
443 struct wps_token
*token
,
444 struct wps_data
*wps_data
)
446 /* NOTE: both the string validations that happen in here will
447 * automatically PASS on checkwps because its too hard to get
448 * settings_list.c and english.lang built for it.
449 * If that ever changes remove the #ifndef __PCTOOL__'s here
452 char *temp
= element
->params
[0].data
.text
;
455 if (token
->type
== SKIN_TOKEN_TRANSLATEDSTRING
)
458 i
= lang_english_to_id(temp
);
460 return WPS_ERROR_INVALID_PARAM
;
465 /* Find the setting */
466 for (i
=0; i
<nb_settings
; i
++)
467 if (settings
[i
].cfg_name
&&
468 !strcmp(settings
[i
].cfg_name
, temp
))
471 if (i
== nb_settings
)
472 return WPS_ERROR_INVALID_PARAM
;
475 /* Store the setting number */
480 static int parse_timeout_tag(struct skin_element
*element
,
481 struct wps_token
*token
,
482 struct wps_data
*wps_data
)
486 if (element
->params_count
== 0)
490 case SKIN_TOKEN_SUBLINE_TIMEOUT
:
492 case SKIN_TOKEN_BUTTON_VOLUME
:
493 case SKIN_TOKEN_TRACK_STARTING
:
494 case SKIN_TOKEN_TRACK_ENDING
:
495 case SKIN_TOKEN_LASTTOUCH
:
503 val
= element
->params
[0].data
.number
;
504 token
->value
.i
= val
* TIMEOUT_UNIT
;
508 static int parse_progressbar_tag(struct skin_element
* element
,
509 struct wps_token
*token
,
510 struct wps_data
*wps_data
)
512 #ifdef HAVE_LCD_BITMAP
513 struct progressbar
*pb
;
514 struct skin_token_list
*item
;
515 struct viewport
*vp
= &curr_vp
->vp
;
516 struct skin_tag_parameter
*param
= element
->params
;
518 if (element
->params_count
== 0 &&
519 element
->tag
->type
!= SKIN_TOKEN_PROGRESSBAR
)
520 return 0; /* nothing to do */
521 pb
= (struct progressbar
*)skin_buffer_alloc(sizeof(struct progressbar
));
523 token
->value
.data
= pb
;
526 return WPS_ERROR_INVALID_PARAM
;
528 pb
->have_bitmap_pb
= false;
529 pb
->bm
.data
= NULL
; /* no bitmap specified */
530 pb
->follow_lang_direction
= follow_lang_direction
> 0;
532 if (element
->params_count
== 0)
535 pb
->width
= vp
->width
;
536 pb
->height
= SYSFONT_HEIGHT
-2;
537 pb
->y
= -1; /* Will be computed during the rendering */
538 pb
->type
= element
->tag
->type
;
542 item
= new_skin_token_list_item(token
, pb
);
545 add_to_ll_chain(&wps_data
->progressbars
, item
);
547 /* (x,y,width,height,filename) */
548 if (!isdefault(param
))
549 pb
->x
= param
->data
.number
;
554 if (!isdefault(param
))
555 pb
->y
= param
->data
.number
;
557 pb
->y
= -1; /* computed at rendering */
560 if (!isdefault(param
))
561 pb
->width
= param
->data
.number
;
563 pb
->width
= vp
->width
- pb
->x
;
566 if (!isdefault(param
))
568 /* A zero height makes no sense - reject it */
569 if (param
->data
.number
== 0)
570 return WPS_ERROR_INVALID_PARAM
;
572 pb
->height
= param
->data
.number
;
576 if (vp
->font
> FONT_UI
)
577 pb
->height
= -1; /* calculate at display time */
581 pb
->height
= font_get(vp
->font
)->height
;
588 if (!isdefault(param
))
589 pb
->bm
.data
= param
->data
.text
;
592 if (token
->type
== SKIN_TOKEN_VOLUME
)
593 token
->type
= SKIN_TOKEN_VOLUMEBAR
;
594 else if (token
->type
== SKIN_TOKEN_BATTERY_PERCENT
)
595 token
->type
= SKIN_TOKEN_BATTERY_PERCENTBAR
;
596 pb
->type
= token
->type
;
602 if (token
->type
== SKIN_TOKEN_PROGRESSBAR
||
603 token
->type
== SKIN_TOKEN_PLAYER_PROGRESSBAR
)
605 wps_data
->full_line_progressbar
=
606 token
->type
== SKIN_TOKEN_PLAYER_PROGRESSBAR
;
614 static int parse_albumart_load(struct skin_element
* element
,
615 struct wps_token
*token
,
616 struct wps_data
*wps_data
)
618 struct dim dimensions
;
620 bool swap_for_rtl
= lang_is_rtl() && follow_lang_direction
;
621 struct skin_albumart
*aa
=
622 (struct skin_albumart
*)skin_buffer_alloc(sizeof(struct skin_albumart
));
623 (void)token
; /* silence warning */
627 /* reset albumart info in wps */
630 aa
->xalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
631 aa
->yalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
633 aa
->x
= element
->params
[0].data
.number
;
634 aa
->y
= element
->params
[1].data
.number
;
635 aa
->width
= element
->params
[2].data
.number
;
636 aa
->height
= element
->params
[3].data
.number
;
638 aa
->vp
= &curr_vp
->vp
;
639 aa
->draw_handle
= -1;
641 /* if we got here, we parsed everything ok .. ! */
644 else if (aa
->width
> LCD_WIDTH
)
645 aa
->width
= LCD_WIDTH
;
649 else if (aa
->height
> LCD_HEIGHT
)
650 aa
->height
= LCD_HEIGHT
;
653 aa
->x
= LCD_WIDTH
- (aa
->x
+ aa
->width
);
655 aa
->state
= WPS_ALBUMART_LOAD
;
656 wps_data
->albumart
= aa
;
658 dimensions
.width
= aa
->width
;
659 dimensions
.height
= aa
->height
;
661 albumart_slot
= playback_claim_aa_slot(&dimensions
);
663 if (0 <= albumart_slot
)
664 wps_data
->playback_aa_slot
= albumart_slot
;
666 if (element
->params_count
> 4 && !isdefault(&element
->params
[4]))
668 switch (*element
->params
[4].data
.text
)
673 aa
->xalign
= WPS_ALBUMART_ALIGN_RIGHT
;
675 aa
->xalign
= WPS_ALBUMART_ALIGN_LEFT
;
679 aa
->xalign
= WPS_ALBUMART_ALIGN_CENTER
;
684 aa
->xalign
= WPS_ALBUMART_ALIGN_LEFT
;
686 aa
->xalign
= WPS_ALBUMART_ALIGN_RIGHT
;
690 if (element
->params_count
> 5 && !isdefault(&element
->params
[5]))
692 switch (*element
->params
[5].data
.text
)
696 aa
->yalign
= WPS_ALBUMART_ALIGN_TOP
;
700 aa
->yalign
= WPS_ALBUMART_ALIGN_CENTER
;
704 aa
->yalign
= WPS_ALBUMART_ALIGN_BOTTOM
;
711 #endif /* HAVE_ALBUMART */
713 #ifdef HAVE_TOUCHSCREEN
715 struct touchaction
{const char* s
; int action
;};
716 static const struct touchaction touchactions
[] = {
717 /* generic actions, convert to screen actions on use */
718 {"prev", ACTION_STD_PREV
}, {"next", ACTION_STD_NEXT
},
719 {"rwd", ACTION_STD_PREVREPEAT
}, {"ffwd", ACTION_STD_NEXTREPEAT
},
720 {"hotkey", ACTION_STD_HOTKEY
}, {"select", ACTION_STD_OK
},
721 {"menu", ACTION_STD_MENU
}, {"cancel", ACTION_STD_CANCEL
},
722 {"contextmenu", ACTION_STD_CONTEXT
},{"quickscreen", ACTION_STD_QUICKSCREEN
},
723 /* not really WPS specific, but no equivilant ACTION_STD_* */
724 {"voldown", ACTION_WPS_VOLDOWN
}, {"volup", ACTION_WPS_VOLUP
},
726 /* WPS specific actions */
727 {"browse", ACTION_WPS_BROWSE
},
728 {"play", ACTION_WPS_PLAY
}, {"stop", ACTION_WPS_STOP
},
729 {"shuffle", ACTION_TOUCH_SHUFFLE
}, {"repmode", ACTION_TOUCH_REPMODE
},
730 {"pitch", ACTION_WPS_PITCHSCREEN
}, {"playlist", ACTION_WPS_VIEW_PLAYLIST
},
733 /* FM screen actions */
734 /* Also allow browse, play, stop from WPS codes */
735 {"mode", ACTION_FM_MODE
}, {"record", ACTION_FM_RECORD
},
736 {"presets", ACTION_FM_PRESET
},
740 static int parse_touchregion(struct skin_element
*element
,
741 struct wps_token
*token
,
742 struct wps_data
*wps_data
)
746 struct touchregion
*region
= NULL
;
748 const char pb_string
[] = "progressbar";
749 const char vol_string
[] = "volume";
752 /* format: %T(x,y,width,height,action)
753 * if action starts with & the area must be held to happen
755 * play - play/pause playback
756 * stop - stop playback, exit the wps
759 * ffwd - seek forward
760 * rwd - seek backwards
761 * menu - go back to the main menu
762 * browse - go back to the file/db browser
763 * shuffle - toggle shuffle mode
764 * repmode - cycle the repeat mode
765 * quickscreen - go into the quickscreen
766 * contextmenu - open the context menu
767 * playlist - go into the playlist
768 * pitch - go into the pitchscreen
769 * volup - increase volume by one step
770 * voldown - decrease volume by one step
774 region
= (struct touchregion
*)skin_buffer_alloc(sizeof(struct touchregion
));
776 return WPS_ERROR_INVALID_PARAM
;
778 /* should probably do some bounds checking here with the viewport... but later */
779 region
->action
= ACTION_NONE
;
780 region
->x
= element
->params
[0].data
.number
;
781 region
->y
= element
->params
[1].data
.number
;
782 region
->width
= element
->params
[2].data
.number
;
783 region
->height
= element
->params
[3].data
.number
;
784 region
->wvp
= curr_vp
;
785 region
->armed
= false;
786 region
->reverse_bar
= false;
787 action
= element
->params
[4].data
.text
;
789 strcpy(temp
, action
);
794 region
->reverse_bar
= true;
798 if(!strcmp(pb_string
, action
))
799 region
->type
= WPS_TOUCHREGION_SCROLLBAR
;
800 else if(!strcmp(vol_string
, action
))
801 region
->type
= WPS_TOUCHREGION_VOLUME
;
804 region
->type
= WPS_TOUCHREGION_ACTION
;
809 region
->repeat
= true;
812 region
->repeat
= false;
814 imax
= ARRAYLEN(touchactions
);
815 for (i
= 0; i
< imax
; i
++)
817 /* try to match with one of our touchregion screens */
818 if (!strcmp(touchactions
[i
].s
, action
))
820 region
->action
= touchactions
[i
].action
;
824 if (region
->action
== ACTION_NONE
)
825 return WPS_ERROR_INVALID_PARAM
;
827 struct skin_token_list
*item
= new_skin_token_list_item(NULL
, region
);
829 return WPS_ERROR_INVALID_PARAM
;
830 add_to_ll_chain(&wps_data
->touchregions
, item
);
835 static bool check_feature_tag(const int type
)
839 case SKIN_TOKEN_RTC_PRESENT
:
845 case SKIN_TOKEN_HAVE_RECORDING
:
846 #ifdef HAVE_RECORDING
851 case SKIN_TOKEN_HAVE_TUNER
:
853 if (radio_hardware_present())
859 case SKIN_TOKEN_HAVE_RDS
:
864 #endif /* HAVE_RDS_CAP */
865 #endif /* CONFIG_TUNER */
866 default: /* not a tag we care about, just don't skip */
872 * initial setup of wps_data; does reset everything
873 * except fields which need to survive, i.e.
876 static void skin_data_reset(struct wps_data
*wps_data
)
878 wps_data
->tree
= NULL
;
879 #ifdef HAVE_LCD_BITMAP
880 wps_data
->images
= NULL
;
881 wps_data
->progressbars
= NULL
;
883 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
884 wps_data
->backdrop
= NULL
;
886 #ifdef HAVE_TOUCHSCREEN
887 wps_data
->touchregions
= NULL
;
890 wps_data
->albumart
= NULL
;
891 if (wps_data
->playback_aa_slot
>= 0)
893 playback_release_aa_slot(wps_data
->playback_aa_slot
);
894 wps_data
->playback_aa_slot
= -1;
898 #ifdef HAVE_LCD_BITMAP
899 wps_data
->peak_meter_enabled
= false;
900 wps_data
->wps_sb_tag
= false;
901 wps_data
->show_sb_on_wps
= false;
902 #else /* HAVE_LCD_CHARCELLS */
905 for (i
= 0; i
< 8; i
++)
907 wps_data
->wps_progress_pat
[i
] = 0;
909 wps_data
->full_line_progressbar
= false;
911 wps_data
->wps_loaded
= false;
914 #ifdef HAVE_LCD_BITMAP
915 static bool load_skin_bmp(struct wps_data
*wps_data
, struct bitmap
*bitmap
, char* bmpdir
)
917 (void)wps_data
; /* only needed for remote targets */
918 char img_path
[MAX_PATH
];
920 get_image_filename(bitmap
->data
, bmpdir
,
921 img_path
, sizeof(img_path
));
925 #ifdef HAVE_REMOTE_LCD
926 if (curr_screen
== SCREEN_REMOTE
)
927 format
= FORMAT_ANY
|FORMAT_REMOTE
;
930 format
= FORMAT_ANY
|FORMAT_TRANSPARENT
;
932 fd
= open(img_path
, O_RDONLY
);
935 size_t buf_size
= read_bmp_file(img_path
, bitmap
, 0,
936 format
|FORMAT_RETURN_SIZE
, NULL
);
937 char* imgbuf
= (char*)skin_buffer_alloc(buf_size
);
943 lseek(fd
, 0, SEEK_SET
);
944 bitmap
->data
= imgbuf
;
945 int ret
= read_bmp_fd(fd
, bitmap
, buf_size
, format
, NULL
);
954 /* Abort if we can't load an image */
955 DEBUGF("Couldn't load '%s'\n", img_path
);
960 static bool load_skin_bitmaps(struct wps_data
*wps_data
, char *bmpdir
)
962 struct skin_token_list
*list
;
963 bool retval
= true; /* return false if a single image failed to load */
964 /* do the progressbars */
965 list
= wps_data
->progressbars
;
968 struct progressbar
*pb
= (struct progressbar
*)list
->token
->value
.data
;
971 pb
->have_bitmap_pb
= load_skin_bmp(wps_data
, &pb
->bm
, bmpdir
);
972 if (!pb
->have_bitmap_pb
) /* no success */
978 list
= wps_data
->images
;
981 struct gui_img
*img
= (struct gui_img
*)list
->token
->value
.data
;
984 img
->loaded
= load_skin_bmp(wps_data
, &img
->bm
, bmpdir
);
986 img
->subimage_height
= img
->bm
.height
/ img
->num_subimages
;
993 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
994 /* Backdrop load scheme:
996 * 2) load the backdrop from settings
998 if (wps_data
->backdrop
)
1000 if (screens
[curr_screen
].depth
== 1)
1002 wps_data
->backdrop
= NULL
;
1005 bool needed
= wps_data
->backdrop
[0] != '-';
1006 wps_data
->backdrop
= skin_backdrop_load(wps_data
->backdrop
,
1007 bmpdir
, curr_screen
);
1008 if (!wps_data
->backdrop
&& needed
)
1011 #endif /* has backdrop support */
1016 static bool skin_load_fonts(struct wps_data
*data
)
1018 /* don't spit out after the first failue to aid debugging */
1019 bool success
= true;
1020 struct skin_element
*vp_list
;
1022 /* walk though each viewport and assign its font */
1023 for(vp_list
= data
->tree
; vp_list
; vp_list
= vp_list
->next
)
1025 /* first, find the viewports that have a non-sys/ui-font font */
1026 struct skin_viewport
*skin_vp
=
1027 (struct skin_viewport
*)vp_list
->data
;
1028 struct viewport
*vp
= &skin_vp
->vp
;
1031 if (vp
->font
<= FONT_UI
)
1032 { /* the usual case -> built-in fonts */
1033 #ifdef HAVE_REMOTE_LCD
1034 if (vp
->font
== FONT_UI
)
1035 vp
->font
+= curr_screen
;
1041 /* now find the corresponding skin_font */
1042 struct skin_font
*font
= &skinfonts
[font_id
-FONT_FIRSTUSERFONT
];
1047 DEBUGF("font %d not specified\n", font_id
);
1053 /* load the font - will handle loading the same font again if
1054 * multiple viewports use the same */
1057 char *dot
= strchr(font
->name
, '.');
1059 font
->id
= skin_font_load(font
->name
);
1064 DEBUGF("Unable to load font %d: '%s.fnt'\n",
1065 font_id
, font
->name
);
1066 font
->name
= NULL
; /* to stop trying to load it again if we fail */
1072 /* finally, assign the font_id to the viewport */
1073 vp
->font
= font
->id
;
1078 #endif /* HAVE_LCD_BITMAP */
1079 static int convert_viewport(struct wps_data
*data
, struct skin_element
* element
)
1081 struct skin_viewport
*skin_vp
=
1082 (struct skin_viewport
*)skin_buffer_alloc(sizeof(struct skin_viewport
));
1083 struct screen
*display
= &screens
[curr_screen
];
1086 return CALLBACK_ERROR
;
1088 skin_vp
->hidden_flags
= 0;
1089 skin_vp
->label
= VP_NO_LABEL
;
1090 element
->data
= skin_vp
;
1092 curr_viewport_element
= element
;
1094 viewport_set_defaults(&skin_vp
->vp
, curr_screen
);
1095 #ifdef HAVE_REMOTE_LCD
1096 /* viewport_set_defaults() sets the font to FONT_UI+curr_screen.
1097 * This parser requires font 1 to always be the UI font,
1098 * so force it back to FONT_UI and handle the screen number at the end */
1099 skin_vp
->vp
.font
= FONT_UI
;
1102 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1103 skin_vp
->start_fgcolour
= skin_vp
->vp
.fg_pattern
;
1104 skin_vp
->start_bgcolour
= skin_vp
->vp
.bg_pattern
;
1108 struct skin_tag_parameter
*param
= element
->params
;
1109 if (element
->params_count
== 0) /* default viewport */
1111 if (!data
->tree
) /* first viewport in the skin */
1112 data
->tree
= element
;
1113 skin_vp
->label
= VP_DEFAULT_LABEL
;
1117 if (element
->params_count
== 6)
1119 if (element
->tag
->type
== SKIN_TOKEN_UIVIEWPORT_LOAD
)
1121 if (isdefault(param
))
1123 skin_vp
->hidden_flags
= VP_NEVER_VISIBLE
;
1124 skin_vp
->label
= VP_INFO_LABEL
|VP_DEFAULT_LABEL
;
1128 skin_vp
->hidden_flags
= VP_NEVER_VISIBLE
;
1129 skin_vp
->label
= VP_INFO_LABEL
|param
->data
.text
[0];
1134 skin_vp
->hidden_flags
= VP_DRAW_HIDEABLE
|VP_DRAW_HIDDEN
;
1135 skin_vp
->label
= param
->data
.text
[0];
1140 if (!isdefault(param
))
1142 skin_vp
->vp
.x
= param
->data
.number
;
1143 if (param
->data
.number
< 0)
1144 skin_vp
->vp
.x
+= display
->lcdwidth
;
1148 if (!isdefault(param
))
1150 skin_vp
->vp
.y
= param
->data
.number
;
1151 if (param
->data
.number
< 0)
1152 skin_vp
->vp
.y
+= display
->lcdheight
;
1156 if (!isdefault(param
))
1158 skin_vp
->vp
.width
= param
->data
.number
;
1159 if (param
->data
.number
< 0)
1160 skin_vp
->vp
.width
= (skin_vp
->vp
.width
+ display
->lcdwidth
) - skin_vp
->vp
.x
;
1164 skin_vp
->vp
.width
= display
->lcdwidth
- skin_vp
->vp
.x
;
1168 if (!isdefault(param
))
1170 skin_vp
->vp
.height
= param
->data
.number
;
1171 if (param
->data
.number
< 0)
1172 skin_vp
->vp
.height
= (skin_vp
->vp
.height
+ display
->lcdheight
) - skin_vp
->vp
.y
;
1176 skin_vp
->vp
.height
= display
->lcdheight
- skin_vp
->vp
.y
;
1179 #ifdef HAVE_LCD_BITMAP
1181 if (!isdefault(param
))
1183 skin_vp
->vp
.font
= param
->data
.number
;
1191 static int skin_element_callback(struct skin_element
* element
, void* data
)
1193 struct wps_data
*wps_data
= (struct wps_data
*)data
;
1194 struct wps_token
*token
;
1195 parse_function function
= NULL
;
1197 switch (element
->type
)
1199 /* IMPORTANT: element params are shared, so copy them if needed
1200 * or use then NOW, dont presume they have a long lifespan
1204 token
= (struct wps_token
*)skin_buffer_alloc(sizeof(struct wps_token
));
1205 memset(token
, 0, sizeof(*token
));
1206 token
->type
= element
->tag
->type
;
1208 if ((element
->tag
->flags
&SKIN_REFRESH_ALL
) == SKIN_RTC_REFRESH
)
1211 curr_line
->update_mode
|= SKIN_REFRESH_DYNAMIC
;
1213 curr_line
->update_mode
|= SKIN_REFRESH_STATIC
;
1217 curr_line
->update_mode
|= element
->tag
->flags
&SKIN_REFRESH_ALL
;
1219 element
->data
= token
;
1221 /* Some tags need special handling for the tag, so add them here */
1222 switch (token
->type
)
1224 case SKIN_TOKEN_ALIGN_LANGDIRECTION
:
1225 follow_lang_direction
= 2;
1227 case SKIN_TOKEN_PROGRESSBAR
:
1228 case SKIN_TOKEN_VOLUME
:
1229 case SKIN_TOKEN_BATTERY_PERCENT
:
1230 case SKIN_TOKEN_PLAYER_PROGRESSBAR
:
1231 function
= parse_progressbar_tag
;
1233 case SKIN_TOKEN_SUBLINE_TIMEOUT
:
1234 case SKIN_TOKEN_BUTTON_VOLUME
:
1235 case SKIN_TOKEN_TRACK_STARTING
:
1236 case SKIN_TOKEN_TRACK_ENDING
:
1237 case SKIN_TOKEN_LASTTOUCH
:
1238 function
= parse_timeout_tag
;
1240 #ifdef HAVE_LCD_BITMAP
1241 case SKIN_TOKEN_DISABLE_THEME
:
1242 case SKIN_TOKEN_ENABLE_THEME
:
1243 case SKIN_TOKEN_DRAW_INBUILTBAR
:
1244 function
= parse_statusbar_tags
;
1247 case SKIN_TOKEN_FILE_DIRECTORY
:
1248 token
->value
.i
= element
->params
[0].data
.number
;
1250 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1251 case SKIN_TOKEN_VIEWPORT_FGCOLOUR
:
1252 case SKIN_TOKEN_VIEWPORT_BGCOLOUR
:
1253 function
= parse_viewportcolour
;
1255 case SKIN_TOKEN_IMAGE_BACKDROP
:
1256 function
= parse_image_special
;
1259 case SKIN_TOKEN_TRANSLATEDSTRING
:
1260 case SKIN_TOKEN_SETTING
:
1261 function
= parse_setting_and_lang
;
1263 #ifdef HAVE_LCD_BITMAP
1264 case SKIN_TOKEN_VIEWPORT_CUSTOMLIST
:
1265 function
= parse_playlistview
;
1267 case SKIN_TOKEN_LOAD_FONT
:
1268 function
= parse_font_load
;
1270 case SKIN_TOKEN_VIEWPORT_ENABLE
:
1271 case SKIN_TOKEN_UIVIEWPORT_ENABLE
:
1272 token
->value
.i
= element
->params
[0].data
.text
[0];
1274 case SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY
:
1275 function
= parse_image_display
;
1277 case SKIN_TOKEN_IMAGE_PRELOAD
:
1278 case SKIN_TOKEN_IMAGE_DISPLAY
:
1279 function
= parse_image_load
;
1282 #ifdef HAVE_TOUCHSCREEN
1283 case SKIN_TOKEN_TOUCHREGION
:
1284 function
= parse_touchregion
;
1287 #ifdef HAVE_ALBUMART
1288 case SKIN_TOKEN_ALBUMART_DISPLAY
:
1289 if (wps_data
->albumart
)
1290 wps_data
->albumart
->vp
= &curr_vp
->vp
;
1292 case SKIN_TOKEN_ALBUMART_LOAD
:
1293 function
= parse_albumart_load
;
1301 if (function(element
, token
, wps_data
) < 0)
1302 return CALLBACK_ERROR
;
1304 /* tags that start with 'F', 'I' or 'D' are for the next file */
1305 if ( *(element
->tag
->name
) == 'I' || *(element
->tag
->name
) == 'F' ||
1306 *(element
->tag
->name
) == 'D')
1308 if (follow_lang_direction
> 0 )
1309 follow_lang_direction
--;
1313 return convert_viewport(wps_data
, element
);
1317 (struct line
*)skin_buffer_alloc(sizeof(struct line
));
1318 line
->update_mode
= SKIN_REFRESH_STATIC
;
1319 line
->timeout
= DEFAULT_SUBLINE_TIME_MULTIPLIER
* TIMEOUT_UNIT
;
1321 element
->data
= line
;
1324 case LINE_ALTERNATOR
:
1326 struct line_alternator
*alternator
=
1327 (struct line_alternator
*)skin_buffer_alloc(sizeof(struct line_alternator
));
1328 alternator
->current_line
= 0;
1330 alternator
->last_change_tick
= current_tick
;
1332 element
->data
= alternator
;
1337 struct conditional
*conditional
=
1338 (struct conditional
*)skin_buffer_alloc(sizeof(struct conditional
));
1339 conditional
->last_value
= -1;
1340 conditional
->token
= element
->data
;
1341 element
->data
= conditional
;
1342 if (!check_feature_tag(element
->tag
->type
))
1344 return FEATURE_NOT_AVAILABLE
;
1349 curr_line
->update_mode
|= SKIN_REFRESH_STATIC
;
1357 /* to setup up the wps-data from a format-buffer (isfile = false)
1358 from a (wps-)file (isfile = true)*/
1359 bool skin_data_load(enum screen_type screen
, struct wps_data
*wps_data
,
1360 const char *buf
, bool isfile
)
1362 char *wps_buffer
= NULL
;
1363 if (!wps_data
|| !buf
)
1365 #ifdef HAVE_ALBUMART
1367 struct mp3entry
*curtrack
;
1369 struct skin_albumart old_aa
= {.state
= WPS_ALBUMART_NONE
};
1370 if (wps_data
->albumart
)
1372 old_aa
.state
= wps_data
->albumart
->state
;
1373 old_aa
.height
= wps_data
->albumart
->height
;
1374 old_aa
.width
= wps_data
->albumart
->width
;
1377 #ifdef HAVE_LCD_BITMAP
1379 for (i
=0;i
<MAXUSERFONTS
;i
++)
1381 skinfonts
[i
].id
= -1;
1382 skinfonts
[i
].name
= NULL
;
1385 #ifdef DEBUG_SKIN_ENGINE
1386 if (isfile
&& debug_wps
)
1388 DEBUGF("\n=====================\nLoading '%s'\n=====================\n", buf
);
1393 skin_data_reset(wps_data
);
1394 wps_data
->wps_loaded
= false;
1395 curr_screen
= screen
;
1398 curr_viewport_element
= NULL
;
1402 int fd
= open_utf8(buf
, O_RDONLY
);
1407 /* get buffer space from the plugin buffer */
1408 size_t buffersize
= 0;
1409 wps_buffer
= (char *)plugin_get_buffer(&buffersize
);
1414 /* copy the file's content to the buffer for parsing,
1415 ensuring that every line ends with a newline char. */
1416 unsigned int start
= 0;
1417 while(read_line(fd
, wps_buffer
+ start
, buffersize
- start
) > 0)
1419 start
+= strlen(wps_buffer
+ start
);
1420 if (start
< buffersize
- 1)
1422 wps_buffer
[start
++] = '\n';
1423 wps_buffer
[start
] = 0;
1432 wps_buffer
= (char*)buf
;
1434 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1435 wps_data
->backdrop
= "-";
1437 /* parse the skin source */
1438 skin_buffer_save_position();
1439 wps_data
->tree
= skin_parse(wps_buffer
, skin_element_callback
, wps_data
);
1440 if (!wps_data
->tree
) {
1441 skin_data_reset(wps_data
);
1442 skin_buffer_restore_position();
1446 #ifdef HAVE_LCD_BITMAP
1447 char bmpdir
[MAX_PATH
];
1450 /* get the bitmap dir */
1451 char *dot
= strrchr(buf
, '.');
1452 strlcpy(bmpdir
, buf
, dot
- buf
+ 1);
1455 { /* fall back to backdrop dir for built-in themes */
1456 /* no get_user_file_path(), assuming we ship bmps for built-in themes */
1457 snprintf(bmpdir
, MAX_PATH
, "%s", BACKDROP_DIR
);
1459 /* load the bitmaps that were found by the parsing */
1460 if (!load_skin_bitmaps(wps_data
, bmpdir
) ||
1461 !skin_load_fonts(wps_data
))
1463 skin_data_reset(wps_data
);
1464 skin_buffer_restore_position();
1468 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
1469 status
= audio_status();
1470 if (status
& AUDIO_STATUS_PLAY
)
1472 struct skin_albumart
*aa
= wps_data
->albumart
;
1473 if (aa
&& ((aa
->state
&& !old_aa
.state
) ||
1475 (((old_aa
.height
!= aa
->height
) ||
1476 (old_aa
.width
!= aa
->width
))))))
1478 curtrack
= audio_current_track();
1479 offset
= curtrack
->offset
;
1481 if (!(status
& AUDIO_STATUS_PAUSE
))
1486 wps_data
->wps_loaded
= true;
1487 #ifdef DEBUG_SKIN_ENGINE
1488 // if (isfile && debug_wps)
1489 // debug_skin_usage();