From 18a8e529b5d14413dce83cdb9103f3426db10708 Mon Sep 17 00:00:00 2001 From: Jonathan Gordon Date: Sun, 16 Aug 2009 18:23:00 +0000 Subject: more wps->skin engine work.. start redoing memory management in the skins to use a single larger buffer instead of lots of arrays for things like images and progressbars. This commit removes the limit on the amount of progressbars allowed on the screen, still 1 per viewport, but unlimited otherwise(!) Also a larger buffer for remote targets, same size for non-remote targets but very easy to make it bigger (technically removed the 52(?) image limit in skins, except still limited to 1 char identifiers) Unlimited "string" tokens now (limit was 1024 which was rediculously wasteful) git-svn-id: svn://svn.rockbox.org/rockbox/trunk@22350 a1c6a512-1295-4272-9138-f99709370657 --- apps/gui/skin_engine/skin_buffer.c | 100 ++ apps/gui/skin_engine/skin_buffer.h | 51 + apps/gui/skin_engine/skin_display.c | 102 +- apps/gui/skin_engine/skin_engine.h | 2 + apps/gui/skin_engine/skin_parser.c | 1732 ++++++++++++++++++++++++++++++++++ apps/gui/skin_engine/skin_tokens.c | 2 +- apps/gui/skin_engine/skin_tokens.h | 218 +++++ apps/gui/skin_engine/wps_debug.c | 13 +- apps/gui/skin_engine/wps_internals.h | 201 +--- apps/gui/skin_engine/wps_parser.c | 1697 --------------------------------- 10 files changed, 2173 insertions(+), 1945 deletions(-) create mode 100644 apps/gui/skin_engine/skin_buffer.c create mode 100644 apps/gui/skin_engine/skin_buffer.h create mode 100644 apps/gui/skin_engine/skin_parser.c create mode 100644 apps/gui/skin_engine/skin_tokens.h delete mode 100644 apps/gui/skin_engine/wps_parser.c (limited to 'apps/gui') diff --git a/apps/gui/skin_engine/skin_buffer.c b/apps/gui/skin_engine/skin_buffer.c new file mode 100644 index 0000000000..2d10a931ec --- /dev/null +++ b/apps/gui/skin_engine/skin_buffer.c @@ -0,0 +1,100 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: wps_parser.c 19880 2009-01-29 20:49:43Z mcuelenaere $ + * + * Copyright (C) 2002 by Linus Nielsen Feltzing + * Copyright (C) 2009 Jonathan Gordon + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include +#include +#include +#include "config.h" +#include "buffer.h" +#include "settings.h" +#include "screen_access.h" + +/* skin buffer management. + * This module is used to allocate space in a single global skin buffer for + * tokens for both/all screens. + * + * This is mostly just copy/paste from firmware/buffer.c + */ + +#define IMG_BUFSIZE (((LCD_HEIGHT*LCD_WIDTH*LCD_DEPTH/8) \ + + (2*LCD_HEIGHT*LCD_WIDTH/8)) * NB_SCREENS) + +static unsigned char buffer_start[IMG_BUFSIZE], *buffer_pos = NULL; +static size_t buf_size = IMG_BUFSIZE; + +void skin_buffer_init(void) +{ +#if 0 /* this will go in again later probably */ + if (buffer_start == NULL) + { + buf_size = IMG_BUFSIZE;/* global_settings.skin_buf_size */ + + buffer_start = buffer_alloc(buf_size); + buffer_pos = buffer_start; + } + else +#endif + { + /* reset the buffer.... */ + buffer_pos = buffer_start; + } +} + +/* get the number of bytes currently being used */ +size_t skin_buffer_usage(void) +{ + return buffer_pos-buffer_start; +} + +/* Allocate size bytes from the buffer */ +void* skin_buffer_alloc(size_t size) +{ + void* retval = buffer_pos; + if (skin_buffer_usage()+size >= buf_size) + { + return NULL; + } + buffer_pos += size; + /* 32-bit aligned */ + buffer_pos = (void *)(((unsigned long)buffer_pos + 3) & ~3); + return retval; +} + +/* Get a pointer to the skin buffer and the count of how much is free + * used to do your own buffer management. + * Any memory used will be overwritten next time wps_buffer_alloc() + * is called unless skin_buffer_increment() is called first + */ +void* skin_buffer_grab(size_t *freespace) +{ + *freespace = buf_size - skin_buffer_usage(); + return buffer_pos; +} + +/* Use after skin_buffer_grab() to specify how much buffer was used */ +void skin_buffer_increment(size_t used) +{ + buffer_pos += used; + /* 32-bit aligned */ + buffer_pos = (void *)(((unsigned long)buffer_pos + 3) & ~3); +} + diff --git a/apps/gui/skin_engine/skin_buffer.h b/apps/gui/skin_engine/skin_buffer.h new file mode 100644 index 0000000000..779f575689 --- /dev/null +++ b/apps/gui/skin_engine/skin_buffer.h @@ -0,0 +1,51 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: wps_parser.c 19880 2009-01-29 20:49:43Z mcuelenaere $ + * + * Copyright (C) 2002 by Linus Nielsen Feltzing + * Copyright (C) 2009 Jonathan Gordon + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#ifndef _SKIN_BUFFER_H_ +#define _SKIN_BUFFER_H_ + + +#include +#include +#include + +/* int the global buffer */ +void skin_buffer_init(void); + +/* get the number of bytes currently being used */ +size_t skin_buffer_usage(void); + +/* Allocate size bytes from the buffer */ +void* skin_buffer_alloc(size_t size); + + +/* Get a pointer to the skin buffer and the count of how much is free + * used to do your own buffer management. + * Any memory used will be overwritten next time wps_buffer_alloc() + * is called unless skin_buffer_increment() is called first + */ +void* skin_buffer_grab(size_t *freespace); + +/* Use after skin_buffer_grab() to specify how much buffer was used */ +void skin_buffer_increment(size_t used); + +#endif /* _SKIN_BUFFER_H_ */ diff --git a/apps/gui/skin_engine/skin_display.c b/apps/gui/skin_engine/skin_display.c index bf342116a6..4e264b0489 100644 --- a/apps/gui/skin_engine/skin_display.c +++ b/apps/gui/skin_engine/skin_display.c @@ -68,34 +68,16 @@ static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode); -#ifdef HAVE_LCD_BITMAP -/* Clear the WPS image cache */ -static void wps_images_clear(struct wps_data *data) -{ - int i; - /* set images to unloaded and not displayed */ - for (i = 0; i < MAX_IMAGES; i++) - { - data->img[i].loaded = false; - data->img[i].display = -1; - data->img[i].always_display = false; - data->img[i].num_subimages = 1; - } -} -#endif - /* initial setup of wps_data */ void skin_data_init(struct wps_data *wps_data) { #ifdef HAVE_LCD_BITMAP - wps_images_clear(wps_data); wps_data->wps_sb_tag = false; wps_data->show_sb_on_wps = false; - wps_data->img_buf_ptr = wps_data->img_buf; /* where in image buffer */ - wps_data->img_buf_free = IMG_BUFSIZE; /* free space in image buffer */ wps_data->peak_meter_enabled = false; + wps_data->images = NULL; + wps_data->progressbars = NULL; /* progress bars */ - wps_data->progressbar_count = 0; #else /* HAVE_LCD_CHARCELLS */ int i; for (i = 0; i < 8; i++) @@ -191,41 +173,38 @@ static void draw_progressbar(struct gui_wps *gwps, } /* clears the area where the image was shown */ -static void clear_image_pos(struct gui_wps *gwps, int n) +static void clear_image_pos(struct gui_wps *gwps, struct gui_img *img) { if(!gwps) return; - struct wps_data *data = gwps->data; gwps->display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID); - gwps->display->fillrect(data->img[n].x, data->img[n].y, - data->img[n].bm.width, data->img[n].subimage_height); + gwps->display->fillrect(img->x, img->y, img->bm.width, img->subimage_height); gwps->display->set_drawmode(DRMODE_SOLID); } -static void wps_draw_image(struct gui_wps *gwps, int n, int subimage) +static void wps_draw_image(struct gui_wps *gwps, struct gui_img *img, int subimage) { struct screen *display = gwps->display; - struct wps_data *data = gwps->data; - if(data->img[n].always_display) + if(img->always_display) display->set_drawmode(DRMODE_FG); else display->set_drawmode(DRMODE_SOLID); #if LCD_DEPTH > 1 - if(data->img[n].bm.format == FORMAT_MONO) { + if(img->bm.format == FORMAT_MONO) { #endif - display->mono_bitmap_part(data->img[n].bm.data, - 0, data->img[n].subimage_height * subimage, - data->img[n].bm.width, data->img[n].x, - data->img[n].y, data->img[n].bm.width, - data->img[n].subimage_height); + display->mono_bitmap_part(img->bm.data, + 0, img->subimage_height * subimage, + img->bm.width, img->x, + img->y, img->bm.width, + img->subimage_height); #if LCD_DEPTH > 1 } else { - display->transparent_bitmap_part((fb_data *)data->img[n].bm.data, - 0, data->img[n].subimage_height * subimage, - data->img[n].bm.width, data->img[n].x, - data->img[n].y, data->img[n].bm.width, - data->img[n].subimage_height); + display->transparent_bitmap_part((fb_data *)img->bm.data, + 0, img->subimage_height * subimage, + img->bm.width, img->x, + img->y, img->bm.width, + img->subimage_height); } #endif } @@ -235,22 +214,25 @@ static void wps_display_images(struct gui_wps *gwps, struct viewport* vp) if(!gwps || !gwps->data || !gwps->display) return; - int n; struct wps_data *data = gwps->data; struct screen *display = gwps->display; + struct skin_token_list *list = data->images; - for (n = 0; n < MAX_IMAGES; n++) + while (list) { - if (data->img[n].loaded) + struct gui_img *img = (struct gui_img*)list->token->value.data; + if (img->loaded) { - if (data->img[n].display >= 0) + if (img->display >= 0) { - wps_draw_image(gwps, n, data->img[n].display); - } else if (data->img[n].always_display && data->img[n].vp == vp) + wps_draw_image(gwps, img, img->display); + } + else if (img->always_display && img->vp == vp) { - wps_draw_image(gwps, n, 0); + wps_draw_image(gwps, img, 0); } } + list = list->next; } display->set_drawmode(DRMODE_SOLID); } @@ -484,7 +466,7 @@ static bool evaluate_conditional(struct gui_wps *gwps, int *token_index) #ifdef HAVE_LCD_BITMAP /* clear all pictures in the conditional and nested ones */ if (data->tokens[i].type == WPS_TOKEN_IMAGE_PRELOAD_DISPLAY) - clear_image_pos(gwps, data->tokens[i].value.i & 0xFF); + clear_image_pos(gwps, find_image(data->tokens[i].value.i&0xFF, gwps->data)); #endif #ifdef HAVE_ALBUMART if (data->tokens[i].type == WPS_TOKEN_ALBUMART_DISPLAY) @@ -494,7 +476,20 @@ static bool evaluate_conditional(struct gui_wps *gwps, int *token_index) return true; } - +struct gui_img* find_image(int n, struct wps_data *data) +{ + struct skin_token_list *list = data->images; + while (list) + { + struct gui_img *img = (struct gui_img *)list->token->value.data; + if (img->id == n) + return img; + list = list->next; + } + return NULL; +} + + /* Read a (sub)line to the given alignment format buffer. linebuf is the buffer where the data is actually stored. align is the alignment format that'll be used to display the text. @@ -544,12 +539,12 @@ static bool get_line(struct gui_wps *gwps, #ifdef HAVE_LCD_BITMAP case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY: { - struct gui_img *img = data->img; int n = data->tokens[i].value.i & 0xFF; int subimage = data->tokens[i].value.i >> 8; + struct gui_img *img = find_image(n, data); - if (n >= 0 && n < MAX_IMAGES && img[n].loaded) - img[n].display = subimage; + if (img && img->loaded) + img->display = subimage; break; } #endif @@ -992,9 +987,12 @@ static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode) #ifdef HAVE_LCD_BITMAP /* Set images to not to be displayed */ - for (i = 0; i < MAX_IMAGES; i++) + struct skin_token_list *imglist = data->images; + while (imglist) { - data->img[i].display = -1; + struct gui_img *img = (struct gui_img *)imglist->token->value.data; + img->display = -1; + imglist = imglist->next; } #endif /* dont redraw the viewport if its disabled */ diff --git a/apps/gui/skin_engine/skin_engine.h b/apps/gui/skin_engine/skin_engine.h index 1f5236fc15..3ec7b93a9d 100644 --- a/apps/gui/skin_engine/skin_engine.h +++ b/apps/gui/skin_engine/skin_engine.h @@ -23,6 +23,8 @@ #ifndef _SKIN_ENGINE_H #define _SKIN_ENGINE_H +#include "skin_buffer.h" + #include "wps_internals.h" /* TODO: remove this line.. shoudlnt be needed */ diff --git a/apps/gui/skin_engine/skin_parser.c b/apps/gui/skin_engine/skin_parser.c new file mode 100644 index 0000000000..4445ee86c3 --- /dev/null +++ b/apps/gui/skin_engine/skin_parser.c @@ -0,0 +1,1732 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2007 Nicolas Pennequin, Dan Everton, Matthias Mohr + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include +#include +#include +#include "file.h" +#include "misc.h" +#include "plugin.h" +#include "viewport.h" + +#ifdef __PCTOOL__ +#ifdef WPSEDITOR +#include "proxy.h" +#include "sysfont.h" +#else +#include "checkwps.h" +#include "audio.h" +#define DEBUGF printf +#endif /*WPSEDITOR*/ +#else +#include "debug.h" +#endif /*__PCTOOL__*/ + +#include +#include +#include "font.h" + +#include "wps_internals.h" +#include "skin_engine.h" +#include "settings.h" +#include "settings_list.h" + +#ifdef HAVE_LCD_BITMAP +#include "bmp.h" +#endif + +#include "backdrop.h" + +#define WPS_DEFAULTCFG WPS_DIR "/rockbox_default.wps" +#define RWPS_DEFAULTCFG WPS_DIR "/rockbox_default.rwps" + +#define WPS_ERROR_INVALID_PARAM -1 + +/* level of current conditional. + -1 means we're not in a conditional. */ +static int level = -1; + +/* index of the last WPS_TOKEN_CONDITIONAL_OPTION + or WPS_TOKEN_CONDITIONAL_START in current level */ +static int lastcond[WPS_MAX_COND_LEVEL]; + +/* index of the WPS_TOKEN_CONDITIONAL in current level */ +static int condindex[WPS_MAX_COND_LEVEL]; + +/* number of condtional options in current level */ +static int numoptions[WPS_MAX_COND_LEVEL]; + +/* the current line in the file */ +static int line; + +#ifdef HAVE_LCD_BITMAP + +#if LCD_DEPTH > 1 +#define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS+1) /* WPS images + pbar bitmap + backdrop */ +#else +#define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS) /* WPS images + pbar bitmap */ +#endif + +#define PROGRESSBAR_BMP MAX_IMAGES +#define BACKDROP_BMP (MAX_BITMAPS-1) + +/* pointers to the bitmap filenames in the WPS source */ +static const char *bmp_names[MAX_BITMAPS]; + +#endif /* HAVE_LCD_BITMAP */ + +#if defined(DEBUG) || defined(SIMULATOR) +/* debugging function */ +extern void print_debug_info(struct wps_data *data, int fail, int line); +#endif + +static void wps_reset(struct wps_data *data); + +/* Function for parsing of details for a token. At the moment the + function is called, the token type has already been set. The + function must fill in the details and possibly add more tokens + to the token array. It should return the number of chars that + has been consumed. + + wps_bufptr points to the char following the tag (i.e. where + details begin). + token is the pointer to the 'main' token being parsed + */ +typedef int (*wps_tag_parse_func)(const char *wps_bufptr, + struct wps_token *token, struct wps_data *wps_data); + +struct wps_tag { + enum wps_token_type type; + const char name[3]; + unsigned char refresh_type; + const wps_tag_parse_func parse_func; +}; +static int skip_end_of_line(const char *wps_bufptr); +/* prototypes of all special parse functions : */ +static int parse_timeout(const char *wps_bufptr, + struct wps_token *token, struct wps_data *wps_data); +static int parse_progressbar(const char *wps_bufptr, + struct wps_token *token, struct wps_data *wps_data); +static int parse_dir_level(const char *wps_bufptr, + struct wps_token *token, struct wps_data *wps_data); +static int parse_setting(const char *wps_bufptr, + struct wps_token *token, struct wps_data *wps_data); + +#ifdef HAVE_LCD_BITMAP +static int parse_viewport_display(const char *wps_bufptr, + struct wps_token *token, struct wps_data *wps_data); +static int parse_viewport(const char *wps_bufptr, + struct wps_token *token, struct wps_data *wps_data); +static int parse_statusbar_enable(const char *wps_bufptr, + struct wps_token *token, struct wps_data *wps_data); +static int parse_statusbar_disable(const char *wps_bufptr, + struct wps_token *token, struct wps_data *wps_data); +static int parse_image_display(const char *wps_bufptr, + struct wps_token *token, struct wps_data *wps_data); +static int parse_image_load(const char *wps_bufptr, + struct wps_token *token, struct wps_data *wps_data); +#endif /*HAVE_LCD_BITMAP */ +#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1)) +static int parse_image_special(const char *wps_bufptr, + struct wps_token *token, struct wps_data *wps_data); +#endif +#ifdef HAVE_ALBUMART +static int parse_albumart_load(const char *wps_bufptr, + struct wps_token *token, struct wps_data *wps_data); +static int parse_albumart_conditional(const char *wps_bufptr, + struct wps_token *token, struct wps_data *wps_data); +#endif /* HAVE_ALBUMART */ +#ifdef HAVE_TOUCHSCREEN +static int parse_touchregion(const char *wps_bufptr, + struct wps_token *token, struct wps_data *wps_data); +#else +static int fulline_tag_not_supported(const char *wps_bufptr, + struct wps_token *token, struct wps_data *wps_data) +{ + (void)token; (void)wps_data; + return skip_end_of_line(wps_bufptr); +} +#define parse_touchregion fulline_tag_not_supported +#endif +#ifdef CONFIG_RTC +#define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC +#else +#define WPS_RTC_REFRESH WPS_REFRESH_STATIC +#endif + +/* array of available tags - those with more characters have to go first + (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */ +static const struct wps_tag all_tags[] = { + + { WPS_TOKEN_ALIGN_CENTER, "ac", 0, NULL }, + { WPS_TOKEN_ALIGN_LEFT, "al", 0, NULL }, + { WPS_TOKEN_ALIGN_RIGHT, "ar", 0, NULL }, + + { WPS_TOKEN_BATTERY_PERCENT, "bl", WPS_REFRESH_DYNAMIC, NULL }, + { WPS_TOKEN_BATTERY_VOLTS, "bv", WPS_REFRESH_DYNAMIC, NULL }, + { WPS_TOKEN_BATTERY_TIME, "bt", WPS_REFRESH_DYNAMIC, NULL }, + { WPS_TOKEN_BATTERY_SLEEPTIME, "bs", WPS_REFRESH_DYNAMIC, NULL }, +#if CONFIG_CHARGING >= CHARGING_MONITOR + { WPS_TOKEN_BATTERY_CHARGING, "bc", WPS_REFRESH_DYNAMIC, NULL }, +#endif +#if CONFIG_CHARGING + { WPS_TOKEN_BATTERY_CHARGER_CONNECTED,"bp", WPS_REFRESH_DYNAMIC, NULL }, +#endif + + { WPS_TOKEN_RTC_PRESENT , "cc", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_RTC_DAY_OF_MONTH, "cd", WPS_RTC_REFRESH, NULL }, + { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,"ce", WPS_RTC_REFRESH, NULL }, + { WPS_TOKEN_RTC_12HOUR_CFG, "cf", WPS_RTC_REFRESH, NULL }, + { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", WPS_RTC_REFRESH, NULL }, + { WPS_TOKEN_RTC_HOUR_24, "ck", WPS_RTC_REFRESH, NULL }, + { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", WPS_RTC_REFRESH, NULL }, + { WPS_TOKEN_RTC_HOUR_12, "cl", WPS_RTC_REFRESH, NULL }, + { WPS_TOKEN_RTC_MONTH, "cm", WPS_RTC_REFRESH, NULL }, + { WPS_TOKEN_RTC_MINUTE, "cM", WPS_RTC_REFRESH, NULL }, + { WPS_TOKEN_RTC_SECOND, "cS", WPS_RTC_REFRESH, NULL }, + { WPS_TOKEN_RTC_YEAR_2_DIGITS, "cy", WPS_RTC_REFRESH, NULL }, + { WPS_TOKEN_RTC_YEAR_4_DIGITS, "cY", WPS_RTC_REFRESH, NULL }, + { WPS_TOKEN_RTC_AM_PM_UPPER, "cP", WPS_RTC_REFRESH, NULL }, + { WPS_TOKEN_RTC_AM_PM_LOWER, "cp", WPS_RTC_REFRESH, NULL }, + { WPS_TOKEN_RTC_WEEKDAY_NAME, "ca", WPS_RTC_REFRESH, NULL }, + { WPS_TOKEN_RTC_MONTH_NAME, "cb", WPS_RTC_REFRESH, NULL }, + { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", WPS_RTC_REFRESH, NULL }, + { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", WPS_RTC_REFRESH, NULL }, + + /* current file */ + { WPS_TOKEN_FILE_BITRATE, "fb", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_FILE_CODEC, "fc", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_FILE_FREQUENCY, "ff", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_FILE_FREQUENCY_KHZ, "fk", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "fm", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_FILE_NAME, "fn", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_FILE_PATH, "fp", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_FILE_SIZE, "fs", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_FILE_VBR, "fv", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_FILE_DIRECTORY, "d", WPS_REFRESH_STATIC, + parse_dir_level }, + + /* next file */ + { WPS_TOKEN_FILE_BITRATE, "Fb", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_STATIC, + parse_dir_level }, + + /* current metadata */ + { WPS_TOKEN_METADATA_ARTIST, "ia", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_COMPOSER, "ic", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_ALBUM, "id", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_ALBUM_ARTIST, "iA", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_GROUPING, "iG", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_GENRE, "ig", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_DISC_NUMBER, "ik", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_TRACK_NUMBER, "in", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_TRACK_TITLE, "it", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_VERSION, "iv", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_YEAR, "iy", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_COMMENT, "iC", WPS_REFRESH_STATIC, NULL }, + + /* next metadata */ + { WPS_TOKEN_METADATA_ARTIST, "Ia", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_STATIC, NULL }, + +#if (CONFIG_CODEC != MAS3507D) + { WPS_TOKEN_SOUND_PITCH, "Sp", WPS_REFRESH_DYNAMIC, NULL }, +#endif + +#if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD) + { WPS_TOKEN_VLED_HDD, "lh", WPS_REFRESH_DYNAMIC, NULL }, +#endif + + { WPS_TOKEN_MAIN_HOLD, "mh", WPS_REFRESH_DYNAMIC, NULL }, + +#ifdef HAS_REMOTE_BUTTON_HOLD + { WPS_TOKEN_REMOTE_HOLD, "mr", WPS_REFRESH_DYNAMIC, NULL }, +#else + { WPS_TOKEN_UNKNOWN, "mr", 0, NULL }, +#endif + + { WPS_TOKEN_REPEAT_MODE, "mm", WPS_REFRESH_DYNAMIC, NULL }, + { WPS_TOKEN_PLAYBACK_STATUS, "mp", WPS_REFRESH_DYNAMIC, NULL }, + { WPS_TOKEN_BUTTON_VOLUME, "mv", WPS_REFRESH_DYNAMIC, + parse_timeout }, + +#ifdef HAVE_LCD_BITMAP + { WPS_TOKEN_PEAKMETER, "pm", WPS_REFRESH_PEAK_METER, NULL }, +#else + { WPS_TOKEN_PLAYER_PROGRESSBAR, "pf", + WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar }, +#endif + { WPS_TOKEN_PROGRESSBAR, "pb", WPS_REFRESH_PLAYER_PROGRESS, + parse_progressbar }, + + { WPS_TOKEN_VOLUME, "pv", WPS_REFRESH_DYNAMIC, NULL }, + + { WPS_TOKEN_TRACK_ELAPSED_PERCENT, "px", WPS_REFRESH_DYNAMIC, NULL }, + { WPS_TOKEN_TRACK_TIME_ELAPSED, "pc", WPS_REFRESH_DYNAMIC, NULL }, + { WPS_TOKEN_TRACK_TIME_REMAINING, "pr", WPS_REFRESH_DYNAMIC, NULL }, + { WPS_TOKEN_TRACK_LENGTH, "pt", WPS_REFRESH_STATIC, NULL }, + + { WPS_TOKEN_PLAYLIST_POSITION, "pp", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_PLAYLIST_ENTRIES, "pe", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_PLAYLIST_NAME, "pn", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_PLAYLIST_SHUFFLE, "ps", WPS_REFRESH_DYNAMIC, NULL }, + +#ifdef HAVE_TAGCACHE + { WPS_TOKEN_DATABASE_PLAYCOUNT, "rp", WPS_REFRESH_DYNAMIC, NULL }, + { WPS_TOKEN_DATABASE_RATING, "rr", WPS_REFRESH_DYNAMIC, NULL }, + { WPS_TOKEN_DATABASE_AUTOSCORE, "ra", WPS_REFRESH_DYNAMIC, NULL }, +#endif + +#if CONFIG_CODEC == SWCODEC + { WPS_TOKEN_REPLAYGAIN, "rg", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_CROSSFADE, "xf", WPS_REFRESH_DYNAMIC, NULL }, +#endif + + { WPS_NO_TOKEN, "s", WPS_REFRESH_SCROLL, NULL }, + { WPS_TOKEN_SUBLINE_TIMEOUT, "t", 0, parse_timeout }, + +#ifdef HAVE_LCD_BITMAP + { WPS_NO_TOKEN, "we", 0, parse_statusbar_enable }, + { WPS_NO_TOKEN, "wd", 0, parse_statusbar_disable }, + + { WPS_NO_TOKEN, "xl", 0, parse_image_load }, + + { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, "xd", WPS_REFRESH_STATIC, + parse_image_display }, + + { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load }, +#ifdef HAVE_ALBUMART + { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load }, + { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_STATIC, + parse_albumart_conditional }, +#endif + + { WPS_VIEWPORT_ENABLE, "Vd", WPS_REFRESH_DYNAMIC, + parse_viewport_display }, + { WPS_NO_TOKEN, "V", 0, parse_viewport }, + +#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1)) + { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special }, +#endif +#endif + + { WPS_TOKEN_SETTING, "St", WPS_REFRESH_DYNAMIC, parse_setting }, + + { WPS_TOKEN_LASTTOUCH, "Tl", WPS_REFRESH_DYNAMIC, parse_timeout }, + { WPS_NO_TOKEN, "T", 0, parse_touchregion }, + + { WPS_TOKEN_UNKNOWN, "", 0, NULL } + /* the array MUST end with an empty string (first char is \0) */ +}; + + +/* add a wpsll item to the list chain. ALWAYS appended because some of the + * chains require the order to be kept. + */ +static void add_to_ll_chain(struct skin_token_list **list, struct skin_token_list *item) +{ + if (*list == NULL) + *list = item; + else + { + struct skin_token_list *t = *list; + while (t->next) + t = t->next; + t->next = item; + } +} +/* create and init a new wpsll item. + * passing NULL to token will alloc a new one. + */ +static struct skin_token_list *new_skin_token_list_item(struct wps_token *token, + void* token_data) +{ + struct skin_token_list *llitem = skin_buffer_alloc(sizeof(struct skin_token_list)); + if (!token) + token = skin_buffer_alloc(sizeof(struct wps_token)); + if (!llitem || !token) + return NULL; + llitem->next = NULL; + llitem->token = token; + if (token_data) + llitem->token->value.data = token_data; + return llitem; +} + +/* Returns the number of chars that should be skipped to jump + immediately after the first eol, i.e. to the start of the next line */ +static int skip_end_of_line(const char *wps_bufptr) +{ + line++; + int skip = 0; + while(*(wps_bufptr + skip) != '\n') + skip++; + return ++skip; +} + +/* Starts a new subline in the current line during parsing */ +static void wps_start_new_subline(struct wps_data *data) +{ + data->num_sublines++; + data->sublines[data->num_sublines].first_token_idx = data->num_tokens; + data->lines[data->num_lines].num_sublines++; +} + +#ifdef HAVE_LCD_BITMAP + +static int parse_statusbar_enable(const char *wps_bufptr, + struct wps_token *token, + struct wps_data *wps_data) +{ + (void)token; /* Kill warnings */ + wps_data->wps_sb_tag = true; + wps_data->show_sb_on_wps = true; + if (wps_data->viewports[0].vp.y == 0) + { + wps_data->viewports[0].vp.y = STATUSBAR_HEIGHT; + wps_data->viewports[0].vp.height -= STATUSBAR_HEIGHT; + } + return skip_end_of_line(wps_bufptr); +} + +static int parse_statusbar_disable(const char *wps_bufptr, + struct wps_token *token, + struct wps_data *wps_data) +{ + (void)token; /* Kill warnings */ + wps_data->wps_sb_tag = true; + wps_data->show_sb_on_wps = false; + if (wps_data->viewports[0].vp.y == STATUSBAR_HEIGHT) + { + wps_data->viewports[0].vp.y = 0; + wps_data->viewports[0].vp.height += STATUSBAR_HEIGHT; + } + return skip_end_of_line(wps_bufptr); +} + +static int get_image_id(int c) +{ + if(c >= 'a' && c <= 'z') + return c - 'a'; + else if(c >= 'A' && c <= 'Z') + return c - 'A' + 26; + else + return -1; +} + +static char *get_image_filename(const char *start, const char* bmpdir, + char *buf, int buf_size) +{ + const char *end = strchr(start, '|'); + + if ( !end || (end - start) >= (buf_size - (int)ROCKBOX_DIR_LEN - 2) ) + { + buf = "\0"; + return NULL; + } + + int bmpdirlen = strlen(bmpdir); + + strcpy(buf, bmpdir); + buf[bmpdirlen] = '/'; + memcpy( &buf[bmpdirlen + 1], start, end - start); + buf[bmpdirlen + 1 + end - start] = 0; + + return buf; +} + +static int parse_image_display(const char *wps_bufptr, + struct wps_token *token, + struct wps_data *wps_data) +{ + int n = get_image_id(wps_bufptr[0]); + int subimage; + struct gui_img *img;; + + if (n == -1) + { + /* invalid picture display tag */ + return WPS_ERROR_INVALID_PARAM; + } + + if ((subimage = get_image_id(wps_bufptr[1])) != -1) + { + img = find_image(n, wps_data); + /* Sanity check */ + if (!img || subimage >= img->num_subimages) + return WPS_ERROR_INVALID_PARAM; + + /* Store sub-image number to display in high bits */ + token->value.i = n | (subimage << 8); + return 2; /* We have consumed 2 bytes */ + } else { + token->value.i = n; + return 1; /* We have consumed 1 byte */ + } +} + +static int parse_image_load(const char *wps_bufptr, + struct wps_token *token, + struct wps_data *wps_data) +{ + int n; + const char *ptr = wps_bufptr; + const char *pos; + const char* filename; + const char* id; + const char *newline; + int x,y; + struct gui_img *img; + + /* format: %x|n|filename.bmp|x|y| + or %xl|n|filename.bmp|x|y| + or %xl|n|filename.bmp|x|y|num_subimages| + */ + + if (*ptr != '|') + return WPS_ERROR_INVALID_PARAM; + + ptr++; + + if (!(ptr = parse_list("ssdd", NULL, '|', ptr, &id, &filename, &x, &y))) + return WPS_ERROR_INVALID_PARAM; + + /* Check there is a terminating | */ + if (*ptr != '|') + return WPS_ERROR_INVALID_PARAM; + + /* get the image ID */ + n = get_image_id(*id); + + /* check the image number and load state */ + if(n < 0 || find_image(n, wps_data)) + { + /* Invalid image ID */ + return WPS_ERROR_INVALID_PARAM; + } + img = skin_buffer_alloc(sizeof(struct gui_img)); + if (!img) + return WPS_ERROR_INVALID_PARAM; + /* save a pointer to the filename */ + img->bm.data = (char*)filename; + img->id = n; + img->x = x; + img->y = y; + img->num_subimages = 1; + img->always_display = false; + + /* save current viewport */ + img->vp = &wps_data->viewports[wps_data->num_viewports].vp; + + if (token->type == WPS_TOKEN_IMAGE_DISPLAY) + { + img->always_display = true; + } + else + { + /* Parse the (optional) number of sub-images */ + ptr++; + newline = strchr(ptr, '\n'); + pos = strchr(ptr, '|'); + if (pos && pos < newline) + img->num_subimages = atoi(ptr); + + if (img->num_subimages <= 0) + return WPS_ERROR_INVALID_PARAM; + } + struct skin_token_list *item = new_skin_token_list_item(NULL, img); + if (!item) + return WPS_ERROR_INVALID_PARAM; + add_to_ll_chain(&wps_data->images, item); + + /* Skip the rest of the line */ + return skip_end_of_line(wps_bufptr); +} + +static int parse_viewport_display(const char *wps_bufptr, + struct wps_token *token, + struct wps_data *wps_data) +{ + (void)wps_data; + char letter = wps_bufptr[0]; + + if (letter < 'a' || letter > 'z') + { + /* invalid viewport tag */ + return WPS_ERROR_INVALID_PARAM; + } + token->value.i = letter; + return 1; +} + +static int parse_viewport(const char *wps_bufptr, + struct wps_token *token, + struct wps_data *wps_data) +{ + (void)token; /* Kill warnings */ + const char *ptr = wps_bufptr; + + const int screen = +#ifdef HAVE_REMOTE_LCD + wps_data->remote_wps ? SCREEN_REMOTE : +#endif + SCREEN_MAIN; + + if (wps_data->num_viewports >= WPS_MAX_VIEWPORTS) + return WPS_ERROR_INVALID_PARAM; + + wps_data->num_viewports++; + /* check for the optional letter to signify its a hideable viewport */ + /* %Vl|