rockbox/apps/gui/skin_engine/skin_parser.c
Jonathan Gordon 485ff79584 Add an ability to set a setting to a specific value with a touchscreen action.
example: %T(0,0,20,12, setting_set, repeat, off)
That will set the repeat mode to "off" when it is pressed.
"setting_set" is the action name
"repeat" is the name of the setting in the config files
"off" is the value to set it to (same values as the legal values in the config files)

Not all settings are supported, outright unsupported settings will fail to parse. Some settings might not work too well if they don't apply instantly (Any that work well int he quickscreen should work well here)

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@29483 a1c6a512-1295-4272-9138-f99709370657
2011-03-01 12:31:03 +00:00

1805 lines
53 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2007 Nicolas Pennequin, Dan Everton, Matthias Mohr
* 2010 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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "config.h"
#include "file.h"
#include "misc.h"
#include "plugin.h"
#include "viewport.h"
#include "skin_buffer.h"
#include "skin_parser.h"
#include "tag_table.h"
#ifdef __PCTOOL__
#ifdef WPSEDITOR
#include "proxy.h"
#include "sysfont.h"
#else
#include "action.h"
#include "checkwps.h"
#include "audio.h"
#define lang_is_rtl() (false)
#define DEBUGF printf
#endif /*WPSEDITOR*/
#else
#include "debug.h"
#include "language.h"
#endif /*__PCTOOL__*/
#include <ctype.h>
#include <stdbool.h>
#include "font.h"
#include "wps_internals.h"
#include "skin_engine.h"
#include "settings.h"
#include "settings_list.h"
#if CONFIG_TUNER
#include "radio.h"
#include "tuner.h"
#endif
#include "skin_fonts.h"
#ifdef HAVE_LCD_BITMAP
#include "bmp.h"
#endif
#ifdef HAVE_ALBUMART
#include "playback.h"
#endif
#include "backdrop.h"
#include "statusbar-skinned.h"
#define WPS_ERROR_INVALID_PARAM -1
static bool isdefault(struct skin_tag_parameter *param)
{
return param->type == DEFAULT;
}
/* which screen are we parsing for? */
static enum screen_type curr_screen;
/* the current viewport */
static struct skin_element *curr_viewport_element;
static struct skin_viewport *curr_vp;
static struct line *curr_line;
static int follow_lang_direction = 0;
typedef int (*parse_function)(struct skin_element *element,
struct wps_token *token,
struct wps_data *wps_data);
#ifdef HAVE_LCD_BITMAP
/* add a skin_token_list 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;
}
}
/* traverse the image linked-list for an image */
struct gui_img* find_image(const char *label, 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 (!strcmp(img->label,label))
return img;
list = list->next;
}
return NULL;
}
#endif
/* traverse the viewport linked list for a viewport */
struct skin_viewport* find_viewport(const char *label, bool uivp, struct wps_data *data)
{
struct skin_element *list = data->tree;
while (list)
{
struct skin_viewport *vp = (struct skin_viewport *)list->data;
if (vp->label && (vp->is_infovp == uivp) &&
!strcmp(vp->label, label))
return vp;
list = list->next;
}
return NULL;
}
#ifdef HAVE_LCD_BITMAP
/* create and init a new wpsll item.
* passing NULL to token will alloc a new one.
* You should only pass NULL for the token when the token type (table above)
* is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
*/
static struct skin_token_list *new_skin_token_list_item(struct wps_token *token,
void* token_data)
{
struct skin_token_list *llitem =
(struct skin_token_list *)skin_buffer_alloc(sizeof(struct skin_token_list));
if (!token)
token = (struct wps_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;
}
static int parse_statusbar_tags(struct skin_element* element,
struct wps_token *token,
struct wps_data *wps_data)
{
(void)element;
if (token->type == SKIN_TOKEN_DRAW_INBUILTBAR)
{
token->value.data = (void*)&curr_vp->vp;
}
else
{
struct skin_element *def_vp = wps_data->tree;
struct skin_viewport *default_vp = def_vp->data;
if (def_vp->params_count == 0)
{
wps_data->wps_sb_tag = true;
wps_data->show_sb_on_wps = (token->type == SKIN_TOKEN_ENABLE_THEME);
}
if (wps_data->show_sb_on_wps)
{
viewport_set_defaults(&default_vp->vp, curr_screen);
}
else
{
viewport_set_fullscreen(&default_vp->vp, curr_screen);
}
#ifdef HAVE_REMOTE_LCD
/* viewport_set_defaults() sets the font to FONT_UI+curr_screen.
* This parser requires font 1 to always be the UI font,
* so force it back to FONT_UI and handle the screen number at the end */
default_vp->vp.font = FONT_UI;
#endif
}
return 0;
}
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;
}
char *get_image_filename(const char *start, const char* bmpdir,
char *buf, int buf_size)
{
snprintf(buf, buf_size, "%s/%s", bmpdir, start);
return buf;
}
static int parse_image_display(struct skin_element *element,
struct wps_token *token,
struct wps_data *wps_data)
{
char *label = element->params[0].data.text;
char sublabel = '\0';
int subimage;
struct gui_img *img;
struct image_display *id = skin_buffer_alloc(sizeof(struct image_display));
if (element->params_count == 1 && strlen(label) <= 2)
{
/* backwards compatability. Allow %xd(Aa) to still work */
sublabel = label[1];
label[1] = '\0';
}
/* sanity check */
img = find_image(label, wps_data);
if (!img || !id)
{
return WPS_ERROR_INVALID_PARAM;
}
id->label = label;
id->offset = 0;
id->token = NULL;
if (img->using_preloaded_icons)
{
token->type = SKIN_TOKEN_IMAGE_DISPLAY_LISTICON;
}
if (element->params_count > 1)
{
if (element->params[1].type == CODE)
id->token = element->params[1].data.code->data;
/* specify a number. 1 being the first subimage (i.e top) NOT 0 */
else if (element->params[1].type == INTEGER)
id->subimage = element->params[1].data.number - 1;
if (element->params_count > 2)
id->offset = element->params[2].data.number;
}
else
{
if ((subimage = get_image_id(sublabel)) != -1)
{
if (subimage >= img->num_subimages)
return WPS_ERROR_INVALID_PARAM;
id->subimage = subimage;
} else {
id->subimage = 0;
}
}
token->value.data = id;
return 0;
}
static int parse_image_load(struct skin_element *element,
struct wps_token *token,
struct wps_data *wps_data)
{
const char* filename;
const char* id;
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)
*/
id = element->params[0].data.text;
filename = element->params[1].data.text;
x = element->params[2].data.number;
y = element->params[3].data.number;
/* check the image number and load state */
if(find_image(id, wps_data))
{
/* Invalid image ID */
return WPS_ERROR_INVALID_PARAM;
}
img = (struct gui_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->label = id;
img->x = x;
img->y = y;
img->num_subimages = 1;
img->always_display = false;
img->display = -1;
img->using_preloaded_icons = false;
/* save current viewport */
img->vp = &curr_vp->vp;
if (token->type == SKIN_TOKEN_IMAGE_DISPLAY)
{
img->always_display = true;
}
else if (element->params_count == 5)
{
img->num_subimages = element->params[4].data.number;
if (img->num_subimages <= 0)
return WPS_ERROR_INVALID_PARAM;
}
if (!strcmp(img->bm.data, "__list_icons__"))
{
img->num_subimages = Icon_Last_Themeable;
img->using_preloaded_icons = true;
}
struct skin_token_list *item =
(struct skin_token_list *)new_skin_token_list_item(NULL, img);
if (!item)
return WPS_ERROR_INVALID_PARAM;
add_to_ll_chain(&wps_data->images, item);
return 0;
}
struct skin_font {
int id; /* the id from font_load */
char *name; /* filename without path and extension */
int glyphs; /* how many glyphs to reserve room for */
};
static struct skin_font skinfonts[MAXUSERFONTS];
static int parse_font_load(struct skin_element *element,
struct wps_token *token,
struct wps_data *wps_data)
{
(void)wps_data; (void)token;
int id = element->params[0].data.number;
char *filename = element->params[1].data.text;
int glyphs;
char *ptr;
if(element->params_count > 2)
glyphs = element->params[2].data.number;
else
glyphs = GLYPHS_TO_CACHE;
#if defined(DEBUG) || defined(SIMULATOR)
if (skinfonts[id-FONT_FIRSTUSERFONT].name != NULL)
{
DEBUGF("font id %d already being used\n", id);
}
#endif
/* make sure the filename contains .fnt,
* we dont actually use it, but require it anyway */
ptr = strchr(filename, '.');
if (!ptr || strncmp(ptr, ".fnt", 4))
return WPS_ERROR_INVALID_PARAM;
skinfonts[id-FONT_FIRSTUSERFONT].id = -1;
skinfonts[id-FONT_FIRSTUSERFONT].name = filename;
skinfonts[id-FONT_FIRSTUSERFONT].glyphs = glyphs;
return 0;
}
#ifdef HAVE_LCD_BITMAP
static int parse_playlistview(struct skin_element *element,
struct wps_token *token,
struct wps_data *wps_data)
{
(void)wps_data;
struct playlistviewer *viewer =
(struct playlistviewer *)skin_buffer_alloc(sizeof(struct playlistviewer));
if (!viewer)
return WPS_ERROR_INVALID_PARAM;
viewer->vp = &curr_vp->vp;
viewer->show_icons = true;
viewer->start_offset = element->params[0].data.number;
viewer->line = element->params[1].data.code;
token->value.data = (void*)viewer;
return 0;
}
#endif
#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
static int parse_viewportcolour(struct skin_element *element,
struct wps_token *token,
struct wps_data *wps_data)
{
(void)wps_data;
struct skin_tag_parameter *param = element->params;
struct viewport_colour *colour =
(struct viewport_colour *)skin_buffer_alloc(sizeof(struct viewport_colour));
if (!colour)
return -1;
if (isdefault(param))
{
colour->colour = get_viewport_default_colour(curr_screen,
token->type == SKIN_TOKEN_VIEWPORT_FGCOLOUR);
}
else
{
if (!parse_color(curr_screen, param->data.text, &colour->colour))
return -1;
}
colour->vp = &curr_vp->vp;
token->value.data = colour;
if (element->line == curr_viewport_element->line)
{
if (token->type == SKIN_TOKEN_VIEWPORT_FGCOLOUR)
{
curr_vp->start_fgcolour = colour->colour;
curr_vp->vp.fg_pattern = colour->colour;
}
else
{
curr_vp->start_bgcolour = colour->colour;
curr_vp->vp.bg_pattern = colour->colour;
}
}
return 0;
}
static int parse_image_special(struct skin_element *element,
struct wps_token *token,
struct wps_data *wps_data)
{
(void)wps_data; /* kill warning */
(void)token;
#if LCD_DEPTH > 1
char *filename;
if (token->type == SKIN_TOKEN_IMAGE_BACKDROP)
{
if (isdefault(&element->params[0]))
{
filename = "-";
}
else
{
filename = element->params[0].data.text;
/* format: %X(filename.bmp) or %X(d) */
if (!strcmp(filename, "d"))
filename = NULL;
}
wps_data->backdrop = filename;
}
#endif
return 0;
}
#endif
#endif /* HAVE_LCD_BITMAP */
static int parse_setting_and_lang(struct skin_element *element,
struct wps_token *token,
struct wps_data *wps_data)
{
/* NOTE: both the string validations that happen in here will
* automatically PASS on checkwps because its too hard to get
* settings_list.c and english.lang built for it.
* If that ever changes remove the #ifndef __PCTOOL__'s here
*/
(void)wps_data;
char *temp = element->params[0].data.text;
int i;
if (token->type == SKIN_TOKEN_TRANSLATEDSTRING)
{
#ifndef __PCTOOL__
i = lang_english_to_id(temp);
if (i < 0)
return WPS_ERROR_INVALID_PARAM;
#endif
}
else
{
/* Find the setting */
for (i=0; i<nb_settings; i++)
if (settings[i].cfg_name &&
!strcmp(settings[i].cfg_name, temp))
break;
#ifndef __PCTOOL__
if (i == nb_settings)
return WPS_ERROR_INVALID_PARAM;
#endif
}
/* Store the setting number */
token->value.i = i;
return 0;
}
static int parse_logical_if(struct skin_element *element,
struct wps_token *token,
struct wps_data *wps_data)
{
(void)wps_data;
char *op = element->params[1].data.text;
struct logical_if *lif = skin_buffer_alloc(sizeof(struct logical_if));
if (!lif)
return -1;
token->value.data = lif;
lif->token = element->params[0].data.code->data;
if (!strncmp(op, "=", 1))
lif->op = IF_EQUALS;
else if (!strncmp(op, "!=", 2))
lif->op = IF_NOTEQUALS;
else if (!strncmp(op, ">=", 2))
lif->op = IF_GREATERTHAN_EQ;
else if (!strncmp(op, "<=", 2))
lif->op = IF_LESSTHAN_EQ;
else if (!strncmp(op, ">", 2))
lif->op = IF_GREATERTHAN;
else if (!strncmp(op, "<", 1))
lif->op = IF_LESSTHAN;
memcpy(&lif->operand, &element->params[2], sizeof(lif->operand));
if (element->params_count > 3)
lif->num_options = element->params[3].data.number;
else
lif->num_options = TOKEN_VALUE_ONLY;
return 0;
}
static int parse_timeout_tag(struct skin_element *element,
struct wps_token *token,
struct wps_data *wps_data)
{
(void)wps_data;
int val = 0;
if (element->params_count == 0)
{
switch (token->type)
{
case SKIN_TOKEN_SUBLINE_TIMEOUT:
return -1;
case SKIN_TOKEN_BUTTON_VOLUME:
case SKIN_TOKEN_TRACK_STARTING:
case SKIN_TOKEN_TRACK_ENDING:
val = 10;
break;
default:
break;
}
}
else
val = element->params[0].data.number;
token->value.i = val * TIMEOUT_UNIT;
return 0;
}
static int parse_progressbar_tag(struct skin_element* element,
struct wps_token *token,
struct wps_data *wps_data)
{
#ifdef HAVE_LCD_BITMAP
struct progressbar *pb;
struct viewport *vp = &curr_vp->vp;
struct skin_tag_parameter *param = element->params;
int curr_param = 0;
char *image_filename = NULL;
if (element->params_count == 0 &&
element->tag->type != SKIN_TOKEN_PROGRESSBAR)
return 0; /* nothing to do */
pb = (struct progressbar*)skin_buffer_alloc(sizeof(struct progressbar));
token->value.data = pb;
if (!pb)
return WPS_ERROR_INVALID_PARAM;
pb->vp = vp;
pb->follow_lang_direction = follow_lang_direction > 0;
pb->nofill = false;
pb->nobar = false;
pb->image = NULL;
pb->slider = NULL;
pb->backdrop = NULL;
pb->invert_fill_direction = false;
pb->horizontal = true;
if (element->params_count == 0)
{
pb->x = 0;
pb->width = vp->width;
pb->height = SYSFONT_HEIGHT-2;
pb->y = -1; /* Will be computed during the rendering */
pb->type = element->tag->type;
return 0;
}
/* (x, y, width, height, ...) */
if (!isdefault(param))
pb->x = param->data.number;
else
pb->x = 0;
param++;
if (!isdefault(param))
pb->y = param->data.number;
else
pb->y = -1; /* computed at rendering */
param++;
if (!isdefault(param))
pb->width = param->data.number;
else
pb->width = vp->width - pb->x;
param++;
if (!isdefault(param))
{
/* A zero height makes no sense - reject it */
if (param->data.number == 0)
return WPS_ERROR_INVALID_PARAM;
pb->height = param->data.number;
}
else
{
if (vp->font > FONT_UI)
pb->height = -1; /* calculate at display time */
else
{
#ifndef __PCTOOL__
pb->height = font_get(vp->font)->height;
#else
pb->height = 8;
#endif
}
}
/* optional params, first is the image filename if it isnt recognised as a keyword */
curr_param = 4;
if (isdefault(&element->params[curr_param]))
{
param++;
curr_param++;
}
pb->horizontal = pb->width > pb->height;
while (curr_param < element->params_count)
{
param++;
if (!strcmp(param->data.text, "invert"))
pb->invert_fill_direction = true;
else if (!strcmp(param->data.text, "nofill"))
pb->nofill = true;
else if (!strcmp(param->data.text, "nobar"))
pb->nobar = true;
else if (!strcmp(param->data.text, "slider"))
{
if (curr_param+1 < element->params_count)
{
curr_param++;
param++;
pb->slider = find_image(param->data.text, wps_data);
}
else /* option needs the next param */
return -1;
}
else if (!strcmp(param->data.text, "image"))
{
if (curr_param+1 < element->params_count)
{
curr_param++;
param++;
image_filename = param->data.text;
}
else /* option needs the next param */
return -1;
}
else if (!strcmp(param->data.text, "backdrop"))
{
if (curr_param+1 < element->params_count)
{
curr_param++;
param++;
pb->backdrop = find_image(param->data.text, wps_data);
}
else /* option needs the next param */
return -1;
}
else if (!strcmp(param->data.text, "vertical"))
{
pb->horizontal = false;
if (isdefault(&element->params[3]))
pb->height = vp->height - pb->y;
}
else if (!strcmp(param->data.text, "horizontal"))
pb->horizontal = true;
else if (curr_param == 4)
image_filename = param->data.text;
curr_param++;
}
if (image_filename)
{
pb->image = find_image(image_filename, wps_data);
if (!pb->image) /* load later */
{
struct gui_img* img = (struct gui_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*)image_filename;
img->label = image_filename;
img->x = 0;
img->y = 0;
img->num_subimages = 1;
img->always_display = false;
img->display = -1;
img->using_preloaded_icons = false;
img->vp = &curr_vp->vp;
struct skin_token_list *item =
(struct skin_token_list *)new_skin_token_list_item(NULL, img);
if (!item)
return WPS_ERROR_INVALID_PARAM;
add_to_ll_chain(&wps_data->images, item);
pb->image = img;
}
}
if (token->type == SKIN_TOKEN_VOLUME)
token->type = SKIN_TOKEN_VOLUMEBAR;
else if (token->type == SKIN_TOKEN_BATTERY_PERCENT)
token->type = SKIN_TOKEN_BATTERY_PERCENTBAR;
else if (token->type == SKIN_TOKEN_TUNER_RSSI)
token->type = SKIN_TOKEN_TUNER_RSSI_BAR;
else if (token->type == SKIN_TOKEN_PEAKMETER_LEFT)
token->type = SKIN_TOKEN_PEAKMETER_LEFTBAR;
else if (token->type == SKIN_TOKEN_PEAKMETER_RIGHT)
token->type = SKIN_TOKEN_PEAKMETER_RIGHTBAR;
pb->type = token->type;
return 0;
#else
(void)element;
if (token->type == SKIN_TOKEN_PROGRESSBAR ||
token->type == SKIN_TOKEN_PLAYER_PROGRESSBAR)
{
wps_data->full_line_progressbar =
token->type == SKIN_TOKEN_PLAYER_PROGRESSBAR;
}
return 0;
#endif
}
#ifdef HAVE_ALBUMART
static int parse_albumart_load(struct skin_element* element,
struct wps_token *token,
struct wps_data *wps_data)
{
struct dim dimensions;
int albumart_slot;
bool swap_for_rtl = lang_is_rtl() && follow_lang_direction;
struct skin_albumart *aa =
(struct skin_albumart *)skin_buffer_alloc(sizeof(struct skin_albumart));
(void)token; /* silence warning */
if (!aa)
return -1;
/* reset albumart info in wps */
aa->width = -1;
aa->height = -1;
aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
aa->x = element->params[0].data.number;
aa->y = element->params[1].data.number;
aa->width = element->params[2].data.number;
aa->height = element->params[3].data.number;
aa->vp = &curr_vp->vp;
aa->draw_handle = -1;
/* if we got here, we parsed everything ok .. ! */
if (aa->width < 0)
aa->width = 0;
else if (aa->width > LCD_WIDTH)
aa->width = LCD_WIDTH;
if (aa->height < 0)
aa->height = 0;
else if (aa->height > LCD_HEIGHT)
aa->height = LCD_HEIGHT;
if (swap_for_rtl)
aa->x = LCD_WIDTH - (aa->x + aa->width);
aa->state = WPS_ALBUMART_LOAD;
wps_data->albumart = aa;
dimensions.width = aa->width;
dimensions.height = aa->height;
albumart_slot = playback_claim_aa_slot(&dimensions);
if (0 <= albumart_slot)
wps_data->playback_aa_slot = albumart_slot;
if (element->params_count > 4 && !isdefault(&element->params[4]))
{
switch (*element->params[4].data.text)
{
case 'l':
case 'L':
if (swap_for_rtl)
aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
else
aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
break;
case 'c':
case 'C':
aa->xalign = WPS_ALBUMART_ALIGN_CENTER;
break;
case 'r':
case 'R':
if (swap_for_rtl)
aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
else
aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
break;
}
}
if (element->params_count > 5 && !isdefault(&element->params[5]))
{
switch (*element->params[5].data.text)
{
case 't':
case 'T':
aa->yalign = WPS_ALBUMART_ALIGN_TOP;
break;
case 'c':
case 'C':
aa->yalign = WPS_ALBUMART_ALIGN_CENTER;
break;
case 'b':
case 'B':
aa->yalign = WPS_ALBUMART_ALIGN_BOTTOM;
break;
}
}
return 0;
}
#endif /* HAVE_ALBUMART */
#ifdef HAVE_TOUCHSCREEN
struct touchregion* find_touchregion(const char *label,
struct wps_data *data)
{
struct skin_token_list *list = data->touchregions;
while (list)
{
struct touchregion *tr =
(struct touchregion *)list->token->value.data;
if (tr->label && !strcmp(tr->label, label))
return tr;
list = list->next;
}
return NULL;
}
static int parse_lasttouch(struct skin_element *element,
struct wps_token *token,
struct wps_data *wps_data)
{
struct touchregion_lastpress *data =
(struct touchregion_lastpress*)skin_buffer_alloc(
sizeof(struct touchregion_lastpress));
int i;
if (!data)
return WPS_ERROR_INVALID_PARAM;
data->region = NULL;
data->timeout = 10;
for (i=0; i<element->params_count; i++)
{
if (element->params[i].type == STRING)
data->region = find_touchregion(
element->params[i].data.text, wps_data);
else if (element->params[i].type == INTEGER)
data->timeout = element->params[i].data.number;
}
data->timeout *= TIMEOUT_UNIT;
token->value.data = data;
return 0;
}
struct touchaction {const char* s; int action;};
static const struct touchaction touchactions[] = {
/* generic actions, convert to screen actions on use */
{"none", ACTION_TOUCHSCREEN},
{"prev", ACTION_STD_PREV }, {"next", ACTION_STD_NEXT },
{"rwd", ACTION_STD_PREVREPEAT }, {"ffwd", ACTION_STD_NEXTREPEAT },
{"hotkey", ACTION_STD_HOTKEY}, {"select", ACTION_STD_OK },
{"menu", ACTION_STD_MENU }, {"cancel", ACTION_STD_CANCEL },
{"contextmenu", ACTION_STD_CONTEXT},{"quickscreen", ACTION_STD_QUICKSCREEN },
/* list/tree actions */
{ "resumeplayback", ACTION_TREE_WPS}, /* returns to previous music, WPS/FM */
/* not really WPS specific, but no equivilant ACTION_STD_* */
{"voldown", ACTION_WPS_VOLDOWN}, {"volup", ACTION_WPS_VOLUP},
{"mute", ACTION_TOUCH_MUTE },
/* generic settings changers */
{"setting_inc", ACTION_SETTINGS_INC}, {"setting_dec", ACTION_SETTINGS_DEC},
{"setting_set", ACTION_SETTINGS_SET},
/* WPS specific actions */
{"browse", ACTION_WPS_BROWSE },
{"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
{"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
{"pitch", ACTION_WPS_PITCHSCREEN}, {"playlist", ACTION_WPS_VIEW_PLAYLIST },
#if CONFIG_TUNER
/* FM screen actions */
/* Also allow browse, play, stop from WPS codes */
{"mode", ACTION_FM_MODE }, {"record", ACTION_FM_RECORD },
{"presets", ACTION_FM_PRESET},
#endif
};
bool cfg_string_to_int(int setting_id, int* out, const char* str);
static int parse_touchregion(struct skin_element *element,
struct wps_token *token,
struct wps_data *wps_data)
{
(void)token;
unsigned i, imax;
int p;
struct touchregion *region = NULL;
const char *action;
const char pb_string[] = "progressbar";
const char vol_string[] = "volume";
char temp[20];
/* format: %T([label,], x,y,width,height,action[, ...])
* if action starts with & the area must be held to happen
*/
region = (struct touchregion*)skin_buffer_alloc(sizeof(struct touchregion));
if (!region)
return WPS_ERROR_INVALID_PARAM;
/* should probably do some bounds checking here with the viewport... but later */
region->action = ACTION_NONE;
if (element->params[0].type == STRING)
{
region->label = element->params[0].data.text;
p = 1;
/* "[SI]III[SI]|SS" is the param list. There MUST be 4 numbers
* followed by at least one string. Verify that here */
if (element->params_count < 6 ||
element->params[4].type != INTEGER)
return WPS_ERROR_INVALID_PARAM;
}
else
{
region->label = NULL;
p = 0;
}
region->x = element->params[p++].data.number;
region->y = element->params[p++].data.number;
region->width = element->params[p++].data.number;
region->height = element->params[p++].data.number;
region->wvp = curr_vp;
region->armed = false;
region->reverse_bar = false;
region->value = 0;
region->last_press = 0xffff;
action = element->params[p++].data.text;
strcpy(temp, action);
action = temp;
if (*action == '!')
{
region->reverse_bar = true;
action++;
}
if(!strcmp(pb_string, action))
region->type = WPS_TOUCHREGION_SCROLLBAR;
else if(!strcmp(vol_string, action))
region->type = WPS_TOUCHREGION_VOLUME;
else
{
region->type = WPS_TOUCHREGION_ACTION;
if (*action == '&')
{
action++;
region->repeat = true;
}
else
region->repeat = false;
imax = ARRAYLEN(touchactions);
for (i = 0; i < imax; i++)
{
/* try to match with one of our touchregion screens */
if (!strcmp(touchactions[i].s, action))
{
region->action = touchactions[i].action;
if (region->action == ACTION_SETTINGS_INC ||
region->action == ACTION_SETTINGS_DEC ||
region->action == ACTION_SETTINGS_SET)
{
if (element->params_count < p+1)
{
return WPS_ERROR_INVALID_PARAM;
}
else
{
char *name = element->params[p].data.text;
int j;
/* Find the setting */
for (j=0; j<nb_settings; j++)
if (settings[j].cfg_name &&
!strcmp(settings[j].cfg_name, name))
break;
if (j==nb_settings)
return WPS_ERROR_INVALID_PARAM;
region->setting_data.setting = (void*)&settings[j];
if (region->action == ACTION_SETTINGS_SET)
{
char* text;
int temp;
struct touchsetting *setting =
&region->setting_data;
if (element->params_count < p+2)
return WPS_ERROR_INVALID_PARAM;
text = element->params[p+1].data.text;
switch (settings[j].flags&F_T_MASK)
{
case F_T_CUSTOM:
setting->value.text = text;
break;
case F_T_INT:
case F_T_UINT:
if (settings[j].cfg_vals == NULL)
{
setting->value.number = atoi(text);
}
else if (cfg_string_to_int(j, &temp, text))
{
if (settings[j].flags&F_TABLE_SETTING)
setting->value.number =
settings[j].table_setting->values[temp];
else
setting->value.number = temp;
}
else
return WPS_ERROR_INVALID_PARAM;
break;
case F_T_BOOL:
if (cfg_string_to_int(j, &temp, text))
{
setting->value.number = temp;
}
else
return WPS_ERROR_INVALID_PARAM;
break;
default:
return WPS_ERROR_INVALID_PARAM;
}
}
}
}
break;
}
}
if (region->action == ACTION_NONE)
return WPS_ERROR_INVALID_PARAM;
}
struct skin_token_list *item = new_skin_token_list_item(NULL, region);
if (!item)
return WPS_ERROR_INVALID_PARAM;
add_to_ll_chain(&wps_data->touchregions, item);
if (region->action == ACTION_TOUCH_MUTE)
{
region->value = global_settings.volume;
}
return 0;
}
#endif
static bool check_feature_tag(const int type)
{
switch (type)
{
case SKIN_TOKEN_RTC_PRESENT:
#if CONFIG_RTC
return true;
#else
return false;
#endif
case SKIN_TOKEN_HAVE_RECORDING:
#ifdef HAVE_RECORDING
return true;
#else
return false;
#endif
case SKIN_TOKEN_HAVE_TUNER:
#if CONFIG_TUNER
if (radio_hardware_present())
return true;
#endif
return false;
case SKIN_TOKEN_HAVE_TOUCH:
#ifdef HAVE_TOUCHSCREEN
return true;
#else
return false;
#endif
#if CONFIG_TUNER
case SKIN_TOKEN_HAVE_RDS:
#ifdef HAVE_RDS_CAP
return true;
#else
return false;
#endif /* HAVE_RDS_CAP */
#endif /* CONFIG_TUNER */
default: /* not a tag we care about, just don't skip */
return true;
}
}
/*
* initial setup of wps_data; does reset everything
* except fields which need to survive, i.e.
*
**/
static void skin_data_reset(struct wps_data *wps_data)
{
wps_data->tree = NULL;
#ifdef HAVE_LCD_BITMAP
wps_data->images = NULL;
#endif
#if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
if (wps_data->backdrop_id >= 0)
skin_backdrop_unload(wps_data->backdrop_id);
wps_data->backdrop = NULL;
#endif
#ifdef HAVE_TOUCHSCREEN
wps_data->touchregions = NULL;
#endif
#ifdef HAVE_ALBUMART
wps_data->albumart = NULL;
if (wps_data->playback_aa_slot >= 0)
{
playback_release_aa_slot(wps_data->playback_aa_slot);
wps_data->playback_aa_slot = -1;
}
#endif
#ifdef HAVE_LCD_BITMAP
wps_data->peak_meter_enabled = false;
wps_data->wps_sb_tag = false;
wps_data->show_sb_on_wps = false;
#else /* HAVE_LCD_CHARCELLS */
/* progress bars */
int i;
for (i = 0; i < 8; i++)
{
wps_data->wps_progress_pat[i] = 0;
}
wps_data->full_line_progressbar = false;
#endif
wps_data->wps_loaded = false;
}
#ifdef HAVE_LCD_BITMAP
static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
{
(void)wps_data; /* only needed for remote targets */
char img_path[MAX_PATH];
int fd;
get_image_filename(bitmap->data, bmpdir,
img_path, sizeof(img_path));
/* load the image */
int format;
#ifdef HAVE_REMOTE_LCD
if (curr_screen == SCREEN_REMOTE)
format = FORMAT_ANY|FORMAT_REMOTE;
else
#endif
format = FORMAT_ANY|FORMAT_TRANSPARENT;
fd = open(img_path, O_RDONLY);
if (fd < 0)
{
DEBUGF("Couldn't open %s\n", img_path);
return false;
}
size_t buf_size = read_bmp_fd(fd, bitmap, 0,
format|FORMAT_RETURN_SIZE, NULL);
char* imgbuf = (char*)skin_buffer_alloc(buf_size);
if (!imgbuf)
{
#ifndef APPLICATION
DEBUGF("Not enough skin buffer: need %zd more.\n",
buf_size - skin_buffer_freespace());
#endif
close(fd);
return NULL;
}
lseek(fd, 0, SEEK_SET);
bitmap->data = imgbuf;
int ret = read_bmp_fd(fd, bitmap, buf_size, format, NULL);
close(fd);
if (ret > 0)
{
return true;
}
else
{
/* Abort if we can't load an image */
DEBUGF("Couldn't load '%s'\n", img_path);
return false;
}
}
static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
{
struct skin_token_list *list;
bool retval = true; /* return false if a single image failed to load */
/* regular images */
list = wps_data->images;
while (list)
{
struct gui_img *img = (struct gui_img*)list->token->value.data;
if (img->bm.data)
{
if (img->using_preloaded_icons)
{
img->loaded = true;
list->token->type = SKIN_TOKEN_IMAGE_DISPLAY_LISTICON;
}
else
{
img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
if (img->loaded)
img->subimage_height = img->bm.height / img->num_subimages;
else
retval = false;
}
}
list = list->next;
}
#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
wps_data->backdrop_id = skin_backdrop_assign(wps_data->backdrop, bmpdir, curr_screen);
#endif /* has backdrop support */
return retval;
}
static bool skin_load_fonts(struct wps_data *data)
{
/* don't spit out after the first failue to aid debugging */
bool success = true;
struct skin_element *vp_list;
int font_id;
/* walk though each viewport and assign its font */
for(vp_list = data->tree; vp_list; vp_list = vp_list->next)
{
/* first, find the viewports that have a non-sys/ui-font font */
struct skin_viewport *skin_vp =
(struct skin_viewport*)vp_list->data;
struct viewport *vp = &skin_vp->vp;
if (vp->font <= FONT_UI)
{ /* the usual case -> built-in fonts */
#ifdef HAVE_REMOTE_LCD
if (vp->font == FONT_UI)
vp->font += curr_screen;
#endif
continue;
}
font_id = vp->font;
/* now find the corresponding skin_font */
struct skin_font *font = &skinfonts[font_id-FONT_FIRSTUSERFONT];
if (!font->name)
{
if (success)
{
DEBUGF("font %d not specified\n", font_id);
}
success = false;
continue;
}
/* load the font - will handle loading the same font again if
* multiple viewports use the same */
if (font->id < 0)
{
char *dot = strchr(font->name, '.');
*dot = '\0';
font->id = skin_font_load(font->name,
skinfonts[font_id-FONT_FIRSTUSERFONT].glyphs);
}
if (font->id < 0)
{
DEBUGF("Unable to load font %d: '%s.fnt'\n",
font_id, font->name);
font->name = NULL; /* to stop trying to load it again if we fail */
success = false;
font->name = NULL;
continue;
}
/* finally, assign the font_id to the viewport */
vp->font = font->id;
}
return success;
}
#endif /* HAVE_LCD_BITMAP */
static int convert_viewport(struct wps_data *data, struct skin_element* element)
{
struct skin_viewport *skin_vp =
(struct skin_viewport *)skin_buffer_alloc(sizeof(struct skin_viewport));
struct screen *display = &screens[curr_screen];
if (!skin_vp)
return CALLBACK_ERROR;
skin_vp->hidden_flags = 0;
skin_vp->label = NULL;
skin_vp->is_infovp = false;
element->data = skin_vp;
curr_vp = skin_vp;
curr_viewport_element = element;
viewport_set_defaults(&skin_vp->vp, curr_screen);
#ifdef HAVE_REMOTE_LCD
/* viewport_set_defaults() sets the font to FONT_UI+curr_screen.
* This parser requires font 1 to always be the UI font,
* so force it back to FONT_UI and handle the screen number at the end */
skin_vp->vp.font = FONT_UI;
#endif
#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
skin_vp->start_fgcolour = skin_vp->vp.fg_pattern;
skin_vp->start_bgcolour = skin_vp->vp.bg_pattern;
#endif
struct skin_tag_parameter *param = element->params;
if (element->params_count == 0) /* default viewport */
{
if (!data->tree) /* first viewport in the skin */
data->tree = element;
skin_vp->label = VP_DEFAULT_LABEL;
return CALLBACK_OK;
}
if (element->params_count == 6)
{
if (element->tag->type == SKIN_TOKEN_UIVIEWPORT_LOAD)
{
skin_vp->is_infovp = true;
if (isdefault(param))
{
skin_vp->hidden_flags = VP_NEVER_VISIBLE;
skin_vp->label = VP_DEFAULT_LABEL;
}
else
{
skin_vp->hidden_flags = VP_NEVER_VISIBLE;
skin_vp->label = param->data.text;
}
}
else
{
skin_vp->hidden_flags = VP_DRAW_HIDEABLE|VP_DRAW_HIDDEN;
skin_vp->label = param->data.text;
}
param++;
}
/* x */
if (!isdefault(param))
{
skin_vp->vp.x = param->data.number;
if (param->data.number < 0)
skin_vp->vp.x += display->lcdwidth;
}
param++;
/* y */
if (!isdefault(param))
{
skin_vp->vp.y = param->data.number;
if (param->data.number < 0)
skin_vp->vp.y += display->lcdheight;
}
param++;
/* width */
if (!isdefault(param))
{
skin_vp->vp.width = param->data.number;
if (param->data.number < 0)
skin_vp->vp.width = (skin_vp->vp.width + display->lcdwidth) - skin_vp->vp.x;
}
else
{
skin_vp->vp.width = display->lcdwidth - skin_vp->vp.x;
}
param++;
/* height */
if (!isdefault(param))
{
skin_vp->vp.height = param->data.number;
if (param->data.number < 0)
skin_vp->vp.height = (skin_vp->vp.height + display->lcdheight) - skin_vp->vp.y;
}
else
{
skin_vp->vp.height = display->lcdheight - skin_vp->vp.y;
}
param++;
#ifdef HAVE_LCD_BITMAP
/* font */
if (!isdefault(param))
{
skin_vp->vp.font = param->data.number;
}
#endif
if ((unsigned) skin_vp->vp.x >= (unsigned) display->lcdwidth ||
skin_vp->vp.width + skin_vp->vp.x > display->lcdwidth ||
(unsigned) skin_vp->vp.y >= (unsigned) display->lcdheight ||
skin_vp->vp.height + skin_vp->vp.y > display->lcdheight)
return CALLBACK_ERROR;
return CALLBACK_OK;
}
static int skin_element_callback(struct skin_element* element, void* data)
{
struct wps_data *wps_data = (struct wps_data *)data;
struct wps_token *token;
parse_function function = NULL;
switch (element->type)
{
/* IMPORTANT: element params are shared, so copy them if needed
* or use then NOW, dont presume they have a long lifespan
*/
case TAG:
{
token = (struct wps_token*)skin_buffer_alloc(sizeof(struct wps_token));
memset(token, 0, sizeof(*token));
token->type = element->tag->type;
if (element->tag->flags&SKIN_RTC_REFRESH)
{
#if CONFIG_RTC
curr_line->update_mode |= SKIN_REFRESH_DYNAMIC;
#else
curr_line->update_mode |= SKIN_REFRESH_STATIC;
#endif
}
else
curr_line->update_mode |= element->tag->flags&SKIN_REFRESH_ALL;
element->data = token;
/* Some tags need special handling for the tag, so add them here */
switch (token->type)
{
case SKIN_TOKEN_ALIGN_LANGDIRECTION:
follow_lang_direction = 2;
break;
case SKIN_TOKEN_LOGICAL_IF:
function = parse_logical_if;
break;
case SKIN_TOKEN_PROGRESSBAR:
case SKIN_TOKEN_VOLUME:
case SKIN_TOKEN_BATTERY_PERCENT:
case SKIN_TOKEN_PLAYER_PROGRESSBAR:
case SKIN_TOKEN_PEAKMETER_LEFT:
case SKIN_TOKEN_PEAKMETER_RIGHT:
#ifdef HAVE_RADIO_RSSI
case SKIN_TOKEN_TUNER_RSSI:
#endif
function = parse_progressbar_tag;
break;
case SKIN_TOKEN_SUBLINE_TIMEOUT:
case SKIN_TOKEN_BUTTON_VOLUME:
case SKIN_TOKEN_TRACK_STARTING:
case SKIN_TOKEN_TRACK_ENDING:
function = parse_timeout_tag;
break;
#ifdef HAVE_LCD_BITMAP
case SKIN_TOKEN_DISABLE_THEME:
case SKIN_TOKEN_ENABLE_THEME:
case SKIN_TOKEN_DRAW_INBUILTBAR:
function = parse_statusbar_tags;
break;
case SKIN_TOKEN_LIST_TITLE_TEXT:
#ifndef __PCTOOL__
sb_skin_has_title(curr_screen);
#endif
break;
#endif
case SKIN_TOKEN_FILE_DIRECTORY:
token->value.i = element->params[0].data.number;
break;
#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
case SKIN_TOKEN_VIEWPORT_FGCOLOUR:
case SKIN_TOKEN_VIEWPORT_BGCOLOUR:
function = parse_viewportcolour;
break;
case SKIN_TOKEN_IMAGE_BACKDROP:
function = parse_image_special;
break;
#endif
case SKIN_TOKEN_TRANSLATEDSTRING:
case SKIN_TOKEN_SETTING:
function = parse_setting_and_lang;
break;
#ifdef HAVE_LCD_BITMAP
case SKIN_TOKEN_VIEWPORT_CUSTOMLIST:
function = parse_playlistview;
break;
case SKIN_TOKEN_LOAD_FONT:
function = parse_font_load;
break;
case SKIN_TOKEN_VIEWPORT_ENABLE:
case SKIN_TOKEN_UIVIEWPORT_ENABLE:
token->value.data = element->params[0].data.text;
break;
case SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY:
function = parse_image_display;
break;
case SKIN_TOKEN_IMAGE_PRELOAD:
case SKIN_TOKEN_IMAGE_DISPLAY:
function = parse_image_load;
break;
#endif
#ifdef HAVE_TOUCHSCREEN
case SKIN_TOKEN_TOUCHREGION:
function = parse_touchregion;
break;
case SKIN_TOKEN_LASTTOUCH:
function = parse_lasttouch;
break;
#endif
#ifdef HAVE_ALBUMART
case SKIN_TOKEN_ALBUMART_DISPLAY:
if (wps_data->albumart)
wps_data->albumart->vp = &curr_vp->vp;
break;
case SKIN_TOKEN_ALBUMART_LOAD:
function = parse_albumart_load;
break;
#endif
default:
break;
}
if (function)
{
if (function(element, token, wps_data) < 0)
return CALLBACK_ERROR;
}
/* tags that start with 'F', 'I' or 'D' are for the next file */
if ( *(element->tag->name) == 'I' || *(element->tag->name) == 'F' ||
*(element->tag->name) == 'D')
token->next = true;
if (follow_lang_direction > 0 )
follow_lang_direction--;
break;
}
case VIEWPORT:
return convert_viewport(wps_data, element);
case LINE:
{
struct line *line =
(struct line *)skin_buffer_alloc(sizeof(struct line));
line->update_mode = SKIN_REFRESH_STATIC;
curr_line = line;
element->data = line;
}
break;
case LINE_ALTERNATOR:
{
struct line_alternator *alternator =
(struct line_alternator *)skin_buffer_alloc(sizeof(struct line_alternator));
alternator->current_line = 0;
#ifndef __PCTOOL__
alternator->next_change_tick = current_tick;
#endif
element->data = alternator;
}
break;
case CONDITIONAL:
{
struct conditional *conditional =
(struct conditional *)skin_buffer_alloc(sizeof(struct conditional));
conditional->last_value = -1;
conditional->token = element->data;
element->data = conditional;
if (!check_feature_tag(element->tag->type))
{
return FEATURE_NOT_AVAILABLE;
}
return CALLBACK_OK;
}
case TEXT:
curr_line->update_mode |= SKIN_REFRESH_STATIC;
break;
default:
break;
}
return CALLBACK_OK;
}
/* to setup up the wps-data from a format-buffer (isfile = false)
from a (wps-)file (isfile = true)*/
bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
const char *buf, bool isfile)
{
char *wps_buffer = NULL;
if (!wps_data || !buf)
return false;
#ifdef HAVE_ALBUMART
int status;
struct mp3entry *curtrack;
long offset;
struct skin_albumart old_aa = {.state = WPS_ALBUMART_NONE};
if (wps_data->albumart)
{
old_aa.state = wps_data->albumart->state;
old_aa.height = wps_data->albumart->height;
old_aa.width = wps_data->albumart->width;
}
#endif
#ifdef HAVE_LCD_BITMAP
int i;
for (i=0;i<MAXUSERFONTS;i++)
{
skinfonts[i].id = -1;
skinfonts[i].name = NULL;
}
#endif
#ifdef DEBUG_SKIN_ENGINE
if (isfile && debug_wps)
{
DEBUGF("\n=====================\nLoading '%s'\n=====================\n", buf);
}
#endif
skin_data_reset(wps_data);
wps_data->wps_loaded = false;
curr_screen = screen;
curr_line = NULL;
curr_vp = NULL;
curr_viewport_element = NULL;
if (isfile)
{
int fd = open_utf8(buf, O_RDONLY);
if (fd < 0)
return false;
/* get buffer space from the plugin buffer */
size_t buffersize = 0;
wps_buffer = (char *)plugin_get_buffer(&buffersize);
if (!wps_buffer)
return false;
/* copy the file's content to the buffer for parsing,
ensuring that every line ends with a newline char. */
unsigned int start = 0;
while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
{
start += strlen(wps_buffer + start);
if (start < buffersize - 1)
{
wps_buffer[start++] = '\n';
wps_buffer[start] = 0;
}
}
close(fd);
if (start <= 0)
return false;
}
else
{
wps_buffer = (char*)buf;
}
#if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
wps_data->backdrop = "-";
wps_data->backdrop_id = -1;
#endif
/* parse the skin source */
#ifndef APPLICATION
skin_buffer_save_position();
#endif
wps_data->tree = skin_parse(wps_buffer, skin_element_callback, wps_data);
if (!wps_data->tree) {
skin_data_reset(wps_data);
#ifndef APPLICATION
skin_buffer_restore_position();
#endif
return false;
}
#ifdef HAVE_LCD_BITMAP
char bmpdir[MAX_PATH];
if (isfile)
{
/* get the bitmap dir */
char *dot = strrchr(buf, '.');
strlcpy(bmpdir, buf, dot - buf + 1);
}
else
{
snprintf(bmpdir, MAX_PATH, "%s", BACKDROP_DIR);
}
/* load the bitmaps that were found by the parsing */
if (!load_skin_bitmaps(wps_data, bmpdir) ||
!skin_load_fonts(wps_data))
{
skin_data_reset(wps_data);
#ifndef APPLICATION
skin_buffer_restore_position();
#endif
return false;
}
#endif
#if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
status = audio_status();
if (status & AUDIO_STATUS_PLAY)
{
struct skin_albumart *aa = wps_data->albumart;
if (aa && ((aa->state && !old_aa.state) ||
(aa->state &&
(((old_aa.height != aa->height) ||
(old_aa.width != aa->width))))))
{
curtrack = audio_current_track();
offset = curtrack->offset;
audio_stop();
if (!(status & AUDIO_STATUS_PAUSE))
audio_play(offset);
}
}
#endif
wps_data->wps_loaded = true;
#ifdef DEBUG_SKIN_ENGINE
// if (isfile && debug_wps)
// debug_skin_usage();
#endif
return true;
}