summaryrefslogtreecommitdiff
path: root/apps/gui/wps_engine
diff options
context:
space:
mode:
Diffstat (limited to 'apps/gui/wps_engine')
-rw-r--r--apps/gui/wps_engine/gwps.h546
-rw-r--r--apps/gui/wps_engine/wps_debug.c641
-rw-r--r--apps/gui/wps_engine/wps_display.c1099
-rw-r--r--apps/gui/wps_engine/wps_engine.h49
-rw-r--r--apps/gui/wps_engine/wps_internals.h556
-rw-r--r--apps/gui/wps_engine/wps_parser.c1843
-rw-r--r--apps/gui/wps_engine/wps_tokens.c807
7 files changed, 5541 insertions, 0 deletions
diff --git a/apps/gui/wps_engine/gwps.h b/apps/gui/wps_engine/gwps.h
new file mode 100644
index 0000000000..2acde0dc39
--- /dev/null
+++ b/apps/gui/wps_engine/gwps.h
@@ -0,0 +1,546 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2007 Nicolas Pennequin
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#ifndef _WPS_H
22#define _WPS_H
23
24#include "screen_access.h"
25#include "statusbar.h"
26#include "metadata.h"
27
28/* constants used in line_type and as refresh_mode for wps_refresh */
29#define WPS_REFRESH_STATIC (1u<<0) /* line doesn't change over time */
30#define WPS_REFRESH_DYNAMIC (1u<<1) /* line may change (e.g. time flag) */
31#define WPS_REFRESH_SCROLL (1u<<2) /* line scrolls */
32#define WPS_REFRESH_PLAYER_PROGRESS (1u<<3) /* line contains a progress bar */
33#define WPS_REFRESH_PEAK_METER (1u<<4) /* line contains a peak meter */
34#define WPS_REFRESH_STATUSBAR (1u<<5) /* refresh statusbar */
35#define WPS_REFRESH_ALL (0xffffffffu) /* to refresh all line types */
36
37/* to refresh only those lines that change over time */
38#define WPS_REFRESH_NON_STATIC (WPS_REFRESH_DYNAMIC| \
39 WPS_REFRESH_PLAYER_PROGRESS| \
40 WPS_REFRESH_PEAK_METER)
41/* alignments */
42#define WPS_ALIGN_RIGHT 32
43#define WPS_ALIGN_CENTER 64
44#define WPS_ALIGN_LEFT 128
45
46#ifdef HAVE_ALBUMART
47
48/* albumart definitions */
49#define WPS_ALBUMART_NONE 0 /* WPS does not contain AA tag */
50#define WPS_ALBUMART_CHECK 1 /* WPS contains AA conditional tag */
51#define WPS_ALBUMART_LOAD 2 /* WPS contains AA tag */
52
53#define WPS_ALBUMART_ALIGN_RIGHT 1 /* x align: right */
54#define WPS_ALBUMART_ALIGN_CENTER 2 /* x/y align: center */
55#define WPS_ALBUMART_ALIGN_LEFT 4 /* x align: left */
56#define WPS_ALBUMART_ALIGN_TOP 1 /* y align: top */
57#define WPS_ALBUMART_ALIGN_BOTTOM 4 /* y align: bottom */
58
59#endif /* HAVE_ALBUMART */
60
61/* wps_data*/
62
63#ifdef HAVE_LCD_BITMAP
64struct gui_img {
65 struct bitmap bm;
66 struct viewport* vp; /* The viewport to display this image in */
67 short int x; /* x-pos */
68 short int y; /* y-pos */
69 short int num_subimages; /* number of sub-images */
70 short int subimage_height; /* height of each sub-image */
71 short int display; /* -1 for no display, 0..n to display a subimage */
72 bool loaded; /* load state */
73 bool always_display; /* not using the preload/display mechanism */
74};
75
76struct progressbar {
77 /* regular pb */
78 short x;
79 /* >=0: explicitly set in the tag -> y-coord within the viewport
80 <0 : not set in the tag -> negated 1-based line number within
81 the viewport. y-coord will be computed based on the font height */
82 short y;
83 short width;
84 short height;
85 /*progressbar image*/
86 struct bitmap bm;
87 bool have_bitmap_pb;
88};
89#endif
90
91
92
93struct align_pos {
94 char* left;
95 char* center;
96 char* right;
97};
98
99#ifdef HAVE_LCD_BITMAP
100
101#define MAX_IMAGES (26*2) /* a-z and A-Z */
102#define MAX_PROGRESSBARS 3
103
104/* The image buffer is big enough to store one full-screen native bitmap,
105 plus two full-screen mono bitmaps. */
106
107#define IMG_BUFSIZE ((LCD_HEIGHT*LCD_WIDTH*LCD_DEPTH/8) \
108 + (2*LCD_HEIGHT*LCD_WIDTH/8))
109
110#define WPS_MAX_VIEWPORTS 24
111#define WPS_MAX_LINES ((LCD_HEIGHT/5+1) * 2)
112#define WPS_MAX_SUBLINES (WPS_MAX_LINES*3)
113#define WPS_MAX_TOKENS 1024
114#define WPS_MAX_STRINGS 128
115#define STRING_BUFFER_SIZE 1024
116#define WPS_MAX_COND_LEVEL 10
117
118#else
119
120#define WPS_MAX_VIEWPORTS 2
121#define WPS_MAX_LINES 2
122#define WPS_MAX_SUBLINES 12
123#define WPS_MAX_TOKENS 64
124#define WPS_MAX_STRINGS 32
125#define STRING_BUFFER_SIZE 64
126#define WPS_MAX_COND_LEVEL 5
127
128#endif
129
130#define SUBLINE_RESET -1
131
132enum wps_parse_error {
133 PARSE_OK,
134 PARSE_FAIL_UNCLOSED_COND,
135 PARSE_FAIL_INVALID_CHAR,
136 PARSE_FAIL_COND_SYNTAX_ERROR,
137 PARSE_FAIL_COND_INVALID_PARAM,
138 PARSE_FAIL_LIMITS_EXCEEDED,
139};
140
141enum wps_token_type {
142 WPS_NO_TOKEN, /* for WPS tags we don't want to save as tokens */
143 WPS_TOKEN_UNKNOWN,
144
145 /* Markers */
146 WPS_TOKEN_CHARACTER,
147 WPS_TOKEN_STRING,
148
149 /* Alignment */
150 WPS_TOKEN_ALIGN_LEFT,
151 WPS_TOKEN_ALIGN_CENTER,
152 WPS_TOKEN_ALIGN_RIGHT,
153
154 /* Sublines */
155 WPS_TOKEN_SUBLINE_TIMEOUT,
156
157 /* Battery */
158 WPS_TOKEN_BATTERY_PERCENT,
159 WPS_TOKEN_BATTERY_VOLTS,
160 WPS_TOKEN_BATTERY_TIME,
161 WPS_TOKEN_BATTERY_CHARGER_CONNECTED,
162 WPS_TOKEN_BATTERY_CHARGING,
163 WPS_TOKEN_BATTERY_SLEEPTIME,
164
165 /* Sound */
166#if (CONFIG_CODEC != MAS3507D)
167 WPS_TOKEN_SOUND_PITCH,
168#endif
169#if (CONFIG_CODEC == SWCODEC)
170 WPS_TOKEN_REPLAYGAIN,
171 WPS_TOKEN_CROSSFADE,
172#endif
173
174 /* Time */
175
176 WPS_TOKEN_RTC_PRESENT,
177
178 /* The begin/end values allow us to know if a token is an RTC one.
179 New RTC tokens should be added between the markers. */
180
181 WPS_TOKENS_RTC_BEGIN, /* just the start marker, not an actual token */
182
183 WPS_TOKEN_RTC_DAY_OF_MONTH,
184 WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,
185 WPS_TOKEN_RTC_12HOUR_CFG,
186 WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED,
187 WPS_TOKEN_RTC_HOUR_24,
188 WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED,
189 WPS_TOKEN_RTC_HOUR_12,
190 WPS_TOKEN_RTC_MONTH,
191 WPS_TOKEN_RTC_MINUTE,
192 WPS_TOKEN_RTC_SECOND,
193 WPS_TOKEN_RTC_YEAR_2_DIGITS,
194 WPS_TOKEN_RTC_YEAR_4_DIGITS,
195 WPS_TOKEN_RTC_AM_PM_UPPER,
196 WPS_TOKEN_RTC_AM_PM_LOWER,
197 WPS_TOKEN_RTC_WEEKDAY_NAME,
198 WPS_TOKEN_RTC_MONTH_NAME,
199 WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON,
200 WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN,
201
202 WPS_TOKENS_RTC_END, /* just the end marker, not an actual token */
203
204 /* Conditional */
205 WPS_TOKEN_CONDITIONAL,
206 WPS_TOKEN_CONDITIONAL_START,
207 WPS_TOKEN_CONDITIONAL_OPTION,
208 WPS_TOKEN_CONDITIONAL_END,
209
210 /* Database */
211#ifdef HAVE_TAGCACHE
212 WPS_TOKEN_DATABASE_PLAYCOUNT,
213 WPS_TOKEN_DATABASE_RATING,
214 WPS_TOKEN_DATABASE_AUTOSCORE,
215#endif
216
217 /* File */
218 WPS_TOKEN_FILE_BITRATE,
219 WPS_TOKEN_FILE_CODEC,
220 WPS_TOKEN_FILE_FREQUENCY,
221 WPS_TOKEN_FILE_FREQUENCY_KHZ,
222 WPS_TOKEN_FILE_NAME,
223 WPS_TOKEN_FILE_NAME_WITH_EXTENSION,
224 WPS_TOKEN_FILE_PATH,
225 WPS_TOKEN_FILE_SIZE,
226 WPS_TOKEN_FILE_VBR,
227 WPS_TOKEN_FILE_DIRECTORY,
228
229#ifdef HAVE_LCD_BITMAP
230 /* Image */
231 WPS_TOKEN_IMAGE_BACKDROP,
232 WPS_TOKEN_IMAGE_PROGRESS_BAR,
233 WPS_TOKEN_IMAGE_PRELOAD,
234 WPS_TOKEN_IMAGE_PRELOAD_DISPLAY,
235 WPS_TOKEN_IMAGE_DISPLAY,
236#endif
237
238#ifdef HAVE_ALBUMART
239 /* Albumart */
240 WPS_TOKEN_ALBUMART_DISPLAY,
241 WPS_TOKEN_ALBUMART_FOUND,
242#endif
243
244 /* Metadata */
245 WPS_TOKEN_METADATA_ARTIST,
246 WPS_TOKEN_METADATA_COMPOSER,
247 WPS_TOKEN_METADATA_ALBUM_ARTIST,
248 WPS_TOKEN_METADATA_GROUPING,
249 WPS_TOKEN_METADATA_ALBUM,
250 WPS_TOKEN_METADATA_GENRE,
251 WPS_TOKEN_METADATA_DISC_NUMBER,
252 WPS_TOKEN_METADATA_TRACK_NUMBER,
253 WPS_TOKEN_METADATA_TRACK_TITLE,
254 WPS_TOKEN_METADATA_VERSION,
255 WPS_TOKEN_METADATA_YEAR,
256 WPS_TOKEN_METADATA_COMMENT,
257
258 /* Mode */
259 WPS_TOKEN_REPEAT_MODE,
260 WPS_TOKEN_PLAYBACK_STATUS,
261
262 WPS_TOKEN_MAIN_HOLD,
263
264#ifdef HAS_REMOTE_BUTTON_HOLD
265 WPS_TOKEN_REMOTE_HOLD,
266#endif
267
268 /* Progressbar */
269 WPS_TOKEN_PROGRESSBAR,
270#ifdef HAVE_LCD_CHARCELLS
271 WPS_TOKEN_PLAYER_PROGRESSBAR,
272#endif
273
274#ifdef HAVE_LCD_BITMAP
275 /* Peakmeter */
276 WPS_TOKEN_PEAKMETER,
277#endif
278
279 /* Volume level */
280 WPS_TOKEN_VOLUME,
281
282 /* Current track */
283 WPS_TOKEN_TRACK_ELAPSED_PERCENT,
284 WPS_TOKEN_TRACK_TIME_ELAPSED,
285 WPS_TOKEN_TRACK_TIME_REMAINING,
286 WPS_TOKEN_TRACK_LENGTH,
287
288 /* Playlist */
289 WPS_TOKEN_PLAYLIST_ENTRIES,
290 WPS_TOKEN_PLAYLIST_NAME,
291 WPS_TOKEN_PLAYLIST_POSITION,
292 WPS_TOKEN_PLAYLIST_SHUFFLE,
293
294#if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
295 /* Virtual LED */
296 WPS_TOKEN_VLED_HDD,
297#endif
298
299 /* Viewport display */
300 WPS_VIEWPORT_ENABLE,
301
302 /* buttons */
303 WPS_TOKEN_BUTTON_VOLUME,
304 WPS_TOKEN_LASTTOUCH,
305
306 /* Setting option */
307 WPS_TOKEN_SETTING,
308};
309
310struct wps_token {
311 unsigned char type; /* enough to store the token type */
312
313 /* Whether the tag (e.g. track name or the album) refers the
314 current or the next song (false=current, true=next) */
315 bool next;
316
317 union {
318 char c;
319 unsigned short i;
320 } value;
321};
322
323/* Description of a subline on the WPS */
324struct wps_subline {
325
326 /* Index of the first token for this subline in the token array.
327 Tokens of this subline end where tokens for the next subline
328 begin. */
329 unsigned short first_token_idx;
330
331 /* Bit or'ed WPS_REFRESH_xxx */
332 unsigned char line_type;
333
334 /* How long the subline should be displayed, in 10ths of sec */
335 unsigned char time_mult;
336};
337
338/* Description of a line on the WPS. A line is a set of sublines.
339 A subline is displayed for a certain amount of time. After that,
340 the next subline of the line is displayed. And so on. */
341struct wps_line {
342
343 /* Number of sublines in this line */
344 signed char num_sublines;
345
346 /* Number (0-based) of the subline within this line currently being displayed */
347 signed char curr_subline;
348
349 /* Index of the first subline of this line in the subline array.
350 Sublines for this line end where sublines for the next line begin. */
351 unsigned short first_subline_idx;
352
353 /* When the next subline of this line should be displayed
354 (absolute time value in ticks) */
355 long subline_expire_time;
356};
357
358#define VP_DRAW_HIDEABLE 0x1
359#define VP_DRAW_HIDDEN 0x2
360#define VP_DRAW_WASHIDDEN 0x4
361struct wps_viewport {
362 struct viewport vp; /* The LCD viewport struct */
363 struct progressbar *pb;
364 /* Indexes of the first and last lines belonging to this viewport in the
365 lines[] array */
366 int first_line, last_line;
367 char hidden_flags;
368 char label;
369};
370
371#ifdef HAVE_TOUCHSCREEN
372struct touchregion {
373 struct wps_viewport* wvp;/* The viewport this region is in */
374 short int x; /* x-pos */
375 short int y; /* y-pos */
376 short int width; /* width */
377 short int height; /* height */
378 bool repeat; /* requires the area be held for the action */
379 int action; /* action this button will return */
380};
381#define MAX_TOUCHREGIONS 15
382#endif
383/* wps_data
384 this struct holds all necessary data which describes the
385 viewable content of a wps */
386struct wps_data
387{
388#ifdef HAVE_LCD_BITMAP
389 struct gui_img img[MAX_IMAGES];
390 unsigned char img_buf[IMG_BUFSIZE];
391 unsigned char* img_buf_ptr;
392 int img_buf_free;
393 bool wps_sb_tag;
394 bool show_sb_on_wps;
395
396 struct progressbar progressbar[MAX_PROGRESSBARS];
397 short progressbar_count;
398
399 bool peak_meter_enabled;
400
401#ifdef HAVE_ALBUMART
402 /* Album art support */
403 unsigned char wps_uses_albumart; /* WPS_ALBUMART_NONE, _CHECK, _LOAD */
404 short albumart_x;
405 short albumart_y;
406 unsigned char albumart_xalign; /* WPS_ALBUMART_ALIGN_LEFT, _CENTER, _RIGHT */
407 unsigned char albumart_yalign; /* WPS_ALBUMART_ALIGN_TOP, _CENTER, _BOTTOM */
408 short albumart_max_width;
409 short albumart_max_height;
410
411 int albumart_cond_index;
412#endif
413
414#else /*HAVE_LCD_CHARCELLS */
415 unsigned short wps_progress_pat[8];
416 bool full_line_progressbar;
417#endif
418
419#ifdef HAVE_TOUCHSCREEN
420 struct touchregion touchregion[MAX_TOUCHREGIONS];
421 short touchregion_count;
422#endif
423
424#ifdef HAVE_REMOTE_LCD
425 bool remote_wps;
426#endif
427
428 /* Number of lines in the WPS. During WPS parsing, this is
429 the index of the line being parsed. */
430 int num_lines;
431
432 /* Number of viewports in the WPS */
433 int num_viewports;
434 struct wps_viewport viewports[WPS_MAX_VIEWPORTS];
435
436 struct wps_line lines[WPS_MAX_LINES];
437
438 /* Total number of sublines in the WPS. During WPS parsing, this is
439 the index of the subline where the parsed tokens are added to. */
440 int num_sublines;
441 struct wps_subline sublines[WPS_MAX_SUBLINES];
442
443 /* Total number of tokens in the WPS. During WPS parsing, this is
444 the index of the token being parsed. */
445 int num_tokens;
446 struct wps_token tokens[WPS_MAX_TOKENS];
447
448 char string_buffer[STRING_BUFFER_SIZE];
449 char *strings[WPS_MAX_STRINGS];
450 int num_strings;
451
452 bool wps_loaded;
453
454 /* tick the volume button was last pressed */
455 unsigned int button_time_volume;
456};
457
458/* initial setup of wps_data */
459void wps_data_init(struct wps_data *wps_data);
460
461/* to setup up the wps-data from a format-buffer (isfile = false)
462 from a (wps-)file (isfile = true)*/
463bool wps_data_load(struct wps_data *wps_data,
464 struct screen *display,
465 const char *buf,
466 bool isfile);
467
468/* Redraw statusbars if necessary */
469void gwps_draw_statusbars(void);
470
471/* Returns the index of the subline in the subline array
472 line - 0-based line number
473 subline - 0-based subline number within the line
474 */
475int wps_subline_index(struct wps_data *wps_data, int line, int subline);
476
477/* Returns the index of the first subline's token in the token array
478 line - 0-based line number
479 subline - 0-based subline number within the line
480 */
481int wps_first_token_index(struct wps_data *data, int line, int subline);
482
483/* Returns the index of the last subline's token in the token array.
484 line - 0-based line number
485 subline - 0-based subline number within the line
486 */
487int wps_last_token_index(struct wps_data *data, int line, int subline);
488
489/* wps_data end */
490
491/* wps_state
492 holds the data which belongs to the current played track,
493 the track which will be played afterwards, current path to the track
494 and some status infos */
495struct wps_state
496{
497 bool ff_rewind;
498 bool paused;
499 int ff_rewind_count;
500 bool wps_time_countup;
501 struct mp3entry* id3;
502 struct mp3entry* nid3;
503 bool do_full_update;
504};
505
506
507/* change the ff/rew-status
508 if ff_rew = true then we are in skipping mode
509 else we are in normal mode */
510/* void wps_state_update_ff_rew(bool ff_rew); Currently unused */
511
512/* change the tag-information of the current played track
513 and the following track */
514/* void wps_state_update_id3_nid3(struct mp3entry *id3, struct mp3entry *nid3); Currently unused */
515/* wps_state end*/
516
517/* gui_wps
518 defines a wps with its data, state,
519 and the screen on which the wps-content should be drawn */
520struct gui_wps
521{
522 struct screen *display;
523 struct wps_data *data;
524 struct wps_state *state;
525};
526
527/* gui_wps end */
528
529long gui_wps_show(void);
530
531/* currently only on wps_state is needed */
532extern struct wps_state wps_state;
533extern struct gui_wps gui_wps[NB_SCREENS];
534
535void gui_sync_wps_init(void);
536
537#ifdef HAVE_TOUCHSCREEN
538int wps_get_touchaction(struct wps_data *data);
539#endif
540
541#ifdef HAVE_ALBUMART
542/* gives back if WPS contains an albumart tag */
543bool gui_sync_wps_uses_albumart(void);
544#endif
545
546#endif
diff --git a/apps/gui/wps_engine/wps_debug.c b/apps/gui/wps_engine/wps_debug.c
new file mode 100644
index 0000000000..a89f61af9d
--- /dev/null
+++ b/apps/gui/wps_engine/wps_debug.c
@@ -0,0 +1,641 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2007 Nicolas Pennequin, Dan Everton, Matthias Mohr
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#if defined(DEBUG) || defined(SIMULATOR)
23
24#include <stdio.h>
25#include <string.h>
26#include "wps_internals.h"
27#ifdef __PCTOOL__
28#ifdef WPSEDITOR
29#include "proxy.h"
30#else
31#define DEBUGF printf
32#endif
33#else
34#include "debug.h"
35#endif
36
37#if defined(SIMULATOR) || defined(__PCTOOL__)
38extern bool debug_wps;
39extern int wps_verbose_level;
40#endif
41
42static char *next_str(bool next) {
43 return next ? "next " : "";
44}
45
46static char *get_token_desc(struct wps_token *token, struct wps_data *data,
47 char *buf, int bufsize)
48{
49 bool next = token->next;
50
51 switch(token->type)
52 {
53 case WPS_NO_TOKEN:
54 snprintf(buf, bufsize, "No token");
55 break;
56
57 case WPS_TOKEN_UNKNOWN:
58 snprintf(buf, bufsize, "Unknown token");
59 break;
60
61 case WPS_TOKEN_CHARACTER:
62 snprintf(buf, bufsize, "Character '%c'",
63 token->value.c);
64 break;
65
66 case WPS_TOKEN_STRING:
67 snprintf(buf, bufsize, "String '%s'",
68 data->strings[token->value.i]);
69 break;
70
71#ifdef HAVE_LCD_BITMAP
72 case WPS_TOKEN_ALIGN_LEFT:
73 snprintf(buf, bufsize, "align left");
74 break;
75
76 case WPS_TOKEN_ALIGN_CENTER:
77 snprintf(buf, bufsize, "align center");
78 break;
79
80 case WPS_TOKEN_ALIGN_RIGHT:
81 snprintf(buf, bufsize, "align right");
82 break;
83#endif
84
85 case WPS_TOKEN_SUBLINE_TIMEOUT:
86 snprintf(buf, bufsize, "subline timeout value: %d",
87 token->value.i);
88 break;
89
90 case WPS_TOKEN_CONDITIONAL:
91 snprintf(buf, bufsize, "conditional, %d options",
92 token->value.i);
93 break;
94
95 case WPS_TOKEN_CONDITIONAL_START:
96 snprintf(buf, bufsize, "conditional start, next cond: %d",
97 token->value.i);
98 break;
99
100 case WPS_TOKEN_CONDITIONAL_OPTION:
101 snprintf(buf, bufsize, "conditional option, next cond: %d",
102 token->value.i);
103 break;
104
105 case WPS_TOKEN_CONDITIONAL_END:
106 snprintf(buf, bufsize, "conditional end");
107 break;
108
109#ifdef HAVE_LCD_BITMAP
110 case WPS_TOKEN_IMAGE_PRELOAD:
111 snprintf(buf, bufsize, "preload image");
112 break;
113
114 case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY:
115 snprintf(buf, bufsize, "display preloaded image %d",
116 token->value.i);
117 break;
118
119 case WPS_TOKEN_IMAGE_DISPLAY:
120 snprintf(buf, bufsize, "display image");
121 break;
122#endif
123
124#ifdef HAS_BUTTON_HOLD
125 case WPS_TOKEN_MAIN_HOLD:
126 snprintf(buf, bufsize, "mode hold");
127 break;
128#endif
129
130#ifdef HAS_REMOTE_BUTTON_HOLD
131 case WPS_TOKEN_REMOTE_HOLD:
132 snprintf(buf, bufsize, "mode remote hold");
133 break;
134#endif
135
136 case WPS_TOKEN_REPEAT_MODE:
137 snprintf(buf, bufsize, "mode repeat");
138 break;
139
140 case WPS_TOKEN_PLAYBACK_STATUS:
141 snprintf(buf, bufsize, "mode playback");
142 break;
143
144 case WPS_TOKEN_RTC_DAY_OF_MONTH:
145 snprintf(buf, bufsize, "rtc: day of month (01..31)");
146 break;
147 case WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED:
148 snprintf(buf, bufsize,
149 "rtc: day of month, blank padded ( 1..31)");
150 break;
151 case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED:
152 snprintf(buf, bufsize, "rtc: hour (00..23)");
153 break;
154 case WPS_TOKEN_RTC_HOUR_24:
155 snprintf(buf, bufsize, "rtc: hour ( 0..23)");
156 break;
157 case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED:
158 snprintf(buf, bufsize, "rtc: hour (01..12)");
159 break;
160 case WPS_TOKEN_RTC_HOUR_12:
161 snprintf(buf, bufsize, "rtc: hour ( 1..12)");
162 break;
163 case WPS_TOKEN_RTC_MONTH:
164 snprintf(buf, bufsize, "rtc: month (01..12)");
165 break;
166 case WPS_TOKEN_RTC_MINUTE:
167 snprintf(buf, bufsize, "rtc: minute (00..59)");
168 break;
169 case WPS_TOKEN_RTC_SECOND:
170 snprintf(buf, bufsize, "rtc: second (00..59)");
171 break;
172 case WPS_TOKEN_RTC_YEAR_2_DIGITS:
173 snprintf(buf, bufsize,
174 "rtc: last two digits of year (00..99)");
175 break;
176 case WPS_TOKEN_RTC_YEAR_4_DIGITS:
177 snprintf(buf, bufsize, "rtc: year (1970...)");
178 break;
179 case WPS_TOKEN_RTC_AM_PM_UPPER:
180 snprintf(buf, bufsize,
181 "rtc: upper case AM or PM indicator");
182 break;
183 case WPS_TOKEN_RTC_AM_PM_LOWER:
184 snprintf(buf, bufsize,
185 "rtc: lower case am or pm indicator");
186 break;
187 case WPS_TOKEN_RTC_WEEKDAY_NAME:
188 snprintf(buf, bufsize,
189 "rtc: abbreviated weekday name (Sun..Sat)");
190 break;
191 case WPS_TOKEN_RTC_MONTH_NAME:
192 snprintf(buf, bufsize,
193 "rtc: abbreviated month name (Jan..Dec)");
194 break;
195 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON:
196 snprintf(buf, bufsize,
197 "rtc: day of week (1..7); 1 is Monday");
198 break;
199 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN:
200 snprintf(buf, bufsize,
201 "rtc: day of week (0..6); 0 is Sunday");
202 break;
203
204#if (CONFIG_CODEC == SWCODEC)
205 case WPS_TOKEN_CROSSFADE:
206 snprintf(buf, bufsize, "crossfade");
207 break;
208
209 case WPS_TOKEN_REPLAYGAIN:
210 snprintf(buf, bufsize, "replaygain");
211 break;
212#endif
213
214#ifdef HAVE_ALBUMART
215 case WPS_TOKEN_ALBUMART_DISPLAY:
216 snprintf(buf, bufsize, "album art display");
217 break;
218
219 case WPS_TOKEN_ALBUMART_FOUND:
220 snprintf(buf, bufsize, "%strack album art conditional",
221 next_str(next));
222 break;
223#endif
224
225#ifdef HAVE_LCD_BITMAP
226 case WPS_TOKEN_IMAGE_BACKDROP:
227 snprintf(buf, bufsize, "backdrop image");
228 break;
229
230 case WPS_TOKEN_IMAGE_PROGRESS_BAR:
231 snprintf(buf, bufsize, "progressbar bitmap");
232 break;
233
234 case WPS_TOKEN_PEAKMETER:
235 snprintf(buf, bufsize, "peakmeter");
236 break;
237#endif
238
239 case WPS_TOKEN_PROGRESSBAR:
240 snprintf(buf, bufsize, "progressbar");
241 break;
242
243#ifdef HAVE_LCD_CHARCELLS
244 case WPS_TOKEN_PLAYER_PROGRESSBAR:
245 snprintf(buf, bufsize, "full line progressbar");
246 break;
247#endif
248
249 case WPS_TOKEN_TRACK_TIME_ELAPSED:
250 snprintf(buf, bufsize, "time elapsed in track");
251 break;
252
253 case WPS_TOKEN_TRACK_ELAPSED_PERCENT:
254 snprintf(buf, bufsize, "played percentage of track");
255 break;
256
257 case WPS_TOKEN_PLAYLIST_ENTRIES:
258 snprintf(buf, bufsize, "number of entries in playlist");
259 break;
260
261 case WPS_TOKEN_PLAYLIST_NAME:
262 snprintf(buf, bufsize, "playlist name");
263 break;
264
265 case WPS_TOKEN_PLAYLIST_POSITION:
266 snprintf(buf, bufsize, "position in playlist");
267 break;
268
269 case WPS_TOKEN_TRACK_TIME_REMAINING:
270 snprintf(buf, bufsize, "time remaining in track");
271 break;
272
273 case WPS_TOKEN_PLAYLIST_SHUFFLE:
274 snprintf(buf, bufsize, "playlist shuffle mode");
275 break;
276
277 case WPS_TOKEN_TRACK_LENGTH:
278 snprintf(buf, bufsize, "track length");
279 break;
280
281 case WPS_TOKEN_VOLUME:
282 snprintf(buf, bufsize, "volume");
283 break;
284
285 case WPS_TOKEN_METADATA_ARTIST:
286 snprintf(buf, bufsize, "%strack artist",
287 next_str(next));
288 break;
289
290 case WPS_TOKEN_METADATA_COMPOSER:
291 snprintf(buf, bufsize, "%strack composer",
292 next_str(next));
293 break;
294
295 case WPS_TOKEN_METADATA_ALBUM:
296 snprintf(buf, bufsize, "%strack album",
297 next_str(next));
298 break;
299
300 case WPS_TOKEN_METADATA_GROUPING:
301 snprintf(buf, bufsize, "%strack grouping",
302 next_str(next));
303 break;
304
305 case WPS_TOKEN_METADATA_GENRE:
306 snprintf(buf, bufsize, "%strack genre",
307 next_str(next));
308 break;
309
310 case WPS_TOKEN_METADATA_DISC_NUMBER:
311 snprintf(buf, bufsize, "%strack disc", next_str(next));
312 break;
313
314 case WPS_TOKEN_METADATA_TRACK_NUMBER:
315 snprintf(buf, bufsize, "%strack number",
316 next_str(next));
317 break;
318
319 case WPS_TOKEN_METADATA_TRACK_TITLE:
320 snprintf(buf, bufsize, "%strack title",
321 next_str(next));
322 break;
323
324 case WPS_TOKEN_METADATA_VERSION:
325 snprintf(buf, bufsize, "%strack ID3 version",
326 next_str(next));
327 break;
328
329 case WPS_TOKEN_METADATA_ALBUM_ARTIST:
330 snprintf(buf, bufsize, "%strack album artist",
331 next_str(next));
332 break;
333
334 case WPS_TOKEN_METADATA_COMMENT:
335 snprintf(buf, bufsize, "%strack comment",
336 next_str(next));
337 break;
338
339 case WPS_TOKEN_METADATA_YEAR:
340 snprintf(buf, bufsize, "%strack year", next_str(next));
341 break;
342
343#ifdef HAVE_TAGCACHE
344 case WPS_TOKEN_DATABASE_PLAYCOUNT:
345 snprintf(buf, bufsize, "track playcount (database)");
346 break;
347
348 case WPS_TOKEN_DATABASE_RATING:
349 snprintf(buf, bufsize, "track rating (database)");
350 break;
351
352 case WPS_TOKEN_DATABASE_AUTOSCORE:
353 snprintf(buf, bufsize, "track autoscore (database)");
354 break;
355#endif
356
357 case WPS_TOKEN_BATTERY_PERCENT:
358 snprintf(buf, bufsize, "battery percentage");
359 break;
360
361 case WPS_TOKEN_BATTERY_VOLTS:
362 snprintf(buf, bufsize, "battery voltage");
363 break;
364
365 case WPS_TOKEN_BATTERY_TIME:
366 snprintf(buf, bufsize, "battery time left");
367 break;
368
369 case WPS_TOKEN_BATTERY_CHARGER_CONNECTED:
370 snprintf(buf, bufsize, "battery charger connected");
371 break;
372
373 case WPS_TOKEN_BATTERY_CHARGING:
374 snprintf(buf, bufsize, "battery charging");
375 break;
376
377 case WPS_TOKEN_BATTERY_SLEEPTIME:
378 snprintf(buf, bufsize, "sleep timer");
379 break;
380
381 case WPS_TOKEN_FILE_BITRATE:
382 snprintf(buf, bufsize, "%sfile bitrate", next_str(next));
383 break;
384
385 case WPS_TOKEN_FILE_CODEC:
386 snprintf(buf, bufsize, "%sfile codec", next_str(next));
387 break;
388
389 case WPS_TOKEN_FILE_FREQUENCY:
390 snprintf(buf, bufsize, "%sfile audio frequency in Hz",
391 next_str(next));
392 break;
393
394 case WPS_TOKEN_FILE_FREQUENCY_KHZ:
395 snprintf(buf, bufsize, "%sfile audio frequency in KHz",
396 next_str(next));
397 break;
398
399 case WPS_TOKEN_FILE_NAME:
400 snprintf(buf, bufsize, "%sfile name", next_str(next));
401 break;
402
403 case WPS_TOKEN_FILE_NAME_WITH_EXTENSION:
404 snprintf(buf, bufsize, "%sfile name with extension",
405 next_str(next));
406 break;
407
408 case WPS_TOKEN_FILE_PATH:
409 snprintf(buf, bufsize, "%sfile path", next_str(next));
410 break;
411
412 case WPS_TOKEN_FILE_SIZE:
413 snprintf(buf, bufsize, "%sfile size", next_str(next));
414 break;
415
416 case WPS_TOKEN_FILE_VBR:
417 snprintf(buf, bufsize, "%sfile is vbr", next_str(next));
418 break;
419
420 case WPS_TOKEN_FILE_DIRECTORY:
421 snprintf(buf, bufsize, "%sfile directory, level: %d",
422 next_str(next), token->value.i);
423 break;
424
425#if (CONFIG_CODEC != MAS3507D)
426 case WPS_TOKEN_SOUND_PITCH:
427 snprintf(buf, bufsize, "pitch value");
428 break;
429#endif
430 case WPS_VIEWPORT_ENABLE:
431 snprintf(buf, bufsize, "enable VP:%d",
432 token->value.i);
433 break;
434 case WPS_TOKEN_BUTTON_VOLUME:
435 snprintf(buf, bufsize, "Volume button timeout:%d",
436 token->value.i);
437 break;
438 default:
439 snprintf(buf, bufsize, "FIXME (code: %d)",
440 token->type);
441 break;
442 }
443
444 return buf;
445}
446
447#if defined(SIMULATOR) || defined(__PCTOOL__)
448static void dump_wps_tokens(struct wps_data *data)
449{
450 struct wps_token *token;
451 int i, j;
452 int indent = 0;
453 char buf[64];
454 int num_string_tokens = 0;
455
456 /* Dump parsed WPS */
457 for (i = 0, token = data->tokens; i < data->num_tokens; i++, token++)
458 {
459 get_token_desc(token, data, buf, sizeof(buf));
460
461 switch(token->type)
462 {
463 case WPS_TOKEN_STRING:
464 num_string_tokens++;
465 break;
466
467 case WPS_TOKEN_CONDITIONAL_START:
468 indent++;
469 break;
470
471 case WPS_TOKEN_CONDITIONAL_END:
472 indent--;
473 break;
474
475 default:
476 break;
477 }
478
479 if (wps_verbose_level > 2)
480 {
481 for(j = 0; j < indent; j++) {
482 DEBUGF("\t");
483 }
484
485 DEBUGF("[%3d] = (%2d) %s\n", i, token->type, buf);
486 }
487 }
488
489 if (wps_verbose_level > 0)
490 {
491 DEBUGF("\n");
492 DEBUGF("Number of string tokens: %d\n", num_string_tokens);
493 DEBUGF("\n");
494 }
495}
496
497static void print_line_info(struct wps_data *data)
498{
499 int i, j, v;
500 struct wps_line *line;
501 struct wps_subline *subline;
502
503 if (wps_verbose_level > 0)
504 {
505 DEBUGF("Number of viewports : %d\n", data->num_viewports);
506 for (v = 0; v < data->num_viewports; v++)
507 {
508 DEBUGF("vp %d: First line: %d\n", v, data->viewports[v].first_line);
509 DEBUGF("vp %d: Last line: %d\n", v, data->viewports[v].last_line);
510 }
511 DEBUGF("Number of sublines : %d\n", data->num_sublines);
512 DEBUGF("Number of tokens : %d\n", data->num_tokens);
513 DEBUGF("\n");
514 }
515
516 if (wps_verbose_level > 1)
517 {
518 for (v = 0; v < data->num_viewports; v++)
519 {
520 DEBUGF("Viewport %d - +%d+%d (%dx%d)\n",v,data->viewports[v].vp.x,
521 data->viewports[v].vp.y,
522 data->viewports[v].vp.width,
523 data->viewports[v].vp.height);
524 for (i = data->viewports[v].first_line, line = &data->lines[data->viewports[v].first_line]; i <= data->viewports[v].last_line; i++,line++)
525 {
526 DEBUGF("Line %2d (num_sublines=%d, first_subline=%d)\n",
527 i, line->num_sublines, line->first_subline_idx);
528
529 for (j = 0, subline = data->sublines + line->first_subline_idx;
530 j < line->num_sublines; j++, subline++)
531 {
532 DEBUGF(" Subline %d: first_token=%3d, last_token=%3d",
533 j, subline->first_token_idx,
534 wps_last_token_index(data, i, j));
535
536 if (subline->line_type & WPS_REFRESH_SCROLL)
537 DEBUGF(", scrolled");
538 else if (subline->line_type & WPS_REFRESH_PLAYER_PROGRESS)
539 DEBUGF(", progressbar");
540 else if (subline->line_type & WPS_REFRESH_PEAK_METER)
541 DEBUGF(", peakmeter");
542
543 DEBUGF("\n");
544 }
545 }
546 }
547
548 DEBUGF("\n");
549 }
550}
551
552static void print_wps_strings(struct wps_data *data)
553{
554 int i, len, total_len = 0, buf_used = 0;
555
556 if (wps_verbose_level > 1) DEBUGF("Strings:\n");
557 for (i = 0; i < data->num_strings; i++)
558 {
559 len = strlen(data->strings[i]);
560 total_len += len;
561 buf_used += len + 1;
562 if (wps_verbose_level > 1)
563 DEBUGF("%2d: (%2d) '%s'\n", i, len, data->strings[i]);
564 }
565 if (wps_verbose_level > 1) DEBUGF("\n");
566
567 if (wps_verbose_level > 0)
568 {
569 DEBUGF("Number of unique strings: %d (max: %d)\n",
570 data->num_strings, WPS_MAX_STRINGS);
571 DEBUGF("Total string length: %d\n", total_len);
572 DEBUGF("String buffer used: %d out of %d bytes\n",
573 buf_used, STRING_BUFFER_SIZE);
574 DEBUGF("\n");
575 }
576}
577#endif
578
579void print_debug_info(struct wps_data *data, enum wps_parse_error fail, int line)
580{
581#if defined(SIMULATOR) || defined(__PCTOOL__)
582 if (debug_wps && wps_verbose_level)
583 {
584 dump_wps_tokens(data);
585 print_wps_strings(data);
586 print_line_info(data);
587 }
588#endif /* SIMULATOR */
589
590 if (data->num_tokens >= WPS_MAX_TOKENS - 1) {
591 DEBUGF("Warning: Max number of tokens was reached (%d)\n",
592 WPS_MAX_TOKENS - 1);
593 }
594
595 if (fail != PARSE_OK)
596 {
597 char buf[64];
598
599 DEBUGF("ERR: Failed parsing on line %d : ", line);
600 switch (fail)
601 {
602 case PARSE_OK:
603 break;
604
605 case PARSE_FAIL_UNCLOSED_COND:
606 DEBUGF("ERR: Unclosed conditional");
607 break;
608
609 case PARSE_FAIL_INVALID_CHAR:
610 DEBUGF("ERR: Unexpected conditional char after token %d: \"%s\"",
611 data->num_tokens-1,
612 get_token_desc(&data->tokens[data->num_tokens-1], data,
613 buf, sizeof(buf))
614 );
615 break;
616
617 case PARSE_FAIL_COND_SYNTAX_ERROR:
618 DEBUGF("ERR: Conditional syntax error after token %d: \"%s\"",
619 data->num_tokens-1,
620 get_token_desc(&data->tokens[data->num_tokens-1], data,
621 buf, sizeof(buf))
622 );
623 break;
624
625 case PARSE_FAIL_COND_INVALID_PARAM:
626 DEBUGF("ERR: Invalid parameter list for token %d: \"%s\"",
627 data->num_tokens,
628 get_token_desc(&data->tokens[data->num_tokens], data,
629 buf, sizeof(buf))
630 );
631 break;
632
633 case PARSE_FAIL_LIMITS_EXCEEDED:
634 DEBUGF("ERR: Limits exceeded");
635 break;
636 }
637 DEBUGF("\n");
638 }
639}
640
641#endif /* DEBUG || SIMULATOR */
diff --git a/apps/gui/wps_engine/wps_display.c b/apps/gui/wps_engine/wps_display.c
new file mode 100644
index 0000000000..c74e2cedfa
--- /dev/null
+++ b/apps/gui/wps_engine/wps_display.c
@@ -0,0 +1,1099 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2002-2007 Björn Stenberg
11 * Copyright (C) 2007-2008 Nicolas Pennequin
12 *
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.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ****************************************************************************/
22#include "font.h"
23#include <stdio.h>
24#include <string.h>
25#include <stdlib.h>
26#include "system.h"
27#include "settings.h"
28#include "settings_list.h"
29#include "rbunicode.h"
30#include "rtc.h"
31#include "audio.h"
32#include "status.h"
33#include "power.h"
34#include "powermgmt.h"
35#include "sound.h"
36#include "debug.h"
37#ifdef HAVE_LCD_CHARCELLS
38#include "hwcompat.h"
39#endif
40#include "abrepeat.h"
41#include "mp3_playback.h"
42#include "lang.h"
43#include "misc.h"
44#include "splash.h"
45#include "scrollbar.h"
46#include "led.h"
47#include "lcd.h"
48#ifdef HAVE_LCD_BITMAP
49#include "peakmeter.h"
50/* Image stuff */
51#include "bmp.h"
52#include "albumart.h"
53#endif
54#include "dsp.h"
55#include "action.h"
56#include "cuesheet.h"
57#include "playlist.h"
58#if CONFIG_CODEC == SWCODEC
59#include "playback.h"
60#endif
61#include "backdrop.h"
62#include "viewport.h"
63
64
65#include "wps_internals.h"
66#include "wps_engine.h"
67
68bool gui_wps_display(struct gui_wps *gwps)
69{
70 struct screen *display = gwps->display;
71 struct wps_data *data = gwps->data;
72 int screen = display->screen_type;
73
74 /* Update the values in the first (default) viewport - in case the user
75 has modified the statusbar or colour settings */
76#if LCD_DEPTH > 1
77 if (display->depth > 1)
78 {
79 data->viewports[0].vp.fg_pattern = display->get_foreground();
80 data->viewports[0].vp.bg_pattern = display->get_background();
81 }
82#endif
83 display->clear_display();
84 if (!data->wps_loaded) {
85 if ( !data->num_tokens ) {
86 /* set the default wps for the main-screen */
87 if(screen == SCREEN_MAIN)
88 {
89#if LCD_DEPTH > 1
90 unload_wps_backdrop();
91#endif
92 wps_data_load(data,
93 display,
94#ifdef HAVE_LCD_BITMAP
95 "%s%?it<%?in<%in. |>%it|%fn>\n"
96 "%s%?ia<%ia|%?d2<%d2|(root)>>\n"
97 "%s%?id<%id|%?d1<%d1|(root)>> %?iy<(%iy)|>\n"
98 "\n"
99 "%al%pc/%pt%ar[%pp:%pe]\n"
100 "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n"
101 "%pb\n"
102 "%pm\n", false);
103#else
104 "%s%pp/%pe: %?it<%it|%fn> - %?ia<%ia|%d2> - %?id<%id|%d1>\n"
105 "%pc%?ps<*|/>%pt\n", false);
106#endif
107 }
108#ifdef HAVE_REMOTE_LCD
109 /* set the default wps for the remote-screen */
110 else if(screen == SCREEN_REMOTE)
111 {
112#if LCD_REMOTE_DEPTH > 1
113 unload_remote_wps_backdrop();
114#endif
115 wps_data_load(data,
116 display,
117 "%s%?ia<%ia|%?d2<%d2|(root)>>\n"
118 "%s%?it<%?in<%in. |>%it|%fn>\n"
119 "%al%pc/%pt%ar[%pp:%pe]\n"
120 "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n"
121 "%pb\n", false);
122 }
123#endif
124 }
125 }
126 else
127 {
128#if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
129 if (screen == SCREEN_REMOTE)
130 show_remote_wps_backdrop();
131 else if (screen == SCREEN_MAIN)
132#endif
133#if LCD_DEPTH > 1
134 show_wps_backdrop();
135#endif
136 }
137 return gui_wps_redraw(gwps, 0, WPS_REFRESH_ALL);
138}
139
140bool gui_wps_update(struct gui_wps *gwps)
141{
142 struct mp3entry *id3 = gwps->state->id3;
143 bool retval;
144 bool cuesheet_update = (id3 != NULL ? cuesheet_subtrack_changed(id3) : false);
145 gwps->state->do_full_update = cuesheet_update || gwps->state->do_full_update;
146 retval = gui_wps_redraw(gwps, 0,
147 gwps->state->do_full_update ?
148 WPS_REFRESH_ALL : WPS_REFRESH_NON_STATIC);
149 return retval;
150}
151
152
153#ifdef HAVE_LCD_BITMAP
154
155static void draw_progressbar(struct gui_wps *gwps,
156 struct wps_viewport *wps_vp)
157 {
158 struct screen *display = gwps->display;
159 struct wps_state *state = gwps->state;
160 struct progressbar *pb = wps_vp->pb;
161 int y = pb->y;
162
163 if (y < 0)
164 {
165 int line_height = font_get(wps_vp->vp.font)->height;
166 /* center the pb in the line, but only if the line is higher than the pb */
167 int center = (line_height-pb->height)/2;
168 /* if Y was not set calculate by font height,Y is -line_number-1 */
169 y = (-y -1)*line_height + (0 > center ? 0 : center);
170 }
171
172 if (pb->have_bitmap_pb)
173 gui_bitmap_scrollbar_draw(display, pb->bm,
174 pb->x, y, pb->width, pb->bm.height,
175 state->id3->length ? state->id3->length : 1, 0,
176 state->id3->length ? state->id3->elapsed
177 + state->ff_rewind_count : 0,
178 HORIZONTAL);
179 else
180 gui_scrollbar_draw(display, pb->x, y, pb->width, pb->height,
181 state->id3->length ? state->id3->length : 1, 0,
182 state->id3->length ? state->id3->elapsed
183 + state->ff_rewind_count : 0,
184 HORIZONTAL);
185#ifdef AB_REPEAT_ENABLE
186 if ( ab_repeat_mode_enabled() && state->id3->length != 0 )
187 ab_draw_markers(display, state->id3->length,
188 pb->x, pb->x + pb->width, y, pb->height);
189#endif
190
191 if (state->id3->cuesheet)
192 cue_draw_markers(display, state->id3->cuesheet, state->id3->length,
193 pb->x, pb->x + pb->width, y+1, pb->height-2);
194}
195
196/* clears the area where the image was shown */
197static void clear_image_pos(struct gui_wps *gwps, int n)
198{
199 if(!gwps)
200 return;
201 struct wps_data *data = gwps->data;
202 gwps->display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
203 gwps->display->fillrect(data->img[n].x, data->img[n].y,
204 data->img[n].bm.width, data->img[n].subimage_height);
205 gwps->display->set_drawmode(DRMODE_SOLID);
206}
207
208static void wps_draw_image(struct gui_wps *gwps, int n, int subimage)
209{
210 struct screen *display = gwps->display;
211 struct wps_data *data = gwps->data;
212 if(data->img[n].always_display)
213 display->set_drawmode(DRMODE_FG);
214 else
215 display->set_drawmode(DRMODE_SOLID);
216
217#if LCD_DEPTH > 1
218 if(data->img[n].bm.format == FORMAT_MONO) {
219#endif
220 display->mono_bitmap_part(data->img[n].bm.data,
221 0, data->img[n].subimage_height * subimage,
222 data->img[n].bm.width, data->img[n].x,
223 data->img[n].y, data->img[n].bm.width,
224 data->img[n].subimage_height);
225#if LCD_DEPTH > 1
226 } else {
227 display->transparent_bitmap_part((fb_data *)data->img[n].bm.data,
228 0, data->img[n].subimage_height * subimage,
229 data->img[n].bm.width, data->img[n].x,
230 data->img[n].y, data->img[n].bm.width,
231 data->img[n].subimage_height);
232 }
233#endif
234}
235
236static void wps_display_images(struct gui_wps *gwps, struct viewport* vp)
237{
238 if(!gwps || !gwps->data || !gwps->display)
239 return;
240
241 int n;
242 struct wps_data *data = gwps->data;
243 struct screen *display = gwps->display;
244
245 for (n = 0; n < MAX_IMAGES; n++)
246 {
247 if (data->img[n].loaded)
248 {
249 if (data->img[n].display >= 0)
250 {
251 wps_draw_image(gwps, n, data->img[n].display);
252 } else if (data->img[n].always_display && data->img[n].vp == vp)
253 {
254 wps_draw_image(gwps, n, 0);
255 }
256 }
257 }
258 display->set_drawmode(DRMODE_SOLID);
259}
260
261#else /* HAVE_LCD_CHARCELL */
262
263static bool draw_player_progress(struct gui_wps *gwps)
264{
265 struct wps_state *state = gwps->state;
266 struct screen *display = gwps->display;
267 unsigned char progress_pattern[7];
268 int pos = 0;
269 int i;
270
271 if (!state->id3)
272 return false;
273
274 if (state->id3->length)
275 pos = 36 * (state->id3->elapsed + state->ff_rewind_count)
276 / state->id3->length;
277
278 for (i = 0; i < 7; i++, pos -= 5)
279 {
280 if (pos <= 0)
281 progress_pattern[i] = 0x1fu;
282 else if (pos >= 5)
283 progress_pattern[i] = 0x00u;
284 else
285 progress_pattern[i] = 0x1fu >> pos;
286 }
287
288 display->define_pattern(gwps->data->wps_progress_pat[0], progress_pattern);
289 return true;
290}
291
292static void draw_player_fullbar(struct gui_wps *gwps, char* buf, int buf_size)
293{
294 static const unsigned char numbers[10][4] = {
295 {0x0e, 0x0a, 0x0a, 0x0e}, /* 0 */
296 {0x04, 0x0c, 0x04, 0x04}, /* 1 */
297 {0x0e, 0x02, 0x04, 0x0e}, /* 2 */
298 {0x0e, 0x02, 0x06, 0x0e}, /* 3 */
299 {0x08, 0x0c, 0x0e, 0x04}, /* 4 */
300 {0x0e, 0x0c, 0x02, 0x0c}, /* 5 */
301 {0x0e, 0x08, 0x0e, 0x0e}, /* 6 */
302 {0x0e, 0x02, 0x04, 0x08}, /* 7 */
303 {0x0e, 0x0e, 0x0a, 0x0e}, /* 8 */
304 {0x0e, 0x0e, 0x02, 0x0e}, /* 9 */
305 };
306
307 struct wps_state *state = gwps->state;
308 struct screen *display = gwps->display;
309 struct wps_data *data = gwps->data;
310 unsigned char progress_pattern[7];
311 char timestr[10];
312 int time;
313 int time_idx = 0;
314 int pos = 0;
315 int pat_idx = 1;
316 int digit, i, j;
317 bool softchar;
318
319 if (!state->id3 || buf_size < 34) /* worst case: 11x UTF-8 char + \0 */
320 return;
321
322 time = state->id3->elapsed + state->ff_rewind_count;
323 if (state->id3->length)
324 pos = 55 * time / state->id3->length;
325
326 memset(timestr, 0, sizeof(timestr));
327 format_time(timestr, sizeof(timestr)-2, time);
328 timestr[strlen(timestr)] = ':'; /* always safe */
329
330 for (i = 0; i < 11; i++, pos -= 5)
331 {
332 softchar = false;
333 memset(progress_pattern, 0, sizeof(progress_pattern));
334
335 if ((digit = timestr[time_idx]))
336 {
337 softchar = true;
338 digit -= '0';
339
340 if (timestr[time_idx + 1] == ':') /* ones, left aligned */
341 {
342 memcpy(progress_pattern, numbers[digit], 4);
343 time_idx += 2;
344 }
345 else /* tens, shifted right */
346 {
347 for (j = 0; j < 4; j++)
348 progress_pattern[j] = numbers[digit][j] >> 1;
349
350 if (time_idx > 0) /* not the first group, add colon in front */
351 {
352 progress_pattern[1] |= 0x10u;
353 progress_pattern[3] |= 0x10u;
354 }
355 time_idx++;
356 }
357
358 if (pos >= 5)
359 progress_pattern[5] = progress_pattern[6] = 0x1fu;
360 }
361
362 if (pos > 0 && pos < 5)
363 {
364 softchar = true;
365 progress_pattern[5] = progress_pattern[6] = (~0x1fu >> pos) & 0x1fu;
366 }
367
368 if (softchar && pat_idx < 8)
369 {
370 display->define_pattern(data->wps_progress_pat[pat_idx],
371 progress_pattern);
372 buf = utf8encode(data->wps_progress_pat[pat_idx], buf);
373 pat_idx++;
374 }
375 else if (pos <= 0)
376 buf = utf8encode(' ', buf);
377 else
378 buf = utf8encode(0xe115, buf); /* 2/7 _ */
379 }
380 *buf = '\0';
381}
382
383#endif /* HAVE_LCD_CHARCELL */
384
385/* Return the index to the end token for the conditional token at index.
386 The conditional token can be either a start token or a separator
387 (i.e. option) token.
388*/
389static int find_conditional_end(struct wps_data *data, int index)
390{
391 int ret = index;
392 while (data->tokens[ret].type != WPS_TOKEN_CONDITIONAL_END)
393 ret = data->tokens[ret].value.i;
394
395 /* ret now is the index to the end token for the conditional. */
396 return ret;
397}
398
399/* Evaluate the conditional that is at *token_index and return whether a skip
400 has ocurred. *token_index is updated with the new position.
401*/
402static bool evaluate_conditional(struct gui_wps *gwps, int *token_index)
403{
404 if (!gwps)
405 return false;
406
407 struct wps_data *data = gwps->data;
408
409 int i, cond_end;
410 int cond_index = *token_index;
411 char result[128];
412 const char *value;
413 unsigned char num_options = data->tokens[cond_index].value.i & 0xFF;
414 unsigned char prev_val = (data->tokens[cond_index].value.i & 0xFF00) >> 8;
415
416 /* treat ?xx<true> constructs as if they had 2 options. */
417 if (num_options < 2)
418 num_options = 2;
419
420 int intval = num_options;
421 /* get_token_value needs to know the number of options in the enum */
422 value = get_token_value(gwps, &data->tokens[cond_index + 1],
423 result, sizeof(result), &intval);
424
425 /* intval is now the number of the enum option we want to read,
426 starting from 1. If intval is -1, we check if value is empty. */
427 if (intval == -1)
428 intval = (value && *value) ? 1 : num_options;
429 else if (intval > num_options || intval < 1)
430 intval = num_options;
431
432 data->tokens[cond_index].value.i = (intval << 8) + num_options;
433
434 /* skip to the appropriate enum case */
435 int next = cond_index + 2;
436 for (i = 1; i < intval; i++)
437 {
438 next = data->tokens[next].value.i;
439 }
440 *token_index = next;
441
442 if (prev_val == intval)
443 {
444 /* Same conditional case as previously. Return without clearing the
445 pictures */
446 return false;
447 }
448
449 cond_end = find_conditional_end(data, cond_index + 2);
450 for (i = cond_index + 3; i < cond_end; i++)
451 {
452#ifdef HAVE_LCD_BITMAP
453 /* clear all pictures in the conditional and nested ones */
454 if (data->tokens[i].type == WPS_TOKEN_IMAGE_PRELOAD_DISPLAY)
455 clear_image_pos(gwps, data->tokens[i].value.i & 0xFF);
456#endif
457#ifdef HAVE_ALBUMART
458 if (data->tokens[i].type == WPS_TOKEN_ALBUMART_DISPLAY)
459 draw_album_art(gwps, audio_current_aa_hid(), true);
460#endif
461 }
462
463 return true;
464}
465
466/* Read a (sub)line to the given alignment format buffer.
467 linebuf is the buffer where the data is actually stored.
468 align is the alignment format that'll be used to display the text.
469 The return value indicates whether the line needs to be updated.
470*/
471static bool get_line(struct gui_wps *gwps,
472 int line, int subline,
473 struct align_pos *align,
474 char *linebuf,
475 int linebuf_size)
476{
477 struct wps_data *data = gwps->data;
478
479 char temp_buf[128];
480 char *buf = linebuf; /* will always point to the writing position */
481 char *linebuf_end = linebuf + linebuf_size - 1;
482 int i, last_token_idx;
483 bool update = false;
484
485 /* alignment-related variables */
486 int cur_align;
487 char* cur_align_start;
488 cur_align_start = buf;
489 cur_align = WPS_ALIGN_LEFT;
490 align->left = NULL;
491 align->center = NULL;
492 align->right = NULL;
493
494 /* Process all tokens of the desired subline */
495 last_token_idx = wps_last_token_index(data, line, subline);
496 for (i = wps_first_token_index(data, line, subline);
497 i <= last_token_idx; i++)
498 {
499 switch(data->tokens[i].type)
500 {
501 case WPS_TOKEN_CONDITIONAL:
502 /* place ourselves in the right conditional case */
503 update |= evaluate_conditional(gwps, &i);
504 break;
505
506 case WPS_TOKEN_CONDITIONAL_OPTION:
507 /* we've finished in the curent conditional case,
508 skip to the end of the conditional structure */
509 i = find_conditional_end(data, i);
510 break;
511
512#ifdef HAVE_LCD_BITMAP
513 case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY:
514 {
515 struct gui_img *img = data->img;
516 int n = data->tokens[i].value.i & 0xFF;
517 int subimage = data->tokens[i].value.i >> 8;
518
519 if (n >= 0 && n < MAX_IMAGES && img[n].loaded)
520 img[n].display = subimage;
521 break;
522 }
523#endif
524
525 case WPS_TOKEN_ALIGN_LEFT:
526 case WPS_TOKEN_ALIGN_CENTER:
527 case WPS_TOKEN_ALIGN_RIGHT:
528 /* remember where the current aligned text started */
529 switch (cur_align)
530 {
531 case WPS_ALIGN_LEFT:
532 align->left = cur_align_start;
533 break;
534
535 case WPS_ALIGN_CENTER:
536 align->center = cur_align_start;
537 break;
538
539 case WPS_ALIGN_RIGHT:
540 align->right = cur_align_start;
541 break;
542 }
543 /* start a new alignment */
544 switch (data->tokens[i].type)
545 {
546 case WPS_TOKEN_ALIGN_LEFT:
547 cur_align = WPS_ALIGN_LEFT;
548 break;
549 case WPS_TOKEN_ALIGN_CENTER:
550 cur_align = WPS_ALIGN_CENTER;
551 break;
552 case WPS_TOKEN_ALIGN_RIGHT:
553 cur_align = WPS_ALIGN_RIGHT;
554 break;
555 default:
556 break;
557 }
558 *buf++ = 0;
559 cur_align_start = buf;
560 break;
561 case WPS_VIEWPORT_ENABLE:
562 {
563 char label = data->tokens[i].value.i;
564 int j;
565 char temp = VP_DRAW_HIDEABLE;
566 for(j=0;j<data->num_viewports;j++)
567 {
568 temp = VP_DRAW_HIDEABLE;
569 if ((data->viewports[j].hidden_flags&VP_DRAW_HIDEABLE) &&
570 (data->viewports[j].label == label))
571 {
572 if (data->viewports[j].hidden_flags&VP_DRAW_WASHIDDEN)
573 temp |= VP_DRAW_WASHIDDEN;
574 data->viewports[j].hidden_flags = temp;
575 }
576 }
577 }
578 break;
579 default:
580 {
581 /* get the value of the tag and copy it to the buffer */
582 const char *value = get_token_value(gwps, &data->tokens[i],
583 temp_buf, sizeof(temp_buf), NULL);
584 if (value)
585 {
586 update = true;
587 while (*value && (buf < linebuf_end))
588 *buf++ = *value++;
589 }
590 break;
591 }
592 }
593 }
594
595 /* close the current alignment */
596 switch (cur_align)
597 {
598 case WPS_ALIGN_LEFT:
599 align->left = cur_align_start;
600 break;
601
602 case WPS_ALIGN_CENTER:
603 align->center = cur_align_start;
604 break;
605
606 case WPS_ALIGN_RIGHT:
607 align->right = cur_align_start;
608 break;
609 }
610
611 return update;
612}
613
614static void get_subline_timeout(struct gui_wps *gwps, int line, int subline)
615{
616 struct wps_data *data = gwps->data;
617 int i;
618 int subline_idx = wps_subline_index(data, line, subline);
619 int last_token_idx = wps_last_token_index(data, line, subline);
620
621 data->sublines[subline_idx].time_mult = DEFAULT_SUBLINE_TIME_MULTIPLIER;
622
623 for (i = wps_first_token_index(data, line, subline);
624 i <= last_token_idx; i++)
625 {
626 switch(data->tokens[i].type)
627 {
628 case WPS_TOKEN_CONDITIONAL:
629 /* place ourselves in the right conditional case */
630 evaluate_conditional(gwps, &i);
631 break;
632
633 case WPS_TOKEN_CONDITIONAL_OPTION:
634 /* we've finished in the curent conditional case,
635 skip to the end of the conditional structure */
636 i = find_conditional_end(data, i);
637 break;
638
639 case WPS_TOKEN_SUBLINE_TIMEOUT:
640 data->sublines[subline_idx].time_mult = data->tokens[i].value.i;
641 break;
642
643 default:
644 break;
645 }
646 }
647}
648
649/* Calculates which subline should be displayed for the specified line
650 Returns true iff the subline must be refreshed */
651static bool update_curr_subline(struct gui_wps *gwps, int line)
652{
653 struct wps_data *data = gwps->data;
654
655 int search, search_start, num_sublines;
656 bool reset_subline;
657 bool new_subline_refresh;
658 bool only_one_subline;
659
660 num_sublines = data->lines[line].num_sublines;
661 reset_subline = (data->lines[line].curr_subline == SUBLINE_RESET);
662 new_subline_refresh = false;
663 only_one_subline = false;
664
665 /* if time to advance to next sub-line */
666 if (TIME_AFTER(current_tick, data->lines[line].subline_expire_time - 1) ||
667 reset_subline)
668 {
669 /* search all sublines until the next subline with time > 0
670 is found or we get back to the subline we started with */
671 if (reset_subline)
672 search_start = 0;
673 else
674 search_start = data->lines[line].curr_subline;
675
676 for (search = 0; search < num_sublines; search++)
677 {
678 data->lines[line].curr_subline++;
679
680 /* wrap around if beyond last defined subline or WPS_MAX_SUBLINES */
681 if (data->lines[line].curr_subline == num_sublines)
682 {
683 if (data->lines[line].curr_subline == 1)
684 only_one_subline = true;
685 data->lines[line].curr_subline = 0;
686 }
687
688 /* if back where we started after search or
689 only one subline is defined on the line */
690 if (((search > 0) &&
691 (data->lines[line].curr_subline == search_start)) ||
692 only_one_subline)
693 {
694 /* no other subline with a time > 0 exists */
695 data->lines[line].subline_expire_time = (reset_subline ?
696 current_tick :
697 data->lines[line].subline_expire_time) + 100 * HZ;
698 break;
699 }
700 else
701 {
702 /* get initial time multiplier for this subline */
703 get_subline_timeout(gwps, line, data->lines[line].curr_subline);
704
705 int subline_idx = wps_subline_index(data, line,
706 data->lines[line].curr_subline);
707
708 /* only use this subline if subline time > 0 */
709 if (data->sublines[subline_idx].time_mult > 0)
710 {
711 new_subline_refresh = true;
712 data->lines[line].subline_expire_time = (reset_subline ?
713 current_tick : data->lines[line].subline_expire_time) +
714 TIMEOUT_UNIT*data->sublines[subline_idx].time_mult;
715 break;
716 }
717 }
718 }
719 }
720
721 return new_subline_refresh;
722}
723
724/* Display a line appropriately according to its alignment format.
725 format_align contains the text, separated between left, center and right.
726 line is the index of the line on the screen.
727 scroll indicates whether the line is a scrolling one or not.
728*/
729static void write_line(struct screen *display,
730 struct align_pos *format_align,
731 int line,
732 bool scroll)
733{
734 int left_width = 0, left_xpos;
735 int center_width = 0, center_xpos;
736 int right_width = 0, right_xpos;
737 int ypos;
738 int space_width;
739 int string_height;
740 int scroll_width;
741
742 /* calculate different string sizes and positions */
743 display->getstringsize((unsigned char *)" ", &space_width, &string_height);
744 if (format_align->left != 0) {
745 display->getstringsize((unsigned char *)format_align->left,
746 &left_width, &string_height);
747 }
748
749 if (format_align->right != 0) {
750 display->getstringsize((unsigned char *)format_align->right,
751 &right_width, &string_height);
752 }
753
754 if (format_align->center != 0) {
755 display->getstringsize((unsigned char *)format_align->center,
756 &center_width, &string_height);
757 }
758
759 left_xpos = 0;
760 right_xpos = (display->getwidth() - right_width);
761 center_xpos = (display->getwidth() + left_xpos - center_width) / 2;
762
763 scroll_width = display->getwidth() - left_xpos;
764
765 /* Checks for overlapping strings.
766 If needed the overlapping strings will be merged, separated by a
767 space */
768
769 /* CASE 1: left and centered string overlap */
770 /* there is a left string, need to merge left and center */
771 if ((left_width != 0 && center_width != 0) &&
772 (left_xpos + left_width + space_width > center_xpos)) {
773 /* replace the former separator '\0' of left and
774 center string with a space */
775 *(--format_align->center) = ' ';
776 /* calculate the new width and position of the merged string */
777 left_width = left_width + space_width + center_width;
778 /* there is no centered string anymore */
779 center_width = 0;
780 }
781 /* there is no left string, move center to left */
782 if ((left_width == 0 && center_width != 0) &&
783 (left_xpos + left_width > center_xpos)) {
784 /* move the center string to the left string */
785 format_align->left = format_align->center;
786 /* calculate the new width and position of the string */
787 left_width = center_width;
788 /* there is no centered string anymore */
789 center_width = 0;
790 }
791
792 /* CASE 2: centered and right string overlap */
793 /* there is a right string, need to merge center and right */
794 if ((center_width != 0 && right_width != 0) &&
795 (center_xpos + center_width + space_width > right_xpos)) {
796 /* replace the former separator '\0' of center and
797 right string with a space */
798 *(--format_align->right) = ' ';
799 /* move the center string to the right after merge */
800 format_align->right = format_align->center;
801 /* calculate the new width and position of the merged string */
802 right_width = center_width + space_width + right_width;
803 right_xpos = (display->getwidth() - right_width);
804 /* there is no centered string anymore */
805 center_width = 0;
806 }
807 /* there is no right string, move center to right */
808 if ((center_width != 0 && right_width == 0) &&
809 (center_xpos + center_width > right_xpos)) {
810 /* move the center string to the right string */
811 format_align->right = format_align->center;
812 /* calculate the new width and position of the string */
813 right_width = center_width;
814 right_xpos = (display->getwidth() - right_width);
815 /* there is no centered string anymore */
816 center_width = 0;
817 }
818
819 /* CASE 3: left and right overlap
820 There is no center string anymore, either there never
821 was one or it has been merged in case 1 or 2 */
822 /* there is a left string, need to merge left and right */
823 if ((left_width != 0 && center_width == 0 && right_width != 0) &&
824 (left_xpos + left_width + space_width > right_xpos)) {
825 /* replace the former separator '\0' of left and
826 right string with a space */
827 *(--format_align->right) = ' ';
828 /* calculate the new width and position of the string */
829 left_width = left_width + space_width + right_width;
830 /* there is no right string anymore */
831 right_width = 0;
832 }
833 /* there is no left string, move right to left */
834 if ((left_width == 0 && center_width == 0 && right_width != 0) &&
835 (left_width > right_xpos)) {
836 /* move the right string to the left string */
837 format_align->left = format_align->right;
838 /* calculate the new width and position of the string */
839 left_width = right_width;
840 /* there is no right string anymore */
841 right_width = 0;
842 }
843
844 ypos = (line * string_height);
845
846
847 if (scroll && ((left_width > scroll_width) ||
848 (center_width > scroll_width) ||
849 (right_width > scroll_width)))
850 {
851 display->puts_scroll(0, line,
852 (unsigned char *)format_align->left);
853 }
854 else
855 {
856#ifdef HAVE_LCD_BITMAP
857 /* clear the line first */
858 display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
859 display->fillrect(left_xpos, ypos, display->getwidth(), string_height);
860 display->set_drawmode(DRMODE_SOLID);
861#endif
862
863 /* Nasty hack: we output an empty scrolling string,
864 which will reset the scroller for that line */
865 display->puts_scroll(0, line, (unsigned char *)"");
866
867 /* print aligned strings */
868 if (left_width != 0)
869 {
870 display->putsxy(left_xpos, ypos,
871 (unsigned char *)format_align->left);
872 }
873 if (center_width != 0)
874 {
875 display->putsxy(center_xpos, ypos,
876 (unsigned char *)format_align->center);
877 }
878 if (right_width != 0)
879 {
880 display->putsxy(right_xpos, ypos,
881 (unsigned char *)format_align->right);
882 }
883 }
884}
885
886bool gui_wps_redraw(struct gui_wps *gwps,
887 int ffwd_offset,
888 unsigned refresh_mode)
889{
890 struct wps_data *data = gwps->data;
891 struct screen *display = gwps->display;
892 struct wps_state *state = gwps->state;
893
894 if (!data || !state || !display)
895 return false;
896
897 struct mp3entry *id3 = state->id3;
898
899 if (!id3)
900 return false;
901
902 int v, line, i, subline_idx;
903 unsigned flags;
904 char linebuf[MAX_PATH];
905
906 struct align_pos align;
907 align.left = NULL;
908 align.center = NULL;
909 align.right = NULL;
910
911 bool update_line, new_subline_refresh;
912
913#ifdef HAVE_LCD_BITMAP
914
915 /* to find out wether the peak meter is enabled we
916 assume it wasn't until we find a line that contains
917 the peak meter. We can't use peak_meter_enabled itself
918 because that would mean to turn off the meter thread
919 temporarily. (That shouldn't matter unless yield
920 or sleep is called but who knows...)
921 */
922 bool enable_pm = false;
923
924#endif
925
926 /* reset to first subline if refresh all flag is set */
927 if (refresh_mode == WPS_REFRESH_ALL)
928 {
929 display->set_viewport(&data->viewports[0].vp);
930 display->clear_viewport();
931
932 for (i = 0; i <= data->num_lines; i++)
933 {
934 data->lines[i].curr_subline = SUBLINE_RESET;
935 }
936 }
937
938#ifdef HAVE_LCD_CHARCELLS
939 for (i = 0; i < 8; i++)
940 {
941 if (data->wps_progress_pat[i] == 0)
942 data->wps_progress_pat[i] = display->get_locked_pattern();
943 }
944#endif
945
946 state->ff_rewind_count = ffwd_offset;
947
948 /* disable any viewports which are conditionally displayed */
949 for (v = 0; v < data->num_viewports; v++)
950 {
951 if (data->viewports[v].hidden_flags&VP_DRAW_HIDEABLE)
952 {
953 if (data->viewports[v].hidden_flags&VP_DRAW_HIDDEN)
954 data->viewports[v].hidden_flags |= VP_DRAW_WASHIDDEN;
955 else
956 data->viewports[v].hidden_flags |= VP_DRAW_HIDDEN;
957 }
958 }
959 for (v = 0; v < data->num_viewports; v++)
960 {
961 struct wps_viewport *wps_vp = &(data->viewports[v]);
962 unsigned vp_refresh_mode = refresh_mode;
963 display->set_viewport(&wps_vp->vp);
964
965#ifdef HAVE_LCD_BITMAP
966 /* Set images to not to be displayed */
967 for (i = 0; i < MAX_IMAGES; i++)
968 {
969 data->img[i].display = -1;
970 }
971#endif
972 /* dont redraw the viewport if its disabled */
973 if ((wps_vp->hidden_flags&VP_DRAW_HIDDEN))
974 {
975 if (!(wps_vp->hidden_flags&VP_DRAW_WASHIDDEN))
976 display->scroll_stop(&wps_vp->vp);
977 wps_vp->hidden_flags |= VP_DRAW_WASHIDDEN;
978 continue;
979 }
980 else if (((wps_vp->hidden_flags&
981 (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE))
982 == (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE)))
983 {
984 vp_refresh_mode = WPS_REFRESH_ALL;
985 wps_vp->hidden_flags = VP_DRAW_HIDEABLE;
986 }
987 if (vp_refresh_mode == WPS_REFRESH_ALL)
988 {
989 display->clear_viewport();
990 }
991
992 for (line = wps_vp->first_line;
993 line <= wps_vp->last_line; line++)
994 {
995 memset(linebuf, 0, sizeof(linebuf));
996 update_line = false;
997
998 /* get current subline for the line */
999 new_subline_refresh = update_curr_subline(gwps, line);
1000
1001 subline_idx = wps_subline_index(data, line,
1002 data->lines[line].curr_subline);
1003 flags = data->sublines[subline_idx].line_type;
1004
1005 if (vp_refresh_mode == WPS_REFRESH_ALL || (flags & vp_refresh_mode)
1006 || new_subline_refresh)
1007 {
1008 /* get_line tells us if we need to update the line */
1009 update_line = get_line(gwps, line, data->lines[line].curr_subline,
1010 &align, linebuf, sizeof(linebuf));
1011 }
1012#ifdef HAVE_LCD_BITMAP
1013 /* peakmeter */
1014 if (flags & vp_refresh_mode & WPS_REFRESH_PEAK_METER)
1015 {
1016 /* the peakmeter should be alone on its line */
1017 update_line = false;
1018
1019 int h = font_get(wps_vp->vp.font)->height;
1020 int peak_meter_y = (line - wps_vp->first_line)* h;
1021
1022 /* The user might decide to have the peak meter in the last
1023 line so that it is only displayed if no status bar is
1024 visible. If so we neither want do draw nor enable the
1025 peak meter. */
1026 if (peak_meter_y + h <= display->getheight()) {
1027 /* found a line with a peak meter -> remember that we must
1028 enable it later */
1029 enable_pm = true;
1030 peak_meter_enabled = true;
1031 peak_meter_screen(gwps->display, 0, peak_meter_y,
1032 MIN(h, display->getheight() - peak_meter_y));
1033 }
1034 else
1035 {
1036 peak_meter_enabled = false;
1037 }
1038 }
1039
1040#else /* HAVE_LCD_CHARCELL */
1041
1042 /* progressbar */
1043 if (flags & vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1044 {
1045 if (data->full_line_progressbar)
1046 draw_player_fullbar(gwps, linebuf, sizeof(linebuf));
1047 else
1048 draw_player_progress(gwps);
1049 }
1050#endif
1051
1052 if (update_line &&
1053 /* conditionals clear the line which means if the %Vd is put into the default
1054 viewport there will be a blank line.
1055 To get around this we dont allow any actual drawing to happen in the
1056 deault vp if other vp's are defined */
1057 ((data->num_viewports>1 && v!=0) || data->num_viewports == 1))
1058 {
1059 if (flags & WPS_REFRESH_SCROLL)
1060 {
1061 /* if the line is a scrolling one we don't want to update
1062 too often, so that it has the time to scroll */
1063 if ((vp_refresh_mode & WPS_REFRESH_SCROLL) || new_subline_refresh)
1064 write_line(display, &align, line - wps_vp->first_line, true);
1065 }
1066 else
1067 write_line(display, &align, line - wps_vp->first_line, false);
1068 }
1069 }
1070
1071#ifdef HAVE_LCD_BITMAP
1072 /* progressbar */
1073 if (vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1074 {
1075 if (wps_vp->pb)
1076 {
1077 draw_progressbar(gwps, wps_vp);
1078 }
1079 }
1080 /* Now display any images in this viewport */
1081 wps_display_images(gwps, &wps_vp->vp);
1082#endif
1083 }
1084
1085#ifdef HAVE_LCD_BITMAP
1086 data->peak_meter_enabled = enable_pm;
1087#endif
1088
1089 if (refresh_mode & WPS_REFRESH_STATUSBAR)
1090 {
1091 gwps_draw_statusbars();
1092 }
1093 /* Restore the default viewport */
1094 display->set_viewport(NULL);
1095
1096 display->update();
1097
1098 return true;
1099}
diff --git a/apps/gui/wps_engine/wps_engine.h b/apps/gui/wps_engine/wps_engine.h
new file mode 100644
index 0000000000..fe034f9138
--- /dev/null
+++ b/apps/gui/wps_engine/wps_engine.h
@@ -0,0 +1,49 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id: gwps.h 22003 2009-07-22 22:10:25Z kugel $
9 *
10 * Copyright (C) 2007 Nicolas Pennequin
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22 /** Use this for stuff which external code needs to include **/
23
24#ifndef _WPS_ENGINE_H
25#define _WPS_ENGINE_H
26#include <stdbool.h>
27#include "wps_internals.h" /* TODO: remove this line.. shoudlnt be needed */
28
29
30#ifdef HAVE_TOUCHSCREEN
31int wps_get_touchaction(struct wps_data *data);
32#endif
33
34#ifdef HAVE_ALBUMART
35/* gives back if WPS contains an albumart tag */
36bool gui_sync_wps_uses_albumart(void);
37#endif
38
39/* setup and display a WPS for the first time */
40bool gui_wps_display(struct gui_wps *gwps);
41/* do a requested redraw */
42bool gui_wps_redraw(struct gui_wps *gwps,
43 int ffwd_offset,
44 unsigned refresh_mode);
45/* do a partial redraw, or full if required, also do any housekeeping
46 * which might be needed */
47bool gui_wps_update(struct gui_wps *gwps);
48
49#endif
diff --git a/apps/gui/wps_engine/wps_internals.h b/apps/gui/wps_engine/wps_internals.h
new file mode 100644
index 0000000000..581763fb9a
--- /dev/null
+++ b/apps/gui/wps_engine/wps_internals.h
@@ -0,0 +1,556 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2007 Nicolas Pennequin
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22 /* This stuff is for the wps engine only.. anyone caught using this outside
23 * of apps/gui/wps_engine will be shot on site! */
24
25#ifndef _WPS_ENGINE_INTERNALS_
26#define _WPS_ENGINE_INTERNALS_
27/* Timeout unit expressed in HZ. In WPS, all timeouts are given in seconds
28 (possibly with a decimal fraction) but stored as integer values.
29 E.g. 2.5 is stored as 25. This means 25 tenth of a second, i.e. 25 units.
30*/
31#define TIMEOUT_UNIT (HZ/10) /* I.e. 0.1 sec */
32#define DEFAULT_SUBLINE_TIME_MULTIPLIER 20 /* In TIMEOUT_UNIT's */
33
34
35
36
37/* TODO: sort this mess out */
38
39#include "screen_access.h"
40#include "statusbar.h"
41#include "metadata.h"
42
43/* constants used in line_type and as refresh_mode for wps_refresh */
44#define WPS_REFRESH_STATIC (1u<<0) /* line doesn't change over time */
45#define WPS_REFRESH_DYNAMIC (1u<<1) /* line may change (e.g. time flag) */
46#define WPS_REFRESH_SCROLL (1u<<2) /* line scrolls */
47#define WPS_REFRESH_PLAYER_PROGRESS (1u<<3) /* line contains a progress bar */
48#define WPS_REFRESH_PEAK_METER (1u<<4) /* line contains a peak meter */
49#define WPS_REFRESH_STATUSBAR (1u<<5) /* refresh statusbar */
50#define WPS_REFRESH_ALL (0xffffffffu) /* to refresh all line types */
51
52/* to refresh only those lines that change over time */
53#define WPS_REFRESH_NON_STATIC (WPS_REFRESH_DYNAMIC| \
54 WPS_REFRESH_PLAYER_PROGRESS| \
55 WPS_REFRESH_PEAK_METER)
56/* alignments */
57#define WPS_ALIGN_RIGHT 32
58#define WPS_ALIGN_CENTER 64
59#define WPS_ALIGN_LEFT 128
60
61#ifdef HAVE_ALBUMART
62
63/* albumart definitions */
64#define WPS_ALBUMART_NONE 0 /* WPS does not contain AA tag */
65#define WPS_ALBUMART_CHECK 1 /* WPS contains AA conditional tag */
66#define WPS_ALBUMART_LOAD 2 /* WPS contains AA tag */
67
68#define WPS_ALBUMART_ALIGN_RIGHT 1 /* x align: right */
69#define WPS_ALBUMART_ALIGN_CENTER 2 /* x/y align: center */
70#define WPS_ALBUMART_ALIGN_LEFT 4 /* x align: left */
71#define WPS_ALBUMART_ALIGN_TOP 1 /* y align: top */
72#define WPS_ALBUMART_ALIGN_BOTTOM 4 /* y align: bottom */
73
74#endif /* HAVE_ALBUMART */
75
76/* wps_data*/
77
78#ifdef HAVE_LCD_BITMAP
79struct gui_img {
80 struct bitmap bm;
81 struct viewport* vp; /* The viewport to display this image in */
82 short int x; /* x-pos */
83 short int y; /* y-pos */
84 short int num_subimages; /* number of sub-images */
85 short int subimage_height; /* height of each sub-image */
86 short int display; /* -1 for no display, 0..n to display a subimage */
87 bool loaded; /* load state */
88 bool always_display; /* not using the preload/display mechanism */
89};
90
91struct progressbar {
92 /* regular pb */
93 short x;
94 /* >=0: explicitly set in the tag -> y-coord within the viewport
95 <0 : not set in the tag -> negated 1-based line number within
96 the viewport. y-coord will be computed based on the font height */
97 short y;
98 short width;
99 short height;
100 /*progressbar image*/
101 struct bitmap bm;
102 bool have_bitmap_pb;
103};
104#endif
105
106
107
108struct align_pos {
109 char* left;
110 char* center;
111 char* right;
112};
113
114#ifdef HAVE_LCD_BITMAP
115
116#define MAX_IMAGES (26*2) /* a-z and A-Z */
117#define MAX_PROGRESSBARS 3
118
119/* The image buffer is big enough to store one full-screen native bitmap,
120 plus two full-screen mono bitmaps. */
121
122#define IMG_BUFSIZE ((LCD_HEIGHT*LCD_WIDTH*LCD_DEPTH/8) \
123 + (2*LCD_HEIGHT*LCD_WIDTH/8))
124
125#define WPS_MAX_VIEWPORTS 24
126#define WPS_MAX_LINES ((LCD_HEIGHT/5+1) * 2)
127#define WPS_MAX_SUBLINES (WPS_MAX_LINES*3)
128#define WPS_MAX_TOKENS 1024
129#define WPS_MAX_STRINGS 128
130#define STRING_BUFFER_SIZE 1024
131#define WPS_MAX_COND_LEVEL 10
132
133#else
134
135#define WPS_MAX_VIEWPORTS 2
136#define WPS_MAX_LINES 2
137#define WPS_MAX_SUBLINES 12
138#define WPS_MAX_TOKENS 64
139#define WPS_MAX_STRINGS 32
140#define STRING_BUFFER_SIZE 64
141#define WPS_MAX_COND_LEVEL 5
142
143#endif
144
145#define SUBLINE_RESET -1
146
147enum wps_parse_error {
148 PARSE_OK,
149 PARSE_FAIL_UNCLOSED_COND,
150 PARSE_FAIL_INVALID_CHAR,
151 PARSE_FAIL_COND_SYNTAX_ERROR,
152 PARSE_FAIL_COND_INVALID_PARAM,
153 PARSE_FAIL_LIMITS_EXCEEDED,
154};
155
156enum wps_token_type {
157 WPS_NO_TOKEN, /* for WPS tags we don't want to save as tokens */
158 WPS_TOKEN_UNKNOWN,
159
160 /* Markers */
161 WPS_TOKEN_CHARACTER,
162 WPS_TOKEN_STRING,
163
164 /* Alignment */
165 WPS_TOKEN_ALIGN_LEFT,
166 WPS_TOKEN_ALIGN_CENTER,
167 WPS_TOKEN_ALIGN_RIGHT,
168
169 /* Sublines */
170 WPS_TOKEN_SUBLINE_TIMEOUT,
171
172 /* Battery */
173 WPS_TOKEN_BATTERY_PERCENT,
174 WPS_TOKEN_BATTERY_VOLTS,
175 WPS_TOKEN_BATTERY_TIME,
176 WPS_TOKEN_BATTERY_CHARGER_CONNECTED,
177 WPS_TOKEN_BATTERY_CHARGING,
178 WPS_TOKEN_BATTERY_SLEEPTIME,
179
180 /* Sound */
181#if (CONFIG_CODEC != MAS3507D)
182 WPS_TOKEN_SOUND_PITCH,
183#endif
184#if (CONFIG_CODEC == SWCODEC)
185 WPS_TOKEN_REPLAYGAIN,
186 WPS_TOKEN_CROSSFADE,
187#endif
188
189 /* Time */
190
191 WPS_TOKEN_RTC_PRESENT,
192
193 /* The begin/end values allow us to know if a token is an RTC one.
194 New RTC tokens should be added between the markers. */
195
196 WPS_TOKENS_RTC_BEGIN, /* just the start marker, not an actual token */
197
198 WPS_TOKEN_RTC_DAY_OF_MONTH,
199 WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,
200 WPS_TOKEN_RTC_12HOUR_CFG,
201 WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED,
202 WPS_TOKEN_RTC_HOUR_24,
203 WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED,
204 WPS_TOKEN_RTC_HOUR_12,
205 WPS_TOKEN_RTC_MONTH,
206 WPS_TOKEN_RTC_MINUTE,
207 WPS_TOKEN_RTC_SECOND,
208 WPS_TOKEN_RTC_YEAR_2_DIGITS,
209 WPS_TOKEN_RTC_YEAR_4_DIGITS,
210 WPS_TOKEN_RTC_AM_PM_UPPER,
211 WPS_TOKEN_RTC_AM_PM_LOWER,
212 WPS_TOKEN_RTC_WEEKDAY_NAME,
213 WPS_TOKEN_RTC_MONTH_NAME,
214 WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON,
215 WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN,
216
217 WPS_TOKENS_RTC_END, /* just the end marker, not an actual token */
218
219 /* Conditional */
220 WPS_TOKEN_CONDITIONAL,
221 WPS_TOKEN_CONDITIONAL_START,
222 WPS_TOKEN_CONDITIONAL_OPTION,
223 WPS_TOKEN_CONDITIONAL_END,
224
225 /* Database */
226#ifdef HAVE_TAGCACHE
227 WPS_TOKEN_DATABASE_PLAYCOUNT,
228 WPS_TOKEN_DATABASE_RATING,
229 WPS_TOKEN_DATABASE_AUTOSCORE,
230#endif
231
232 /* File */
233 WPS_TOKEN_FILE_BITRATE,
234 WPS_TOKEN_FILE_CODEC,
235 WPS_TOKEN_FILE_FREQUENCY,
236 WPS_TOKEN_FILE_FREQUENCY_KHZ,
237 WPS_TOKEN_FILE_NAME,
238 WPS_TOKEN_FILE_NAME_WITH_EXTENSION,
239 WPS_TOKEN_FILE_PATH,
240 WPS_TOKEN_FILE_SIZE,
241 WPS_TOKEN_FILE_VBR,
242 WPS_TOKEN_FILE_DIRECTORY,
243
244#ifdef HAVE_LCD_BITMAP
245 /* Image */
246 WPS_TOKEN_IMAGE_BACKDROP,
247 WPS_TOKEN_IMAGE_PROGRESS_BAR,
248 WPS_TOKEN_IMAGE_PRELOAD,
249 WPS_TOKEN_IMAGE_PRELOAD_DISPLAY,
250 WPS_TOKEN_IMAGE_DISPLAY,
251#endif
252
253#ifdef HAVE_ALBUMART
254 /* Albumart */
255 WPS_TOKEN_ALBUMART_DISPLAY,
256 WPS_TOKEN_ALBUMART_FOUND,
257#endif
258
259 /* Metadata */
260 WPS_TOKEN_METADATA_ARTIST,
261 WPS_TOKEN_METADATA_COMPOSER,
262 WPS_TOKEN_METADATA_ALBUM_ARTIST,
263 WPS_TOKEN_METADATA_GROUPING,
264 WPS_TOKEN_METADATA_ALBUM,
265 WPS_TOKEN_METADATA_GENRE,
266 WPS_TOKEN_METADATA_DISC_NUMBER,
267 WPS_TOKEN_METADATA_TRACK_NUMBER,
268 WPS_TOKEN_METADATA_TRACK_TITLE,
269 WPS_TOKEN_METADATA_VERSION,
270 WPS_TOKEN_METADATA_YEAR,
271 WPS_TOKEN_METADATA_COMMENT,
272
273 /* Mode */
274 WPS_TOKEN_REPEAT_MODE,
275 WPS_TOKEN_PLAYBACK_STATUS,
276
277 WPS_TOKEN_MAIN_HOLD,
278
279#ifdef HAS_REMOTE_BUTTON_HOLD
280 WPS_TOKEN_REMOTE_HOLD,
281#endif
282
283 /* Progressbar */
284 WPS_TOKEN_PROGRESSBAR,
285#ifdef HAVE_LCD_CHARCELLS
286 WPS_TOKEN_PLAYER_PROGRESSBAR,
287#endif
288
289#ifdef HAVE_LCD_BITMAP
290 /* Peakmeter */
291 WPS_TOKEN_PEAKMETER,
292#endif
293
294 /* Volume level */
295 WPS_TOKEN_VOLUME,
296
297 /* Current track */
298 WPS_TOKEN_TRACK_ELAPSED_PERCENT,
299 WPS_TOKEN_TRACK_TIME_ELAPSED,
300 WPS_TOKEN_TRACK_TIME_REMAINING,
301 WPS_TOKEN_TRACK_LENGTH,
302
303 /* Playlist */
304 WPS_TOKEN_PLAYLIST_ENTRIES,
305 WPS_TOKEN_PLAYLIST_NAME,
306 WPS_TOKEN_PLAYLIST_POSITION,
307 WPS_TOKEN_PLAYLIST_SHUFFLE,
308
309#if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
310 /* Virtual LED */
311 WPS_TOKEN_VLED_HDD,
312#endif
313
314 /* Viewport display */
315 WPS_VIEWPORT_ENABLE,
316
317 /* buttons */
318 WPS_TOKEN_BUTTON_VOLUME,
319 WPS_TOKEN_LASTTOUCH,
320
321 /* Setting option */
322 WPS_TOKEN_SETTING,
323};
324
325struct wps_token {
326 unsigned char type; /* enough to store the token type */
327
328 /* Whether the tag (e.g. track name or the album) refers the
329 current or the next song (false=current, true=next) */
330 bool next;
331
332 union {
333 char c;
334 unsigned short i;
335 } value;
336};
337
338/* Description of a subline on the WPS */
339struct wps_subline {
340
341 /* Index of the first token for this subline in the token array.
342 Tokens of this subline end where tokens for the next subline
343 begin. */
344 unsigned short first_token_idx;
345
346 /* Bit or'ed WPS_REFRESH_xxx */
347 unsigned char line_type;
348
349 /* How long the subline should be displayed, in 10ths of sec */
350 unsigned char time_mult;
351};
352
353/* Description of a line on the WPS. A line is a set of sublines.
354 A subline is displayed for a certain amount of time. After that,
355 the next subline of the line is displayed. And so on. */
356struct wps_line {
357
358 /* Number of sublines in this line */
359 signed char num_sublines;
360
361 /* Number (0-based) of the subline within this line currently being displayed */
362 signed char curr_subline;
363
364 /* Index of the first subline of this line in the subline array.
365 Sublines for this line end where sublines for the next line begin. */
366 unsigned short first_subline_idx;
367
368 /* When the next subline of this line should be displayed
369 (absolute time value in ticks) */
370 long subline_expire_time;
371};
372
373#define VP_DRAW_HIDEABLE 0x1
374#define VP_DRAW_HIDDEN 0x2
375#define VP_DRAW_WASHIDDEN 0x4
376struct wps_viewport {
377 struct viewport vp; /* The LCD viewport struct */
378 struct progressbar *pb;
379 /* Indexes of the first and last lines belonging to this viewport in the
380 lines[] array */
381 int first_line, last_line;
382 char hidden_flags;
383 char label;
384};
385
386#ifdef HAVE_TOUCHSCREEN
387struct touchregion {
388 struct wps_viewport* wvp;/* The viewport this region is in */
389 short int x; /* x-pos */
390 short int y; /* y-pos */
391 short int width; /* width */
392 short int height; /* height */
393 bool repeat; /* requires the area be held for the action */
394 int action; /* action this button will return */
395};
396#define MAX_TOUCHREGIONS 15
397#endif
398/* wps_data
399 this struct holds all necessary data which describes the
400 viewable content of a wps */
401struct wps_data
402{
403#ifdef HAVE_LCD_BITMAP
404 struct gui_img img[MAX_IMAGES];
405 unsigned char img_buf[IMG_BUFSIZE];
406 unsigned char* img_buf_ptr;
407 int img_buf_free;
408 bool wps_sb_tag;
409 bool show_sb_on_wps;
410
411 struct progressbar progressbar[MAX_PROGRESSBARS];
412 short progressbar_count;
413
414 bool peak_meter_enabled;
415
416#ifdef HAVE_ALBUMART
417 /* Album art support */
418 unsigned char wps_uses_albumart; /* WPS_ALBUMART_NONE, _CHECK, _LOAD */
419 short albumart_x;
420 short albumart_y;
421 unsigned char albumart_xalign; /* WPS_ALBUMART_ALIGN_LEFT, _CENTER, _RIGHT */
422 unsigned char albumart_yalign; /* WPS_ALBUMART_ALIGN_TOP, _CENTER, _BOTTOM */
423 short albumart_max_width;
424 short albumart_max_height;
425
426 int albumart_cond_index;
427#endif
428
429#else /*HAVE_LCD_CHARCELLS */
430 unsigned short wps_progress_pat[8];
431 bool full_line_progressbar;
432#endif
433
434#ifdef HAVE_TOUCHSCREEN
435 struct touchregion touchregion[MAX_TOUCHREGIONS];
436 short touchregion_count;
437#endif
438
439#ifdef HAVE_REMOTE_LCD
440 bool remote_wps;
441#endif
442
443 /* Number of lines in the WPS. During WPS parsing, this is
444 the index of the line being parsed. */
445 int num_lines;
446
447 /* Number of viewports in the WPS */
448 int num_viewports;
449 struct wps_viewport viewports[WPS_MAX_VIEWPORTS];
450
451 struct wps_line lines[WPS_MAX_LINES];
452
453 /* Total number of sublines in the WPS. During WPS parsing, this is
454 the index of the subline where the parsed tokens are added to. */
455 int num_sublines;
456 struct wps_subline sublines[WPS_MAX_SUBLINES];
457
458 /* Total number of tokens in the WPS. During WPS parsing, this is
459 the index of the token being parsed. */
460 int num_tokens;
461 struct wps_token tokens[WPS_MAX_TOKENS];
462
463 char string_buffer[STRING_BUFFER_SIZE];
464 char *strings[WPS_MAX_STRINGS];
465 int num_strings;
466
467 bool wps_loaded;
468
469 /* tick the volume button was last pressed */
470 unsigned int button_time_volume;
471};
472
473/* initial setup of wps_data */
474void wps_data_init(struct wps_data *wps_data);
475
476/* to setup up the wps-data from a format-buffer (isfile = false)
477 from a (wps-)file (isfile = true)*/
478bool wps_data_load(struct wps_data *wps_data,
479 struct screen *display,
480 const char *buf,
481 bool isfile);
482
483/* Redraw statusbars if necessary */
484void gwps_draw_statusbars(void);
485
486/* Returns the index of the subline in the subline array
487 line - 0-based line number
488 subline - 0-based subline number within the line
489 */
490int wps_subline_index(struct wps_data *wps_data, int line, int subline);
491
492/* Returns the index of the first subline's token in the token array
493 line - 0-based line number
494 subline - 0-based subline number within the line
495 */
496int wps_first_token_index(struct wps_data *data, int line, int subline);
497
498/* Returns the index of the last subline's token in the token array.
499 line - 0-based line number
500 subline - 0-based subline number within the line
501 */
502int wps_last_token_index(struct wps_data *data, int line, int subline);
503
504/* wps_data end */
505
506/* wps_state
507 holds the data which belongs to the current played track,
508 the track which will be played afterwards, current path to the track
509 and some status infos */
510struct wps_state
511{
512 bool ff_rewind;
513 bool paused;
514 int ff_rewind_count;
515 bool wps_time_countup;
516 struct mp3entry* id3;
517 struct mp3entry* nid3;
518 bool do_full_update;
519};
520
521
522/* change the ff/rew-status
523 if ff_rew = true then we are in skipping mode
524 else we are in normal mode */
525/* void wps_state_update_ff_rew(bool ff_rew); Currently unused */
526
527/* change the tag-information of the current played track
528 and the following track */
529/* void wps_state_update_id3_nid3(struct mp3entry *id3, struct mp3entry *nid3); Currently unused */
530/* wps_state end*/
531
532/* gui_wps
533 defines a wps with its data, state,
534 and the screen on which the wps-content should be drawn */
535struct gui_wps
536{
537 struct screen *display;
538 struct wps_data *data;
539 struct wps_state *state;
540};
541
542/* gui_wps end */
543
544
545/* currently only on wps_state is needed */
546extern struct wps_state wps_state;
547extern struct gui_wps gui_wps[NB_SCREENS];
548
549/***** wps_tokens.c ******/
550
551const char *get_token_value(struct gui_wps *gwps,
552 struct wps_token *token,
553 char *buf, int buf_size,
554 int *intval);
555
556#endif
diff --git a/apps/gui/wps_engine/wps_parser.c b/apps/gui/wps_engine/wps_parser.c
new file mode 100644
index 0000000000..15acc1401d
--- /dev/null
+++ b/apps/gui/wps_engine/wps_parser.c
@@ -0,0 +1,1843 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2007 Nicolas Pennequin, Dan Everton, Matthias Mohr
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include <stdio.h>
23#include <string.h>
24#include <stdlib.h>
25#include "file.h"
26#include "misc.h"
27#include "plugin.h"
28
29#ifdef __PCTOOL__
30#ifdef WPSEDITOR
31#include "proxy.h"
32#include "sysfont.h"
33#else
34#include "checkwps.h"
35#include "audio.h"
36#define DEBUGF printf
37#endif /*WPSEDITOR*/
38#else
39#include "debug.h"
40#endif /*__PCTOOL__*/
41
42#include <ctype.h>
43#include <stdbool.h>
44#include "font.h"
45
46#include "wps_internals.h"
47#include "settings.h"
48#include "settings_list.h"
49
50#ifdef HAVE_LCD_BITMAP
51#include "bmp.h"
52#endif
53
54#include "backdrop.h"
55
56#define WPS_DEFAULTCFG WPS_DIR "/rockbox_default.wps"
57#define RWPS_DEFAULTCFG WPS_DIR "/rockbox_default.rwps"
58
59#define WPS_ERROR_INVALID_PARAM -1
60
61/* level of current conditional.
62 -1 means we're not in a conditional. */
63static int level = -1;
64
65/* index of the last WPS_TOKEN_CONDITIONAL_OPTION
66 or WPS_TOKEN_CONDITIONAL_START in current level */
67static int lastcond[WPS_MAX_COND_LEVEL];
68
69/* index of the WPS_TOKEN_CONDITIONAL in current level */
70static int condindex[WPS_MAX_COND_LEVEL];
71
72/* number of condtional options in current level */
73static int numoptions[WPS_MAX_COND_LEVEL];
74
75/* the current line in the file */
76static int line;
77
78#ifdef HAVE_LCD_BITMAP
79
80#if LCD_DEPTH > 1
81#define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS+1) /* WPS images + pbar bitmap + backdrop */
82#else
83#define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS) /* WPS images + pbar bitmap */
84#endif
85
86#define PROGRESSBAR_BMP MAX_IMAGES
87#define BACKDROP_BMP (MAX_BITMAPS-1)
88
89/* pointers to the bitmap filenames in the WPS source */
90static const char *bmp_names[MAX_BITMAPS];
91
92#endif /* HAVE_LCD_BITMAP */
93
94#if defined(DEBUG) || defined(SIMULATOR)
95/* debugging function */
96extern void print_debug_info(struct wps_data *data, int fail, int line);
97#endif
98
99static void wps_reset(struct wps_data *data);
100
101/* Function for parsing of details for a token. At the moment the
102 function is called, the token type has already been set. The
103 function must fill in the details and possibly add more tokens
104 to the token array. It should return the number of chars that
105 has been consumed.
106
107 wps_bufptr points to the char following the tag (i.e. where
108 details begin).
109 token is the pointer to the 'main' token being parsed
110 */
111typedef int (*wps_tag_parse_func)(const char *wps_bufptr,
112 struct wps_token *token, struct wps_data *wps_data);
113
114struct wps_tag {
115 enum wps_token_type type;
116 const char name[3];
117 unsigned char refresh_type;
118 const wps_tag_parse_func parse_func;
119};
120static int skip_end_of_line(const char *wps_bufptr);
121/* prototypes of all special parse functions : */
122static int parse_timeout(const char *wps_bufptr,
123 struct wps_token *token, struct wps_data *wps_data);
124static int parse_progressbar(const char *wps_bufptr,
125 struct wps_token *token, struct wps_data *wps_data);
126static int parse_dir_level(const char *wps_bufptr,
127 struct wps_token *token, struct wps_data *wps_data);
128static int parse_setting(const char *wps_bufptr,
129 struct wps_token *token, struct wps_data *wps_data);
130
131#ifdef HAVE_LCD_BITMAP
132static int parse_viewport_display(const char *wps_bufptr,
133 struct wps_token *token, struct wps_data *wps_data);
134static int parse_viewport(const char *wps_bufptr,
135 struct wps_token *token, struct wps_data *wps_data);
136static int parse_statusbar_enable(const char *wps_bufptr,
137 struct wps_token *token, struct wps_data *wps_data);
138static int parse_statusbar_disable(const char *wps_bufptr,
139 struct wps_token *token, struct wps_data *wps_data);
140static int parse_image_display(const char *wps_bufptr,
141 struct wps_token *token, struct wps_data *wps_data);
142static int parse_image_load(const char *wps_bufptr,
143 struct wps_token *token, struct wps_data *wps_data);
144#endif /*HAVE_LCD_BITMAP */
145#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
146static int parse_image_special(const char *wps_bufptr,
147 struct wps_token *token, struct wps_data *wps_data);
148#endif
149#ifdef HAVE_ALBUMART
150static int parse_albumart_load(const char *wps_bufptr,
151 struct wps_token *token, struct wps_data *wps_data);
152static int parse_albumart_conditional(const char *wps_bufptr,
153 struct wps_token *token, struct wps_data *wps_data);
154#endif /* HAVE_ALBUMART */
155#ifdef HAVE_TOUCHSCREEN
156static int parse_touchregion(const char *wps_bufptr,
157 struct wps_token *token, struct wps_data *wps_data);
158#else
159static int fulline_tag_not_supported(const char *wps_bufptr,
160 struct wps_token *token, struct wps_data *wps_data)
161{
162 (void)token; (void)wps_data;
163 return skip_end_of_line(wps_bufptr);
164}
165#define parse_touchregion fulline_tag_not_supported
166#endif
167#ifdef CONFIG_RTC
168#define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
169#else
170#define WPS_RTC_REFRESH WPS_REFRESH_STATIC
171#endif
172
173/* array of available tags - those with more characters have to go first
174 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
175static const struct wps_tag all_tags[] = {
176
177 { WPS_TOKEN_ALIGN_CENTER, "ac", 0, NULL },
178 { WPS_TOKEN_ALIGN_LEFT, "al", 0, NULL },
179 { WPS_TOKEN_ALIGN_RIGHT, "ar", 0, NULL },
180
181 { WPS_TOKEN_BATTERY_PERCENT, "bl", WPS_REFRESH_DYNAMIC, NULL },
182 { WPS_TOKEN_BATTERY_VOLTS, "bv", WPS_REFRESH_DYNAMIC, NULL },
183 { WPS_TOKEN_BATTERY_TIME, "bt", WPS_REFRESH_DYNAMIC, NULL },
184 { WPS_TOKEN_BATTERY_SLEEPTIME, "bs", WPS_REFRESH_DYNAMIC, NULL },
185#if CONFIG_CHARGING >= CHARGING_MONITOR
186 { WPS_TOKEN_BATTERY_CHARGING, "bc", WPS_REFRESH_DYNAMIC, NULL },
187#endif
188#if CONFIG_CHARGING
189 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED,"bp", WPS_REFRESH_DYNAMIC, NULL },
190#endif
191
192 { WPS_TOKEN_RTC_PRESENT , "cc", WPS_REFRESH_STATIC, NULL },
193 { WPS_TOKEN_RTC_DAY_OF_MONTH, "cd", WPS_RTC_REFRESH, NULL },
194 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,"ce", WPS_RTC_REFRESH, NULL },
195 { WPS_TOKEN_RTC_12HOUR_CFG, "cf", WPS_RTC_REFRESH, NULL },
196 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", WPS_RTC_REFRESH, NULL },
197 { WPS_TOKEN_RTC_HOUR_24, "ck", WPS_RTC_REFRESH, NULL },
198 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", WPS_RTC_REFRESH, NULL },
199 { WPS_TOKEN_RTC_HOUR_12, "cl", WPS_RTC_REFRESH, NULL },
200 { WPS_TOKEN_RTC_MONTH, "cm", WPS_RTC_REFRESH, NULL },
201 { WPS_TOKEN_RTC_MINUTE, "cM", WPS_RTC_REFRESH, NULL },
202 { WPS_TOKEN_RTC_SECOND, "cS", WPS_RTC_REFRESH, NULL },
203 { WPS_TOKEN_RTC_YEAR_2_DIGITS, "cy", WPS_RTC_REFRESH, NULL },
204 { WPS_TOKEN_RTC_YEAR_4_DIGITS, "cY", WPS_RTC_REFRESH, NULL },
205 { WPS_TOKEN_RTC_AM_PM_UPPER, "cP", WPS_RTC_REFRESH, NULL },
206 { WPS_TOKEN_RTC_AM_PM_LOWER, "cp", WPS_RTC_REFRESH, NULL },
207 { WPS_TOKEN_RTC_WEEKDAY_NAME, "ca", WPS_RTC_REFRESH, NULL },
208 { WPS_TOKEN_RTC_MONTH_NAME, "cb", WPS_RTC_REFRESH, NULL },
209 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", WPS_RTC_REFRESH, NULL },
210 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", WPS_RTC_REFRESH, NULL },
211
212 /* current file */
213 { WPS_TOKEN_FILE_BITRATE, "fb", WPS_REFRESH_STATIC, NULL },
214 { WPS_TOKEN_FILE_CODEC, "fc", WPS_REFRESH_STATIC, NULL },
215 { WPS_TOKEN_FILE_FREQUENCY, "ff", WPS_REFRESH_STATIC, NULL },
216 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "fk", WPS_REFRESH_STATIC, NULL },
217 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "fm", WPS_REFRESH_STATIC, NULL },
218 { WPS_TOKEN_FILE_NAME, "fn", WPS_REFRESH_STATIC, NULL },
219 { WPS_TOKEN_FILE_PATH, "fp", WPS_REFRESH_STATIC, NULL },
220 { WPS_TOKEN_FILE_SIZE, "fs", WPS_REFRESH_STATIC, NULL },
221 { WPS_TOKEN_FILE_VBR, "fv", WPS_REFRESH_STATIC, NULL },
222 { WPS_TOKEN_FILE_DIRECTORY, "d", WPS_REFRESH_STATIC,
223 parse_dir_level },
224
225 /* next file */
226 { WPS_TOKEN_FILE_BITRATE, "Fb", WPS_REFRESH_STATIC, NULL },
227 { WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_STATIC, NULL },
228 { WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_STATIC, NULL },
229 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_STATIC, NULL },
230 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_STATIC, NULL },
231 { WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_STATIC, NULL },
232 { WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_STATIC, NULL },
233 { WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_STATIC, NULL },
234 { WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_STATIC, NULL },
235 { WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_STATIC,
236 parse_dir_level },
237
238 /* current metadata */
239 { WPS_TOKEN_METADATA_ARTIST, "ia", WPS_REFRESH_STATIC, NULL },
240 { WPS_TOKEN_METADATA_COMPOSER, "ic", WPS_REFRESH_STATIC, NULL },
241 { WPS_TOKEN_METADATA_ALBUM, "id", WPS_REFRESH_STATIC, NULL },
242 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "iA", WPS_REFRESH_STATIC, NULL },
243 { WPS_TOKEN_METADATA_GROUPING, "iG", WPS_REFRESH_STATIC, NULL },
244 { WPS_TOKEN_METADATA_GENRE, "ig", WPS_REFRESH_STATIC, NULL },
245 { WPS_TOKEN_METADATA_DISC_NUMBER, "ik", WPS_REFRESH_STATIC, NULL },
246 { WPS_TOKEN_METADATA_TRACK_NUMBER, "in", WPS_REFRESH_STATIC, NULL },
247 { WPS_TOKEN_METADATA_TRACK_TITLE, "it", WPS_REFRESH_STATIC, NULL },
248 { WPS_TOKEN_METADATA_VERSION, "iv", WPS_REFRESH_STATIC, NULL },
249 { WPS_TOKEN_METADATA_YEAR, "iy", WPS_REFRESH_STATIC, NULL },
250 { WPS_TOKEN_METADATA_COMMENT, "iC", WPS_REFRESH_STATIC, NULL },
251
252 /* next metadata */
253 { WPS_TOKEN_METADATA_ARTIST, "Ia", WPS_REFRESH_STATIC, NULL },
254 { WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_STATIC, NULL },
255 { WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_STATIC, NULL },
256 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_STATIC, NULL },
257 { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_STATIC, NULL },
258 { WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_STATIC, NULL },
259 { WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_STATIC, NULL },
260 { WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_STATIC, NULL },
261 { WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_STATIC, NULL },
262 { WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_STATIC, NULL },
263 { WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_STATIC, NULL },
264 { WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_STATIC, NULL },
265
266#if (CONFIG_CODEC != MAS3507D)
267 { WPS_TOKEN_SOUND_PITCH, "Sp", WPS_REFRESH_DYNAMIC, NULL },
268#endif
269
270#if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
271 { WPS_TOKEN_VLED_HDD, "lh", WPS_REFRESH_DYNAMIC, NULL },
272#endif
273
274 { WPS_TOKEN_MAIN_HOLD, "mh", WPS_REFRESH_DYNAMIC, NULL },
275
276#ifdef HAS_REMOTE_BUTTON_HOLD
277 { WPS_TOKEN_REMOTE_HOLD, "mr", WPS_REFRESH_DYNAMIC, NULL },
278#else
279 { WPS_TOKEN_UNKNOWN, "mr", 0, NULL },
280#endif
281
282 { WPS_TOKEN_REPEAT_MODE, "mm", WPS_REFRESH_DYNAMIC, NULL },
283 { WPS_TOKEN_PLAYBACK_STATUS, "mp", WPS_REFRESH_DYNAMIC, NULL },
284 { WPS_TOKEN_BUTTON_VOLUME, "mv", WPS_REFRESH_DYNAMIC,
285 parse_timeout },
286
287#ifdef HAVE_LCD_BITMAP
288 { WPS_TOKEN_PEAKMETER, "pm", WPS_REFRESH_PEAK_METER, NULL },
289#else
290 { WPS_TOKEN_PLAYER_PROGRESSBAR, "pf",
291 WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar },
292#endif
293 { WPS_TOKEN_PROGRESSBAR, "pb", WPS_REFRESH_PLAYER_PROGRESS,
294 parse_progressbar },
295
296 { WPS_TOKEN_VOLUME, "pv", WPS_REFRESH_DYNAMIC, NULL },
297
298 { WPS_TOKEN_TRACK_ELAPSED_PERCENT, "px", WPS_REFRESH_DYNAMIC, NULL },
299 { WPS_TOKEN_TRACK_TIME_ELAPSED, "pc", WPS_REFRESH_DYNAMIC, NULL },
300 { WPS_TOKEN_TRACK_TIME_REMAINING, "pr", WPS_REFRESH_DYNAMIC, NULL },
301 { WPS_TOKEN_TRACK_LENGTH, "pt", WPS_REFRESH_STATIC, NULL },
302
303 { WPS_TOKEN_PLAYLIST_POSITION, "pp", WPS_REFRESH_STATIC, NULL },
304 { WPS_TOKEN_PLAYLIST_ENTRIES, "pe", WPS_REFRESH_STATIC, NULL },
305 { WPS_TOKEN_PLAYLIST_NAME, "pn", WPS_REFRESH_STATIC, NULL },
306 { WPS_TOKEN_PLAYLIST_SHUFFLE, "ps", WPS_REFRESH_DYNAMIC, NULL },
307
308#ifdef HAVE_TAGCACHE
309 { WPS_TOKEN_DATABASE_PLAYCOUNT, "rp", WPS_REFRESH_DYNAMIC, NULL },
310 { WPS_TOKEN_DATABASE_RATING, "rr", WPS_REFRESH_DYNAMIC, NULL },
311 { WPS_TOKEN_DATABASE_AUTOSCORE, "ra", WPS_REFRESH_DYNAMIC, NULL },
312#endif
313
314#if CONFIG_CODEC == SWCODEC
315 { WPS_TOKEN_REPLAYGAIN, "rg", WPS_REFRESH_STATIC, NULL },
316 { WPS_TOKEN_CROSSFADE, "xf", WPS_REFRESH_DYNAMIC, NULL },
317#endif
318
319 { WPS_NO_TOKEN, "s", WPS_REFRESH_SCROLL, NULL },
320 { WPS_TOKEN_SUBLINE_TIMEOUT, "t", 0, parse_timeout },
321
322#ifdef HAVE_LCD_BITMAP
323 { WPS_NO_TOKEN, "we", 0, parse_statusbar_enable },
324 { WPS_NO_TOKEN, "wd", 0, parse_statusbar_disable },
325
326 { WPS_NO_TOKEN, "xl", 0, parse_image_load },
327
328 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, "xd", WPS_REFRESH_STATIC,
329 parse_image_display },
330
331 { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load },
332#ifdef HAVE_ALBUMART
333 { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load },
334 { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_STATIC,
335 parse_albumart_conditional },
336#endif
337
338 { WPS_VIEWPORT_ENABLE, "Vd", WPS_REFRESH_DYNAMIC,
339 parse_viewport_display },
340 { WPS_NO_TOKEN, "V", 0, parse_viewport },
341
342#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
343 { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special },
344#endif
345#endif
346
347 { WPS_TOKEN_SETTING, "St", WPS_REFRESH_DYNAMIC, parse_setting },
348
349 { WPS_TOKEN_LASTTOUCH, "Tl", WPS_REFRESH_DYNAMIC, parse_timeout },
350 { WPS_NO_TOKEN, "T", 0, parse_touchregion },
351
352 { WPS_TOKEN_UNKNOWN, "", 0, NULL }
353 /* the array MUST end with an empty string (first char is \0) */
354};
355
356/* Returns the number of chars that should be skipped to jump
357 immediately after the first eol, i.e. to the start of the next line */
358static int skip_end_of_line(const char *wps_bufptr)
359{
360 line++;
361 int skip = 0;
362 while(*(wps_bufptr + skip) != '\n')
363 skip++;
364 return ++skip;
365}
366
367/* Starts a new subline in the current line during parsing */
368static void wps_start_new_subline(struct wps_data *data)
369{
370 data->num_sublines++;
371 data->sublines[data->num_sublines].first_token_idx = data->num_tokens;
372 data->lines[data->num_lines].num_sublines++;
373}
374
375#ifdef HAVE_LCD_BITMAP
376
377static int parse_statusbar_enable(const char *wps_bufptr,
378 struct wps_token *token,
379 struct wps_data *wps_data)
380{
381 (void)token; /* Kill warnings */
382 wps_data->wps_sb_tag = true;
383 wps_data->show_sb_on_wps = true;
384 if (wps_data->viewports[0].vp.y == 0)
385 {
386 wps_data->viewports[0].vp.y = STATUSBAR_HEIGHT;
387 wps_data->viewports[0].vp.height -= STATUSBAR_HEIGHT;
388 }
389 return skip_end_of_line(wps_bufptr);
390}
391
392static int parse_statusbar_disable(const char *wps_bufptr,
393 struct wps_token *token,
394 struct wps_data *wps_data)
395{
396 (void)token; /* Kill warnings */
397 wps_data->wps_sb_tag = true;
398 wps_data->show_sb_on_wps = false;
399 if (wps_data->viewports[0].vp.y == STATUSBAR_HEIGHT)
400 {
401 wps_data->viewports[0].vp.y = 0;
402 wps_data->viewports[0].vp.height += STATUSBAR_HEIGHT;
403 }
404 return skip_end_of_line(wps_bufptr);
405}
406
407static bool load_bitmap(struct wps_data *wps_data,
408 char* filename,
409 struct bitmap *bm)
410{
411 int format;
412#ifdef HAVE_REMOTE_LCD
413 if (wps_data->remote_wps)
414 format = FORMAT_ANY|FORMAT_REMOTE;
415 else
416#endif
417 format = FORMAT_ANY|FORMAT_TRANSPARENT;
418
419 int ret = read_bmp_file(filename, bm,
420 wps_data->img_buf_free,
421 format,NULL);
422
423 if (ret > 0)
424 {
425#if LCD_DEPTH == 16
426 if (ret % 2) ret++;
427 /* Always consume an even number of bytes */
428#endif
429 wps_data->img_buf_ptr += ret;
430 wps_data->img_buf_free -= ret;
431
432 return true;
433 }
434 else
435 return false;
436}
437
438static int get_image_id(int c)
439{
440 if(c >= 'a' && c <= 'z')
441 return c - 'a';
442 else if(c >= 'A' && c <= 'Z')
443 return c - 'A' + 26;
444 else
445 return -1;
446}
447
448static char *get_image_filename(const char *start, const char* bmpdir,
449 char *buf, int buf_size)
450{
451 const char *end = strchr(start, '|');
452
453 if ( !end || (end - start) >= (buf_size - (int)ROCKBOX_DIR_LEN - 2) )
454 {
455 buf = "\0";
456 return NULL;
457 }
458
459 int bmpdirlen = strlen(bmpdir);
460
461 strcpy(buf, bmpdir);
462 buf[bmpdirlen] = '/';
463 memcpy( &buf[bmpdirlen + 1], start, end - start);
464 buf[bmpdirlen + 1 + end - start] = 0;
465
466 return buf;
467}
468
469static int parse_image_display(const char *wps_bufptr,
470 struct wps_token *token,
471 struct wps_data *wps_data)
472{
473 (void)wps_data;
474 int n = get_image_id(wps_bufptr[0]);
475 int subimage;
476
477 if (n == -1)
478 {
479 /* invalid picture display tag */
480 return WPS_ERROR_INVALID_PARAM;
481 }
482
483 if ((subimage = get_image_id(wps_bufptr[1])) != -1)
484 {
485 /* Sanity check */
486 if (subimage >= wps_data->img[n].num_subimages)
487 return WPS_ERROR_INVALID_PARAM;
488
489 /* Store sub-image number to display in high bits */
490 token->value.i = n | (subimage << 8);
491 return 2; /* We have consumed 2 bytes */
492 } else {
493 token->value.i = n;
494 return 1; /* We have consumed 1 byte */
495 }
496}
497
498static int parse_image_load(const char *wps_bufptr,
499 struct wps_token *token,
500 struct wps_data *wps_data)
501{
502 int n;
503 const char *ptr = wps_bufptr;
504 const char *pos;
505 const char* filename;
506 const char* id;
507 const char *newline;
508 int x,y;
509
510 /* format: %x|n|filename.bmp|x|y|
511 or %xl|n|filename.bmp|x|y|
512 or %xl|n|filename.bmp|x|y|num_subimages|
513 */
514
515 if (*ptr != '|')
516 return WPS_ERROR_INVALID_PARAM;
517
518 ptr++;
519
520 if (!(ptr = parse_list("ssdd", NULL, '|', ptr, &id, &filename, &x, &y)))
521 return WPS_ERROR_INVALID_PARAM;
522
523 /* Check there is a terminating | */
524 if (*ptr != '|')
525 return WPS_ERROR_INVALID_PARAM;
526
527 /* get the image ID */
528 n = get_image_id(*id);
529
530 /* check the image number and load state */
531 if(n < 0 || n >= MAX_IMAGES || wps_data->img[n].loaded)
532 {
533 /* Invalid image ID */
534 return WPS_ERROR_INVALID_PARAM;
535 }
536
537 /* save a pointer to the filename */
538 bmp_names[n] = filename;
539
540 wps_data->img[n].x = x;
541 wps_data->img[n].y = y;
542
543 /* save current viewport */
544 wps_data->img[n].vp = &wps_data->viewports[wps_data->num_viewports].vp;
545
546 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
547 {
548 wps_data->img[n].always_display = true;
549 }
550 else
551 {
552 /* Parse the (optional) number of sub-images */
553 ptr++;
554 newline = strchr(ptr, '\n');
555 pos = strchr(ptr, '|');
556 if (pos && pos < newline)
557 wps_data->img[n].num_subimages = atoi(ptr);
558
559 if (wps_data->img[n].num_subimages <= 0)
560 return WPS_ERROR_INVALID_PARAM;
561 }
562
563 /* Skip the rest of the line */
564 return skip_end_of_line(wps_bufptr);
565}
566
567static int parse_viewport_display(const char *wps_bufptr,
568 struct wps_token *token,
569 struct wps_data *wps_data)
570{
571 (void)wps_data;
572 char letter = wps_bufptr[0];
573
574 if (letter < 'a' || letter > 'z')
575 {
576 /* invalid viewport tag */
577 return WPS_ERROR_INVALID_PARAM;
578 }
579 token->value.i = letter;
580 return 1;
581}
582
583static int parse_viewport(const char *wps_bufptr,
584 struct wps_token *token,
585 struct wps_data *wps_data)
586{
587 (void)token; /* Kill warnings */
588 const char *ptr = wps_bufptr;
589 struct viewport* vp;
590 int depth;
591 uint32_t set = 0;
592 enum {
593 PL_X = 0,
594 PL_Y,
595 PL_WIDTH,
596 PL_HEIGHT,
597 PL_FONT,
598 PL_FG,
599 PL_BG,
600 };
601 int lcd_width = LCD_WIDTH, lcd_height = LCD_HEIGHT;
602#ifdef HAVE_REMOTE_LCD
603 if (wps_data->remote_wps)
604 {
605 lcd_width = LCD_REMOTE_WIDTH;
606 lcd_height = LCD_REMOTE_HEIGHT;
607 }
608#endif
609
610 if (wps_data->num_viewports >= WPS_MAX_VIEWPORTS)
611 return WPS_ERROR_INVALID_PARAM;
612
613 wps_data->num_viewports++;
614 /* check for the optional letter to signify its a hideable viewport */
615 /* %Vl|<label>|<rest of tags>| */
616 wps_data->viewports[wps_data->num_viewports].hidden_flags = 0;
617
618 if (*ptr == 'l')
619 {
620 if (*(ptr+1) == '|')
621 {
622 char label = *(ptr+2);
623 if (label >= 'a' && label <= 'z')
624 {
625 wps_data->viewports[wps_data->num_viewports].hidden_flags = VP_DRAW_HIDEABLE;
626 wps_data->viewports[wps_data->num_viewports].label = label;
627 }
628 else
629 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
630 ptr += 3;
631 }
632 }
633 if (*ptr != '|')
634 return WPS_ERROR_INVALID_PARAM;
635
636 ptr++;
637 vp = &wps_data->viewports[wps_data->num_viewports].vp;
638 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
639
640 /* Set the defaults for fields not user-specified */
641 vp->drawmode = DRMODE_SOLID;
642
643 /* Work out the depth of this display */
644#ifdef HAVE_REMOTE_LCD
645 depth = (wps_data->remote_wps ? LCD_REMOTE_DEPTH : LCD_DEPTH);
646#else
647 depth = LCD_DEPTH;
648#endif
649
650#ifdef HAVE_LCD_COLOR
651 if (depth == 16)
652 {
653 if (!(ptr = parse_list("dddddcc", &set, '|', ptr, &vp->x, &vp->y, &vp->width,
654 &vp->height, &vp->font, &vp->fg_pattern,&vp->bg_pattern)))
655 return WPS_ERROR_INVALID_PARAM;
656 }
657 else
658#endif
659#if (LCD_DEPTH == 2) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH == 2)
660 if (depth == 2) {
661 /* Default to black on white */
662 vp->fg_pattern = 0;
663 vp->bg_pattern = 3;
664 if (!(ptr = parse_list("dddddgg", &set, '|', ptr, &vp->x, &vp->y, &vp->width,
665 &vp->height, &vp->font, &vp->fg_pattern, &vp->bg_pattern)))
666 return WPS_ERROR_INVALID_PARAM;
667 }
668 else
669#endif
670#if (LCD_DEPTH == 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH == 1)
671 if (depth == 1)
672 {
673 if (!(ptr = parse_list("ddddd", &set, '|', ptr, &vp->x, &vp->y,
674 &vp->width, &vp->height, &vp->font)))
675 return WPS_ERROR_INVALID_PARAM;
676 }
677 else
678#endif
679 {}
680
681 /* Check for trailing | */
682 if (*ptr != '|')
683 return WPS_ERROR_INVALID_PARAM;
684
685 if (!LIST_VALUE_PARSED(set, PL_X) || !LIST_VALUE_PARSED(set, PL_Y))
686 return WPS_ERROR_INVALID_PARAM;
687
688 /* fix defaults */
689 if (!LIST_VALUE_PARSED(set, PL_WIDTH))
690 vp->width = lcd_width - vp->x;
691 if (!LIST_VALUE_PARSED(set, PL_HEIGHT))
692 vp->height = lcd_height - vp->y;
693
694 /* Default to using the user font if the font was an invalid number */
695 if (!LIST_VALUE_PARSED(set, PL_FONT) ||
696 ((vp->font != FONT_SYSFIXED) && (vp->font != FONT_UI)))
697 vp->font = FONT_UI;
698
699 /* Validate the viewport dimensions - we know that the numbers are
700 non-negative integers */
701 if ((vp->x >= lcd_width) ||
702 ((vp->x + vp->width) > lcd_width) ||
703 (vp->y >= lcd_height) ||
704 ((vp->y + vp->height) > lcd_height))
705 {
706 return WPS_ERROR_INVALID_PARAM;
707 }
708
709#ifdef HAVE_LCD_COLOR
710 if (depth == 16)
711 {
712 if (!LIST_VALUE_PARSED(set, PL_FG))
713 vp->fg_pattern = global_settings.fg_color;
714 if (!LIST_VALUE_PARSED(set, PL_BG))
715 vp->bg_pattern = global_settings.bg_color;
716 }
717#endif
718
719 wps_data->viewports[wps_data->num_viewports-1].last_line = wps_data->num_lines - 1;
720
721 wps_data->viewports[wps_data->num_viewports].first_line = wps_data->num_lines;
722
723 if (wps_data->num_sublines < WPS_MAX_SUBLINES)
724 {
725 wps_data->lines[wps_data->num_lines].first_subline_idx =
726 wps_data->num_sublines;
727
728 wps_data->sublines[wps_data->num_sublines].first_token_idx =
729 wps_data->num_tokens;
730 }
731
732 /* Skip the rest of the line */
733 return skip_end_of_line(wps_bufptr);
734}
735
736#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
737static int parse_image_special(const char *wps_bufptr,
738 struct wps_token *token,
739 struct wps_data *wps_data)
740{
741 (void)wps_data; /* kill warning */
742 (void)token;
743 const char *pos = NULL;
744 const char *newline;
745
746 pos = strchr(wps_bufptr + 1, '|');
747 newline = strchr(wps_bufptr, '\n');
748
749 if (pos > newline)
750 return WPS_ERROR_INVALID_PARAM;
751#if LCD_DEPTH > 1
752 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
753 {
754 /* format: %X|filename.bmp| */
755 bmp_names[BACKDROP_BMP] = wps_bufptr + 1;
756 }
757#endif
758
759 /* Skip the rest of the line */
760 return skip_end_of_line(wps_bufptr);
761}
762#endif
763
764#endif /* HAVE_LCD_BITMAP */
765
766static int parse_setting(const char *wps_bufptr,
767 struct wps_token *token,
768 struct wps_data *wps_data)
769{
770 (void)wps_data;
771 const char *ptr = wps_bufptr;
772 const char *end;
773 int i;
774
775 /* Find the setting's cfg_name */
776 if (*ptr != '|')
777 return WPS_ERROR_INVALID_PARAM;
778 ptr++;
779 end = strchr(ptr,'|');
780 if (!end)
781 return WPS_ERROR_INVALID_PARAM;
782
783 /* Find the setting */
784 for (i=0; i<nb_settings; i++)
785 if (settings[i].cfg_name &&
786 !strncmp(settings[i].cfg_name,ptr,end-ptr) &&
787 /* prevent matches on cfg_name prefixes */
788 strlen(settings[i].cfg_name)==(size_t)(end-ptr))
789 break;
790 if (i == nb_settings)
791 return WPS_ERROR_INVALID_PARAM;
792
793 /* Store the setting number */
794 token->value.i = i;
795
796 /* Skip the rest of the line */
797 return end-ptr+2;
798}
799
800
801static int parse_dir_level(const char *wps_bufptr,
802 struct wps_token *token,
803 struct wps_data *wps_data)
804{
805 char val[] = { *wps_bufptr, '\0' };
806 token->value.i = atoi(val);
807 (void)wps_data; /* Kill warnings */
808 return 1;
809}
810
811static int parse_timeout(const char *wps_bufptr,
812 struct wps_token *token,
813 struct wps_data *wps_data)
814{
815 int skip = 0;
816 int val = 0;
817 bool have_point = false;
818 bool have_tenth = false;
819
820 (void)wps_data; /* Kill the warning */
821
822 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
823 {
824 if (*wps_bufptr != '.')
825 {
826 val *= 10;
827 val += *wps_bufptr - '0';
828 if (have_point)
829 {
830 have_tenth = true;
831 wps_bufptr++;
832 skip++;
833 break;
834 }
835 }
836 else
837 have_point = true;
838
839 wps_bufptr++;
840 skip++;
841 }
842
843 if (have_tenth == false)
844 val *= 10;
845
846 if (val == 0 && skip == 0)
847 {
848 /* decide what to do if no value was specified */
849 switch (token->type)
850 {
851 case WPS_TOKEN_SUBLINE_TIMEOUT:
852 return -1;
853 case WPS_TOKEN_BUTTON_VOLUME:
854 val = 10;
855 break;
856 }
857 }
858 token->value.i = val;
859
860 return skip;
861}
862
863static int parse_progressbar(const char *wps_bufptr,
864 struct wps_token *token,
865 struct wps_data *wps_data)
866{
867 (void)token; /* Kill warnings */
868 /* %pb or %pb|filename|x|y|width|height|
869 using - for any of the params uses "sane" values */
870#ifdef HAVE_LCD_BITMAP
871 enum {
872 PB_FILENAME = 0,
873 PB_X,
874 PB_Y,
875 PB_WIDTH,
876 PB_HEIGHT
877 };
878 const char *filename;
879 int x, y, height, width;
880 uint32_t set = 0;
881 const char *ptr = wps_bufptr;
882 struct progressbar *pb;
883 struct viewport *vp = &wps_data->viewports[wps_data->num_viewports].vp;
884#ifndef __PCTOOL__
885 int font_height = font_get(vp->font)->height;
886#else
887 int font_height = 8;
888#endif
889 int line_num = wps_data->num_lines -
890 wps_data->viewports[wps_data->num_viewports].first_line;
891
892 if (wps_data->progressbar_count >= MAX_PROGRESSBARS)
893 return WPS_ERROR_INVALID_PARAM;
894
895 pb = &wps_data->progressbar[wps_data->progressbar_count];
896 pb->have_bitmap_pb = false;
897
898 if (*wps_bufptr != '|') /* regular old style */
899 {
900 pb->x = 0;
901 pb->width = vp->width;
902 pb->height = SYSFONT_HEIGHT-2;
903 pb->y = -line_num - 1; /* Will be computed during the rendering */
904
905 wps_data->viewports[wps_data->num_viewports].pb = pb;
906 wps_data->progressbar_count++;
907 return 0;
908 }
909 ptr = wps_bufptr + 1;
910
911 if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
912 &x, &y, &width, &height)))
913 return WPS_ERROR_INVALID_PARAM;
914
915 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
916 bmp_names[PROGRESSBAR_BMP+wps_data->progressbar_count] = filename;
917
918 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
919 pb->x = x;
920 else
921 pb->x = vp->x;
922
923 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
924 {
925 /* A zero width causes a divide-by-zero error later, so reject it */
926 if (width == 0)
927 return WPS_ERROR_INVALID_PARAM;
928
929 pb->width = width;
930 }
931 else
932 pb->width = vp->width - pb->x;
933
934 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
935 {
936 /* A zero height makes no sense - reject it */
937 if (height == 0)
938 return WPS_ERROR_INVALID_PARAM;
939
940 pb->height = height;
941 }
942 else
943 pb->height = font_height;
944
945 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
946 pb->y = y;
947 else
948 pb->y = -line_num - 1; /* Will be computed during the rendering */
949
950 wps_data->viewports[wps_data->num_viewports].pb = pb;
951 wps_data->progressbar_count++;
952
953 /* Skip the rest of the line */
954 return skip_end_of_line(wps_bufptr)-1;
955#else
956
957 if (*(wps_bufptr-1) == 'f')
958 wps_data->full_line_progressbar = true;
959 else
960 wps_data->full_line_progressbar = false;
961
962 return 0;
963
964#endif
965}
966
967#ifdef HAVE_ALBUMART
968static int parse_albumart_load(const char *wps_bufptr,
969 struct wps_token *token,
970 struct wps_data *wps_data)
971{
972 const char *_pos, *newline;
973 bool parsing;
974 (void)token; /* silence warning */
975
976 /* reset albumart info in wps */
977 wps_data->albumart_max_width = -1;
978 wps_data->albumart_max_height = -1;
979 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
980 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
981
982 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
983
984 newline = strchr(wps_bufptr, '\n');
985
986 /* initial validation and parsing of x and y components */
987 if (*wps_bufptr != '|')
988 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
989
990 _pos = wps_bufptr + 1;
991 if (!isdigit(*_pos))
992 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|@ */
993 wps_data->albumart_x = atoi(_pos);
994
995 _pos = strchr(_pos, '|');
996 if (!_pos || _pos > newline || !isdigit(*(++_pos)))
997 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|7\n or %Cl|7|@ */
998
999 wps_data->albumart_y = atoi(_pos);
1000
1001 _pos = strchr(_pos, '|');
1002 if (!_pos || _pos > newline)
1003 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after y coordinate
1004 e.g. %Cl|7|59\n */
1005
1006 /* parsing width field */
1007 parsing = true;
1008 while (parsing)
1009 {
1010 /* apply each modifier in turn */
1011 ++_pos;
1012 switch (*_pos)
1013 {
1014 case 'l':
1015 case 'L':
1016 case '+':
1017 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_LEFT;
1018 break;
1019 case 'c':
1020 case 'C':
1021 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER;
1022 break;
1023 case 'r':
1024 case 'R':
1025 case '-':
1026 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_RIGHT;
1027 break;
1028 case 'd':
1029 case 'D':
1030 case 'i':
1031 case 'I':
1032 case 's':
1033 case 'S':
1034 /* simply ignored */
1035 break;
1036 default:
1037 parsing = false;
1038 break;
1039 }
1040 }
1041 /* extract max width data */
1042 if (*_pos != '|')
1043 {
1044 if (!isdigit(*_pos)) /* malformed token: e.g. %Cl|7|59|# */
1045 return WPS_ERROR_INVALID_PARAM;
1046
1047 wps_data->albumart_max_width = atoi(_pos);
1048
1049 _pos = strchr(_pos, '|');
1050 if (!_pos || _pos > newline)
1051 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after width field
1052 e.g. %Cl|7|59|200\n */
1053 }
1054
1055 /* parsing height field */
1056 parsing = true;
1057 while (parsing)
1058 {
1059 /* apply each modifier in turn */
1060 ++_pos;
1061 switch (*_pos)
1062 {
1063 case 't':
1064 case 'T':
1065 case '-':
1066 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_TOP;
1067 break;
1068 case 'c':
1069 case 'C':
1070 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER;
1071 break;
1072 case 'b':
1073 case 'B':
1074 case '+':
1075 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1076 break;
1077 case 'd':
1078 case 'D':
1079 case 'i':
1080 case 'I':
1081 case 's':
1082 case 'S':
1083 /* simply ignored */
1084 break;
1085 default:
1086 parsing = false;
1087 break;
1088 }
1089 }
1090 /* extract max height data */
1091 if (*_pos != '|')
1092 {
1093 if (!isdigit(*_pos))
1094 return WPS_ERROR_INVALID_PARAM; /* malformed token e.g. %Cl|7|59|200|@ */
1095
1096 wps_data->albumart_max_height = atoi(_pos);
1097
1098 _pos = strchr(_pos, '|');
1099 if (!_pos || _pos > newline)
1100 return WPS_ERROR_INVALID_PARAM; /* malformed token: no closing |
1101 e.g. %Cl|7|59|200|200\n */
1102 }
1103
1104 /* if we got here, we parsed everything ok .. ! */
1105 if (wps_data->albumart_max_width < 0)
1106 wps_data->albumart_max_width = 0;
1107 else if (wps_data->albumart_max_width > LCD_WIDTH)
1108 wps_data->albumart_max_width = LCD_WIDTH;
1109
1110 if (wps_data->albumart_max_height < 0)
1111 wps_data->albumart_max_height = 0;
1112 else if (wps_data->albumart_max_height > LCD_HEIGHT)
1113 wps_data->albumart_max_height = LCD_HEIGHT;
1114
1115 wps_data->wps_uses_albumart = WPS_ALBUMART_LOAD;
1116
1117 /* Skip the rest of the line */
1118 return skip_end_of_line(wps_bufptr);
1119}
1120
1121static int parse_albumart_conditional(const char *wps_bufptr,
1122 struct wps_token *token,
1123 struct wps_data *wps_data)
1124{
1125 struct wps_token *prevtoken = token;
1126 --prevtoken;
1127 if (wps_data->num_tokens >= 1 && prevtoken->type == WPS_TOKEN_CONDITIONAL)
1128 {
1129 /* This %C is part of a %?C construct.
1130 It's either %?C<blah> or %?Cn<blah> */
1131 token->type = WPS_TOKEN_ALBUMART_FOUND;
1132 if (*wps_bufptr == 'n' && *(wps_bufptr + 1) == '<')
1133 {
1134 token->next = true;
1135 return 1;
1136 }
1137 else if (*wps_bufptr == '<')
1138 {
1139 return 0;
1140 }
1141 else
1142 {
1143 token->type = WPS_NO_TOKEN;
1144 return 0;
1145 }
1146 }
1147 else
1148 {
1149 /* This %C tag is in a conditional construct. */
1150 wps_data->albumart_cond_index = condindex[level];
1151 return 0;
1152 }
1153};
1154#endif /* HAVE_ALBUMART */
1155
1156#ifdef HAVE_TOUCHSCREEN
1157
1158struct touchaction {char* s; int action;};
1159static struct touchaction touchactions[] = {
1160 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1161 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1162 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1163 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1164 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1165 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1166 {"playlist", ACTION_WPS_VIEW_PLAYLIST },
1167};
1168static int parse_touchregion(const char *wps_bufptr,
1169 struct wps_token *token, struct wps_data *wps_data)
1170{
1171 (void)token;
1172 unsigned i, imax;
1173 struct touchregion *region;
1174 const char *ptr = wps_bufptr;
1175 const char *action;
1176 int x,y,w,h;
1177
1178 /* format: %T|x|y|width|height|action|
1179 * if action starts with & the area must be held to happen
1180 * action is one of:
1181 * play - play/pause playback
1182 * stop - stop playback, exit the wps
1183 * prev - prev track
1184 * next - next track
1185 * ffwd - seek forward
1186 * rwd - seek backwards
1187 * menu - go back to the main menu
1188 * browse - go back to the file/db browser
1189 * shuffle - toggle shuffle mode
1190 * repmode - cycle the repeat mode
1191 * quickscreen - go into the quickscreen
1192 * contextmenu - open the context menu
1193 */
1194
1195
1196 if ((wps_data->touchregion_count +1 >= MAX_TOUCHREGIONS) || (*ptr != '|'))
1197 return WPS_ERROR_INVALID_PARAM;
1198 ptr++;
1199
1200 if (!(ptr = parse_list("dddds", NULL, '|', ptr, &x, &y, &w, &h, &action)))
1201 return WPS_ERROR_INVALID_PARAM;
1202
1203 /* Check there is a terminating | */
1204 if (*ptr != '|')
1205 return WPS_ERROR_INVALID_PARAM;
1206
1207 /* should probably do some bounds checking here with the viewport... but later */
1208 region = &wps_data->touchregion[wps_data->touchregion_count];
1209 region->action = ACTION_NONE;
1210 region->x = x;
1211 region->y = y;
1212 region->width = w;
1213 region->height = h;
1214 region->wvp = &wps_data->viewports[wps_data->num_viewports];
1215 i = 0;
1216 if (*action == '&')
1217 {
1218 action++;
1219 region->repeat = true;
1220 }
1221 else
1222 region->repeat = false;
1223
1224 imax = ARRAYLEN(touchactions);
1225 while ((region->action == ACTION_NONE) &&
1226 (i < imax))
1227 {
1228 /* try to match with one of our touchregion screens */
1229 int len = strlen(touchactions[i].s);
1230 if (!strncmp(touchactions[i].s, action, len)
1231 && *(action+len) == '|')
1232 region->action = touchactions[i].action;
1233 i++;
1234 }
1235 if (region->action == ACTION_NONE)
1236 return WPS_ERROR_INVALID_PARAM;
1237 wps_data->touchregion_count++;
1238 return skip_end_of_line(wps_bufptr);
1239}
1240#endif
1241
1242/* Parse a generic token from the given string. Return the length read */
1243static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1244{
1245 int skip = 0, taglen = 0, ret;
1246 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1247 const struct wps_tag *tag;
1248
1249 switch(*wps_bufptr)
1250 {
1251
1252 case '%':
1253 case '<':
1254 case '|':
1255 case '>':
1256 case ';':
1257 case '#':
1258 /* escaped characters */
1259 token->type = WPS_TOKEN_CHARACTER;
1260 token->value.c = *wps_bufptr;
1261 taglen = 1;
1262 wps_data->num_tokens++;
1263 break;
1264
1265 case '?':
1266 /* conditional tag */
1267 token->type = WPS_TOKEN_CONDITIONAL;
1268 level++;
1269 condindex[level] = wps_data->num_tokens;
1270 numoptions[level] = 1;
1271 wps_data->num_tokens++;
1272 ret = parse_token(wps_bufptr + 1, wps_data);
1273 if (ret < 0) return ret;
1274 taglen = 1 + ret;
1275 break;
1276
1277 default:
1278 /* find what tag we have */
1279 for (tag = all_tags;
1280 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1281 tag++) ;
1282
1283 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1284 token->type = tag->type;
1285 wps_data->sublines[wps_data->num_sublines].line_type |=
1286 tag->refresh_type;
1287
1288 /* if the tag has a special parsing function, we call it */
1289 if (tag->parse_func)
1290 {
1291 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1292 if (ret < 0) return ret;
1293 skip += ret;
1294 }
1295
1296 /* Some tags we don't want to save as tokens */
1297 if (tag->type == WPS_NO_TOKEN)
1298 break;
1299
1300 /* tags that start with 'F', 'I' or 'D' are for the next file */
1301 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1302 *(tag->name) == 'D')
1303 token->next = true;
1304
1305 wps_data->num_tokens++;
1306 break;
1307 }
1308
1309 skip += taglen;
1310 return skip;
1311}
1312
1313/* Parses the WPS.
1314 data is the pointer to the structure where the parsed WPS should be stored.
1315 It is initialised.
1316 wps_bufptr points to the string containing the WPS tags */
1317static bool wps_parse(struct wps_data *data, const char *wps_bufptr)
1318{
1319 if (!data || !wps_bufptr || !*wps_bufptr)
1320 return false;
1321
1322 char *stringbuf = data->string_buffer;
1323 int stringbuf_used = 0;
1324 enum wps_parse_error fail = PARSE_OK;
1325 int ret;
1326 line = 1;
1327 level = -1;
1328
1329 while(*wps_bufptr && !fail && data->num_tokens < WPS_MAX_TOKENS - 1
1330 && data->num_viewports < WPS_MAX_VIEWPORTS
1331 && data->num_lines < WPS_MAX_LINES)
1332 {
1333 switch(*wps_bufptr++)
1334 {
1335
1336 /* Regular tag */
1337 case '%':
1338 if ((ret = parse_token(wps_bufptr, data)) < 0)
1339 {
1340 fail = PARSE_FAIL_COND_INVALID_PARAM;
1341 break;
1342 }
1343 else if (level >= WPS_MAX_COND_LEVEL - 1)
1344 {
1345 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1346 break;
1347 }
1348 wps_bufptr += ret;
1349 break;
1350
1351 /* Alternating sublines separator */
1352 case ';':
1353 if (level >= 0) /* there are unclosed conditionals */
1354 {
1355 fail = PARSE_FAIL_UNCLOSED_COND;
1356 break;
1357 }
1358
1359 if (data->num_sublines+1 < WPS_MAX_SUBLINES)
1360 wps_start_new_subline(data);
1361 else
1362 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1363
1364 break;
1365
1366 /* Conditional list start */
1367 case '<':
1368 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1369 {
1370 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1371 break;
1372 }
1373
1374 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1375 lastcond[level] = data->num_tokens++;
1376 break;
1377
1378 /* Conditional list end */
1379 case '>':
1380 if (level < 0) /* not in a conditional, invalid char */
1381 {
1382 fail = PARSE_FAIL_INVALID_CHAR;
1383 break;
1384 }
1385
1386 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1387 if (lastcond[level])
1388 data->tokens[lastcond[level]].value.i = data->num_tokens;
1389 else
1390 {
1391 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1392 break;
1393 }
1394
1395 lastcond[level] = 0;
1396 data->num_tokens++;
1397 data->tokens[condindex[level]].value.i = numoptions[level];
1398 level--;
1399 break;
1400
1401 /* Conditional list option */
1402 case '|':
1403 if (level < 0) /* not in a conditional, invalid char */
1404 {
1405 fail = PARSE_FAIL_INVALID_CHAR;
1406 break;
1407 }
1408
1409 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1410 if (lastcond[level])
1411 data->tokens[lastcond[level]].value.i = data->num_tokens;
1412 else
1413 {
1414 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1415 break;
1416 }
1417
1418 lastcond[level] = data->num_tokens;
1419 numoptions[level]++;
1420 data->num_tokens++;
1421 break;
1422
1423 /* Comment */
1424 case '#':
1425 if (level >= 0) /* there are unclosed conditionals */
1426 {
1427 fail = PARSE_FAIL_UNCLOSED_COND;
1428 break;
1429 }
1430
1431 wps_bufptr += skip_end_of_line(wps_bufptr);
1432 break;
1433
1434 /* End of this line */
1435 case '\n':
1436 if (level >= 0) /* there are unclosed conditionals */
1437 {
1438 fail = PARSE_FAIL_UNCLOSED_COND;
1439 break;
1440 }
1441
1442 line++;
1443 wps_start_new_subline(data);
1444 data->num_lines++; /* Start a new line */
1445
1446 if ((data->num_lines < WPS_MAX_LINES) &&
1447 (data->num_sublines < WPS_MAX_SUBLINES))
1448 {
1449 data->lines[data->num_lines].first_subline_idx =
1450 data->num_sublines;
1451
1452 data->sublines[data->num_sublines].first_token_idx =
1453 data->num_tokens;
1454 }
1455
1456 break;
1457
1458 /* String */
1459 default:
1460 {
1461 unsigned int len = 1;
1462 const char *string_start = wps_bufptr - 1;
1463
1464 /* find the length of the string */
1465 while (*wps_bufptr && *wps_bufptr != '#' &&
1466 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1467 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1468 *wps_bufptr != '|' && *wps_bufptr != '\n')
1469 {
1470 wps_bufptr++;
1471 len++;
1472 }
1473
1474 /* look if we already have that string */
1475 char **str;
1476 int i;
1477 bool found;
1478 for (i = 0, str = data->strings, found = false;
1479 i < data->num_strings &&
1480 !(found = (strlen(*str) == len &&
1481 strncmp(string_start, *str, len) == 0));
1482 i++, str++);
1483 /* If a matching string is found, found is true and i is
1484 the index of the string. If not, found is false */
1485
1486 if (!found)
1487 {
1488 /* new string */
1489
1490 if (stringbuf_used + len > STRING_BUFFER_SIZE - 1
1491 || data->num_strings >= WPS_MAX_STRINGS)
1492 {
1493 /* too many strings or characters */
1494 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1495 break;
1496 }
1497
1498 strlcpy(stringbuf, string_start, len+1);
1499
1500 data->strings[data->num_strings] = stringbuf;
1501 stringbuf += len + 1;
1502 stringbuf_used += len + 1;
1503 data->tokens[data->num_tokens].value.i =
1504 data->num_strings;
1505 data->num_strings++;
1506 }
1507 else
1508 {
1509 /* another occurrence of an existing string */
1510 data->tokens[data->num_tokens].value.i = i;
1511 }
1512 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1513 data->num_tokens++;
1514 }
1515 break;
1516 }
1517 }
1518
1519 if (!fail && level >= 0) /* there are unclosed conditionals */
1520 fail = PARSE_FAIL_UNCLOSED_COND;
1521
1522 if (*wps_bufptr && !fail)
1523 /* one of the limits of the while loop was exceeded */
1524 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1525
1526 data->viewports[data->num_viewports].last_line = data->num_lines - 1;
1527
1528 /* We have finished with the last viewport, so increment count */
1529 data->num_viewports++;
1530
1531#if defined(DEBUG) || defined(SIMULATOR)
1532 print_debug_info(data, fail, line);
1533#endif
1534
1535 return (fail == 0);
1536}
1537
1538#ifdef HAVE_LCD_BITMAP
1539/* Clear the WPS image cache */
1540static void wps_images_clear(struct wps_data *data)
1541{
1542 int i;
1543 /* set images to unloaded and not displayed */
1544 for (i = 0; i < MAX_IMAGES; i++)
1545 {
1546 data->img[i].loaded = false;
1547 data->img[i].display = -1;
1548 data->img[i].always_display = false;
1549 data->img[i].num_subimages = 1;
1550 }
1551}
1552#endif
1553
1554/* initial setup of wps_data */
1555void wps_data_init(struct wps_data *wps_data)
1556{
1557#ifdef HAVE_LCD_BITMAP
1558 wps_images_clear(wps_data);
1559 wps_data->wps_sb_tag = false;
1560 wps_data->show_sb_on_wps = false;
1561 wps_data->img_buf_ptr = wps_data->img_buf; /* where in image buffer */
1562 wps_data->img_buf_free = IMG_BUFSIZE; /* free space in image buffer */
1563 wps_data->peak_meter_enabled = false;
1564 /* progress bars */
1565 wps_data->progressbar_count = 0;
1566#else /* HAVE_LCD_CHARCELLS */
1567 int i;
1568 for (i = 0; i < 8; i++)
1569 {
1570 wps_data->wps_progress_pat[i] = 0;
1571 }
1572 wps_data->full_line_progressbar = false;
1573#endif
1574 wps_data->button_time_volume = 0;
1575 wps_data->wps_loaded = false;
1576}
1577
1578static void wps_reset(struct wps_data *data)
1579{
1580#ifdef HAVE_REMOTE_LCD
1581 bool rwps = data->remote_wps; /* remember whether the data is for a RWPS */
1582#endif
1583 memset(data, 0, sizeof(*data));
1584 wps_data_init(data);
1585#ifdef HAVE_REMOTE_LCD
1586 data->remote_wps = rwps;
1587#endif
1588}
1589
1590#ifdef HAVE_LCD_BITMAP
1591
1592static bool load_wps_bitmaps(struct wps_data *wps_data, char *bmpdir)
1593{
1594 char img_path[MAX_PATH];
1595 struct bitmap *bitmap;
1596 bool *loaded;
1597 int n;
1598 for (n = 0; n < BACKDROP_BMP; n++)
1599 {
1600 if (bmp_names[n])
1601 {
1602 get_image_filename(bmp_names[n], bmpdir,
1603 img_path, sizeof(img_path));
1604
1605 if (n >= PROGRESSBAR_BMP ) {
1606 /* progressbar bitmap */
1607 bitmap = &wps_data->progressbar[n-PROGRESSBAR_BMP].bm;
1608 loaded = &wps_data->progressbar[n-PROGRESSBAR_BMP].have_bitmap_pb;
1609 } else {
1610 /* regular bitmap */
1611 bitmap = &wps_data->img[n].bm;
1612 loaded = &wps_data->img[n].loaded;
1613 }
1614
1615 /* load the image */
1616 bitmap->data = wps_data->img_buf_ptr;
1617 if (load_bitmap(wps_data, img_path, bitmap))
1618 {
1619 *loaded = true;
1620
1621 /* Calculate and store height if this image has sub-images */
1622 if (n < MAX_IMAGES)
1623 wps_data->img[n].subimage_height = wps_data->img[n].bm.height /
1624 wps_data->img[n].num_subimages;
1625 }
1626 else
1627 {
1628 /* Abort if we can't load an image */
1629 DEBUGF("ERR: Failed to load image %d - %s\n",n,img_path);
1630 return false;
1631 }
1632 }
1633 }
1634
1635#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1636 if (bmp_names[BACKDROP_BMP])
1637 {
1638 get_image_filename(bmp_names[BACKDROP_BMP], bmpdir,
1639 img_path, sizeof(img_path));
1640
1641#if defined(HAVE_REMOTE_LCD)
1642 /* We only need to check LCD type if there is a remote LCD */
1643 if (!wps_data->remote_wps)
1644#endif
1645 {
1646 /* Load backdrop for the main LCD */
1647 if (!load_wps_backdrop(img_path))
1648 return false;
1649 }
1650#if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1651 else
1652 {
1653 /* Load backdrop for the remote LCD */
1654 if (!load_remote_wps_backdrop(img_path))
1655 return false;
1656 }
1657#endif
1658 }
1659#endif /* has backdrop support */
1660
1661 /* If we got here, everything was OK */
1662 return true;
1663}
1664
1665#endif /* HAVE_LCD_BITMAP */
1666
1667/* to setup up the wps-data from a format-buffer (isfile = false)
1668 from a (wps-)file (isfile = true)*/
1669bool wps_data_load(struct wps_data *wps_data,
1670 struct screen *display,
1671 const char *buf,
1672 bool isfile)
1673{
1674#ifdef HAVE_ALBUMART
1675 struct mp3entry *curtrack;
1676 long offset;
1677 int status;
1678 int wps_uses_albumart = wps_data->wps_uses_albumart;
1679 int albumart_max_height = wps_data->albumart_max_height;
1680 int albumart_max_width = wps_data->albumart_max_width;
1681#endif
1682 if (!wps_data || !buf)
1683 return false;
1684
1685 wps_reset(wps_data);
1686
1687 /* Initialise the first (default) viewport */
1688 wps_data->viewports[0].vp.x = 0;
1689 wps_data->viewports[0].vp.width = display->getwidth();
1690 wps_data->viewports[0].vp.height = display->getheight();
1691 switch (statusbar_position(display->screen_type))
1692 {
1693 case STATUSBAR_OFF:
1694 wps_data->viewports[0].vp.y = 0;
1695 break;
1696 case STATUSBAR_TOP:
1697 wps_data->viewports[0].vp.y = STATUSBAR_HEIGHT;
1698 wps_data->viewports[0].vp.height -= STATUSBAR_HEIGHT;
1699 break;
1700 case STATUSBAR_BOTTOM:
1701 wps_data->viewports[0].vp.y = 0;
1702 wps_data->viewports[0].vp.height -= STATUSBAR_HEIGHT;
1703 break;
1704 }
1705#ifdef HAVE_LCD_BITMAP
1706 wps_data->viewports[0].vp.font = FONT_UI;
1707 wps_data->viewports[0].vp.drawmode = DRMODE_SOLID;
1708#endif
1709#if LCD_DEPTH > 1
1710 if (display->depth > 1)
1711 {
1712 wps_data->viewports[0].vp.fg_pattern = display->get_foreground();
1713 wps_data->viewports[0].vp.bg_pattern = display->get_background();
1714 }
1715#endif
1716 if (!isfile)
1717 {
1718 return wps_parse(wps_data, buf);
1719 }
1720 else
1721 {
1722 /*
1723 * Hardcode loading WPS_DEFAULTCFG to cause a reset ideally this
1724 * wants to be a virtual file. Feel free to modify dirbrowse()
1725 * if you're feeling brave.
1726 */
1727#ifndef __PCTOOL__
1728 if (! strcmp(buf, WPS_DEFAULTCFG) )
1729 {
1730 global_settings.wps_file[0] = 0;
1731 return false;
1732 }
1733
1734#ifdef HAVE_REMOTE_LCD
1735 if (! strcmp(buf, RWPS_DEFAULTCFG) )
1736 {
1737 global_settings.rwps_file[0] = 0;
1738 return false;
1739 }
1740#endif
1741#endif /* __PCTOOL__ */
1742
1743 int fd = open_utf8(buf, O_RDONLY);
1744
1745 if (fd < 0)
1746 return false;
1747
1748 /* get buffer space from the plugin buffer */
1749 size_t buffersize = 0;
1750 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
1751
1752 if (!wps_buffer)
1753 return false;
1754
1755 /* copy the file's content to the buffer for parsing,
1756 ensuring that every line ends with a newline char. */
1757 unsigned int start = 0;
1758 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
1759 {
1760 start += strlen(wps_buffer + start);
1761 if (start < buffersize - 1)
1762 {
1763 wps_buffer[start++] = '\n';
1764 wps_buffer[start] = 0;
1765 }
1766 }
1767
1768 close(fd);
1769
1770 if (start <= 0)
1771 return false;
1772
1773#ifdef HAVE_LCD_BITMAP
1774 /* Set all filename pointers to NULL */
1775 memset(bmp_names, 0, sizeof(bmp_names));
1776#endif
1777
1778 /* parse the WPS source */
1779 if (!wps_parse(wps_data, wps_buffer)) {
1780 wps_reset(wps_data);
1781 return false;
1782 }
1783
1784 wps_data->wps_loaded = true;
1785
1786#ifdef HAVE_LCD_BITMAP
1787 /* get the bitmap dir */
1788 char bmpdir[MAX_PATH];
1789 char *dot = strrchr(buf, '.');
1790
1791 strlcpy(bmpdir, buf, dot - buf + 1);
1792
1793 /* load the bitmaps that were found by the parsing */
1794 if (!load_wps_bitmaps(wps_data, bmpdir)) {
1795 wps_reset(wps_data);
1796 return false;
1797 }
1798#endif
1799#ifdef HAVE_ALBUMART
1800 status = audio_status();
1801 if (((!wps_uses_albumart && wps_data->wps_uses_albumart) ||
1802 (wps_data->wps_uses_albumart &&
1803 (albumart_max_height != wps_data->albumart_max_height ||
1804 albumart_max_width != wps_data->albumart_max_width))) &&
1805 status & AUDIO_STATUS_PLAY)
1806 {
1807 curtrack = audio_current_track();
1808 offset = curtrack->offset;
1809 audio_stop();
1810 if (!(status & AUDIO_STATUS_PAUSE))
1811 audio_play(offset);
1812 }
1813#endif
1814 return true;
1815 }
1816}
1817
1818int wps_subline_index(struct wps_data *data, int line, int subline)
1819{
1820 return data->lines[line].first_subline_idx + subline;
1821}
1822
1823int wps_first_token_index(struct wps_data *data, int line, int subline)
1824{
1825 int first_subline_idx = data->lines[line].first_subline_idx;
1826 return data->sublines[first_subline_idx + subline].first_token_idx;
1827}
1828
1829int wps_last_token_index(struct wps_data *data, int line, int subline)
1830{
1831 int first_subline_idx = data->lines[line].first_subline_idx;
1832 int idx = first_subline_idx + subline;
1833 if (idx < data->num_sublines - 1)
1834 {
1835 /* This subline ends where the next begins */
1836 return data->sublines[idx+1].first_token_idx - 1;
1837 }
1838 else
1839 {
1840 /* The last subline goes to the end */
1841 return data->num_tokens - 1;
1842 }
1843}
diff --git a/apps/gui/wps_engine/wps_tokens.c b/apps/gui/wps_engine/wps_tokens.c
new file mode 100644
index 0000000000..3852251916
--- /dev/null
+++ b/apps/gui/wps_engine/wps_tokens.c
@@ -0,0 +1,807 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2002-2007 Björn Stenberg
11 * Copyright (C) 2007-2008 Nicolas Pennequin
12 *
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.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ****************************************************************************/
22#include "font.h"
23#include <stdio.h>
24#include <string.h>
25#include <stdlib.h>
26#include "action.h"
27#include "system.h"
28#include "settings.h"
29#include "settings_list.h"
30#include "rbunicode.h"
31#include "timefuncs.h"
32#include "audio.h"
33#include "status.h"
34#include "power.h"
35#include "powermgmt.h"
36#include "sound.h"
37#include "debug.h"
38#ifdef HAVE_LCD_CHARCELLS
39#include "hwcompat.h"
40#endif
41#include "abrepeat.h"
42#include "mp3_playback.h"
43#include "lang.h"
44#include "misc.h"
45#include "led.h"
46#ifdef HAVE_LCD_BITMAP
47/* Image stuff */
48#include "albumart.h"
49#endif
50#include "dsp.h"
51#include "playlist.h"
52#if CONFIG_CODEC == SWCODEC
53#include "playback.h"
54#endif
55#include "viewport.h"
56
57#include "wps_internals.h"
58#include "music_screen.h"
59
60static char* get_codectype(const struct mp3entry* id3)
61{
62 if (id3->codectype < AFMT_NUM_CODECS) {
63 return (char*)audio_formats[id3->codectype].label;
64 } else {
65 return NULL;
66 }
67}
68
69/* Extract a part from a path.
70 *
71 * buf - buffer extract part to.
72 * buf_size - size of buffer.
73 * path - path to extract from.
74 * level - what to extract. 0 is file name, 1 is parent of file, 2 is
75 * parent of parent, etc.
76 *
77 * Returns buf if the desired level was found, NULL otherwise.
78 */
79static char* get_dir(char* buf, int buf_size, const char* path, int level)
80{
81 const char* sep;
82 const char* last_sep;
83 int len;
84
85 sep = path + strlen(path);
86 last_sep = sep;
87
88 while (sep > path)
89 {
90 if ('/' == *(--sep))
91 {
92 if (!level)
93 break;
94
95 level--;
96 last_sep = sep - 1;
97 }
98 }
99
100 if (level || (last_sep <= sep))
101 return NULL;
102
103 len = MIN(last_sep - sep, buf_size - 1);
104 strlcpy(buf, sep + 1, len + 1);
105 return buf;
106}
107
108/* Return the tag found at index i and write its value in buf.
109 The return value is buf if the tag had a value, or NULL if not.
110
111 intval is used with conditionals/enums: when this function is called,
112 intval should contain the number of options in the conditional/enum.
113 When this function returns, intval is -1 if the tag is non numeric or,
114 if the tag is numeric, *intval is the enum case we want to go to (between 1
115 and the original value of *intval, inclusive).
116 When not treating a conditional/enum, intval should be NULL.
117*/
118const char *get_token_value(struct gui_wps *gwps,
119 struct wps_token *token,
120 char *buf, int buf_size,
121 int *intval)
122{
123 if (!gwps)
124 return NULL;
125
126 struct wps_data *data = gwps->data;
127 struct wps_state *state = gwps->state;
128
129 if (!data || !state)
130 return NULL;
131
132 struct mp3entry *id3;
133
134 if (token->next)
135 id3 = state->nid3;
136 else
137 id3 = state->id3;
138
139 if (!id3)
140 return NULL;
141
142#if CONFIG_RTC
143 struct tm* tm = NULL;
144
145 /* if the token is an RTC one, update the time
146 and do the necessary checks */
147 if (token->type >= WPS_TOKENS_RTC_BEGIN
148 && token->type <= WPS_TOKENS_RTC_END)
149 {
150 tm = get_time();
151
152 if (!valid_time(tm))
153 return NULL;
154 }
155#endif
156
157 int limit = 1;
158 if (intval)
159 {
160 limit = *intval;
161 *intval = -1;
162 }
163
164 switch (token->type)
165 {
166 case WPS_TOKEN_CHARACTER:
167 return &(token->value.c);
168
169 case WPS_TOKEN_STRING:
170 return data->strings[token->value.i];
171
172 case WPS_TOKEN_TRACK_TIME_ELAPSED:
173 format_time(buf, buf_size,
174 id3->elapsed + state->ff_rewind_count);
175 return buf;
176
177 case WPS_TOKEN_TRACK_TIME_REMAINING:
178 format_time(buf, buf_size,
179 id3->length - id3->elapsed -
180 state->ff_rewind_count);
181 return buf;
182
183 case WPS_TOKEN_TRACK_LENGTH:
184 format_time(buf, buf_size, id3->length);
185 return buf;
186
187 case WPS_TOKEN_PLAYLIST_ENTRIES:
188 snprintf(buf, buf_size, "%d", playlist_amount());
189 return buf;
190
191 case WPS_TOKEN_PLAYLIST_NAME:
192 return playlist_name(NULL, buf, buf_size);
193
194 case WPS_TOKEN_PLAYLIST_POSITION:
195 snprintf(buf, buf_size, "%d", playlist_get_display_index());
196 return buf;
197
198 case WPS_TOKEN_PLAYLIST_SHUFFLE:
199 if ( global_settings.playlist_shuffle )
200 return "s";
201 else
202 return NULL;
203 break;
204
205 case WPS_TOKEN_VOLUME:
206 snprintf(buf, buf_size, "%d", global_settings.volume);
207 if (intval)
208 {
209 if (global_settings.volume == sound_min(SOUND_VOLUME))
210 {
211 *intval = 1;
212 }
213 else if (global_settings.volume == 0)
214 {
215 *intval = limit - 1;
216 }
217 else if (global_settings.volume > 0)
218 {
219 *intval = limit;
220 }
221 else
222 {
223 *intval = (limit - 3) * (global_settings.volume
224 - sound_min(SOUND_VOLUME) - 1)
225 / (-1 - sound_min(SOUND_VOLUME)) + 2;
226 }
227 }
228 return buf;
229
230 case WPS_TOKEN_TRACK_ELAPSED_PERCENT:
231 if (id3->length <= 0)
232 return NULL;
233
234 if (intval)
235 {
236 *intval = limit * (id3->elapsed + state->ff_rewind_count)
237 / id3->length + 1;
238 }
239 snprintf(buf, buf_size, "%d",
240 100*(id3->elapsed + state->ff_rewind_count) / id3->length);
241 return buf;
242
243 case WPS_TOKEN_METADATA_ARTIST:
244 return id3->artist;
245
246 case WPS_TOKEN_METADATA_COMPOSER:
247 return id3->composer;
248
249 case WPS_TOKEN_METADATA_ALBUM:
250 return id3->album;
251
252 case WPS_TOKEN_METADATA_ALBUM_ARTIST:
253 return id3->albumartist;
254
255 case WPS_TOKEN_METADATA_GROUPING:
256 return id3->grouping;
257
258 case WPS_TOKEN_METADATA_GENRE:
259 return id3->genre_string;
260
261 case WPS_TOKEN_METADATA_DISC_NUMBER:
262 if (id3->disc_string)
263 return id3->disc_string;
264 if (id3->discnum) {
265 snprintf(buf, buf_size, "%d", id3->discnum);
266 return buf;
267 }
268 return NULL;
269
270 case WPS_TOKEN_METADATA_TRACK_NUMBER:
271 if (id3->track_string)
272 return id3->track_string;
273
274 if (id3->tracknum) {
275 snprintf(buf, buf_size, "%d", id3->tracknum);
276 return buf;
277 }
278 return NULL;
279
280 case WPS_TOKEN_METADATA_TRACK_TITLE:
281 return id3->title;
282
283 case WPS_TOKEN_METADATA_VERSION:
284 switch (id3->id3version)
285 {
286 case ID3_VER_1_0:
287 return "1";
288
289 case ID3_VER_1_1:
290 return "1.1";
291
292 case ID3_VER_2_2:
293 return "2.2";
294
295 case ID3_VER_2_3:
296 return "2.3";
297
298 case ID3_VER_2_4:
299 return "2.4";
300
301 default:
302 return NULL;
303 }
304
305 case WPS_TOKEN_METADATA_YEAR:
306 if( id3->year_string )
307 return id3->year_string;
308
309 if (id3->year) {
310 snprintf(buf, buf_size, "%d", id3->year);
311 return buf;
312 }
313 return NULL;
314
315 case WPS_TOKEN_METADATA_COMMENT:
316 return id3->comment;
317
318#ifdef HAVE_ALBUMART
319 case WPS_TOKEN_ALBUMART_DISPLAY:
320 draw_album_art(gwps, audio_current_aa_hid(), false);
321 return NULL;
322
323 case WPS_TOKEN_ALBUMART_FOUND:
324 if (audio_current_aa_hid() >= 0) {
325 return "C";
326 }
327 return NULL;
328#endif
329
330 case WPS_TOKEN_FILE_BITRATE:
331 if(id3->bitrate)
332 snprintf(buf, buf_size, "%d", id3->bitrate);
333 else
334 return "?";
335 return buf;
336
337 case WPS_TOKEN_FILE_CODEC:
338 if (intval)
339 {
340 if(id3->codectype == AFMT_UNKNOWN)
341 *intval = AFMT_NUM_CODECS;
342 else
343 *intval = id3->codectype;
344 }
345 return get_codectype(id3);
346
347 case WPS_TOKEN_FILE_FREQUENCY:
348 snprintf(buf, buf_size, "%ld", id3->frequency);
349 return buf;
350
351 case WPS_TOKEN_FILE_FREQUENCY_KHZ:
352 /* ignore remainders < 100, so 22050 Hz becomes just 22k */
353 if ((id3->frequency % 1000) < 100)
354 snprintf(buf, buf_size, "%ld", id3->frequency / 1000);
355 else
356 snprintf(buf, buf_size, "%ld.%d",
357 id3->frequency / 1000,
358 (id3->frequency % 1000) / 100);
359 return buf;
360
361 case WPS_TOKEN_FILE_NAME:
362 if (get_dir(buf, buf_size, id3->path, 0)) {
363 /* Remove extension */
364 char* sep = strrchr(buf, '.');
365 if (NULL != sep) {
366 *sep = 0;
367 }
368 return buf;
369 }
370 else {
371 return NULL;
372 }
373
374 case WPS_TOKEN_FILE_NAME_WITH_EXTENSION:
375 return get_dir(buf, buf_size, id3->path, 0);
376
377 case WPS_TOKEN_FILE_PATH:
378 return id3->path;
379
380 case WPS_TOKEN_FILE_SIZE:
381 snprintf(buf, buf_size, "%ld", id3->filesize / 1024);
382 return buf;
383
384 case WPS_TOKEN_FILE_VBR:
385 return id3->vbr ? "(avg)" : NULL;
386
387 case WPS_TOKEN_FILE_DIRECTORY:
388 return get_dir(buf, buf_size, id3->path, token->value.i);
389
390 case WPS_TOKEN_BATTERY_PERCENT:
391 {
392 int l = battery_level();
393
394 if (intval)
395 {
396 limit = MAX(limit, 2);
397 if (l > -1) {
398 /* First enum is used for "unknown level". */
399 *intval = (limit - 1) * l / 100 + 2;
400 } else {
401 *intval = 1;
402 }
403 }
404
405 if (l > -1) {
406 snprintf(buf, buf_size, "%d", l);
407 return buf;
408 } else {
409 return "?";
410 }
411 }
412
413 case WPS_TOKEN_BATTERY_VOLTS:
414 {
415 unsigned int v = battery_voltage();
416 snprintf(buf, buf_size, "%d.%02d", v / 1000, (v % 1000) / 10);
417 return buf;
418 }
419
420 case WPS_TOKEN_BATTERY_TIME:
421 {
422 int t = battery_time();
423 if (t >= 0)
424 snprintf(buf, buf_size, "%dh %dm", t / 60, t % 60);
425 else
426 return "?h ?m";
427 return buf;
428 }
429
430#if CONFIG_CHARGING
431 case WPS_TOKEN_BATTERY_CHARGER_CONNECTED:
432 {
433 if(charger_input_state==CHARGER)
434 return "p";
435 else
436 return NULL;
437 }
438#endif
439#if CONFIG_CHARGING >= CHARGING_MONITOR
440 case WPS_TOKEN_BATTERY_CHARGING:
441 {
442 if (charge_state == CHARGING || charge_state == TOPOFF) {
443 return "c";
444 } else {
445 return NULL;
446 }
447 }
448#endif
449 case WPS_TOKEN_BATTERY_SLEEPTIME:
450 {
451 if (get_sleep_timer() == 0)
452 return NULL;
453 else
454 {
455 format_time(buf, buf_size, get_sleep_timer() * 1000);
456 return buf;
457 }
458 }
459
460 case WPS_TOKEN_PLAYBACK_STATUS:
461 {
462 int status = audio_status();
463 int mode = 1;
464 if (status == AUDIO_STATUS_PLAY)
465 mode = 2;
466 if (is_wps_fading() ||
467 (status & AUDIO_STATUS_PAUSE && !status_get_ffmode()))
468 mode = 3;
469 if (status_get_ffmode() == STATUS_FASTFORWARD)
470 mode = 4;
471 if (status_get_ffmode() == STATUS_FASTBACKWARD)
472 mode = 5;
473
474 if (intval) {
475 *intval = mode;
476 }
477
478 snprintf(buf, buf_size, "%d", mode-1);
479 return buf;
480 }
481
482 case WPS_TOKEN_REPEAT_MODE:
483 if (intval)
484 *intval = global_settings.repeat_mode + 1;
485 snprintf(buf, buf_size, "%d", global_settings.repeat_mode);
486 return buf;
487
488 case WPS_TOKEN_RTC_PRESENT:
489#if CONFIG_RTC
490 return "c";
491#else
492 return NULL;
493#endif
494
495#if CONFIG_RTC
496 case WPS_TOKEN_RTC_12HOUR_CFG:
497 if (intval)
498 *intval = global_settings.timeformat + 1;
499 snprintf(buf, buf_size, "%d", global_settings.timeformat);
500 return buf;
501
502 case WPS_TOKEN_RTC_DAY_OF_MONTH:
503 /* d: day of month (01..31) */
504 snprintf(buf, buf_size, "%02d", tm->tm_mday);
505 return buf;
506
507 case WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED:
508 /* e: day of month, blank padded ( 1..31) */
509 snprintf(buf, buf_size, "%2d", tm->tm_mday);
510 return buf;
511
512 case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED:
513 /* H: hour (00..23) */
514 snprintf(buf, buf_size, "%02d", tm->tm_hour);
515 return buf;
516
517 case WPS_TOKEN_RTC_HOUR_24:
518 /* k: hour ( 0..23) */
519 snprintf(buf, buf_size, "%2d", tm->tm_hour);
520 return buf;
521
522 case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED:
523 /* I: hour (01..12) */
524 snprintf(buf, buf_size, "%02d",
525 (tm->tm_hour % 12 == 0) ? 12 : tm->tm_hour % 12);
526 return buf;
527
528 case WPS_TOKEN_RTC_HOUR_12:
529 /* l: hour ( 1..12) */
530 snprintf(buf, buf_size, "%2d",
531 (tm->tm_hour % 12 == 0) ? 12 : tm->tm_hour % 12);
532 return buf;
533
534 case WPS_TOKEN_RTC_MONTH:
535 /* m: month (01..12) */
536 if (intval)
537 *intval = tm->tm_mon + 1;
538 snprintf(buf, buf_size, "%02d", tm->tm_mon + 1);
539 return buf;
540
541 case WPS_TOKEN_RTC_MINUTE:
542 /* M: minute (00..59) */
543 snprintf(buf, buf_size, "%02d", tm->tm_min);
544 return buf;
545
546 case WPS_TOKEN_RTC_SECOND:
547 /* S: second (00..59) */
548 snprintf(buf, buf_size, "%02d", tm->tm_sec);
549 return buf;
550
551 case WPS_TOKEN_RTC_YEAR_2_DIGITS:
552 /* y: last two digits of year (00..99) */
553 snprintf(buf, buf_size, "%02d", tm->tm_year % 100);
554 return buf;
555
556 case WPS_TOKEN_RTC_YEAR_4_DIGITS:
557 /* Y: year (1970...) */
558 snprintf(buf, buf_size, "%04d", tm->tm_year + 1900);
559 return buf;
560
561 case WPS_TOKEN_RTC_AM_PM_UPPER:
562 /* p: upper case AM or PM indicator */
563 return tm->tm_hour/12 == 0 ? "AM" : "PM";
564
565 case WPS_TOKEN_RTC_AM_PM_LOWER:
566 /* P: lower case am or pm indicator */
567 return tm->tm_hour/12 == 0 ? "am" : "pm";
568
569 case WPS_TOKEN_RTC_WEEKDAY_NAME:
570 /* a: abbreviated weekday name (Sun..Sat) */
571 return str(LANG_WEEKDAY_SUNDAY + tm->tm_wday);
572
573 case WPS_TOKEN_RTC_MONTH_NAME:
574 /* b: abbreviated month name (Jan..Dec) */
575 return str(LANG_MONTH_JANUARY + tm->tm_mon);
576
577 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON:
578 /* u: day of week (1..7); 1 is Monday */
579 if (intval)
580 *intval = (tm->tm_wday == 0) ? 7 : tm->tm_wday;
581 snprintf(buf, buf_size, "%1d", tm->tm_wday + 1);
582 return buf;
583
584 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN:
585 /* w: day of week (0..6); 0 is Sunday */
586 if (intval)
587 *intval = tm->tm_wday + 1;
588 snprintf(buf, buf_size, "%1d", tm->tm_wday);
589 return buf;
590#else
591 case WPS_TOKEN_RTC_DAY_OF_MONTH:
592 case WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED:
593 case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED:
594 case WPS_TOKEN_RTC_HOUR_24:
595 case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED:
596 case WPS_TOKEN_RTC_HOUR_12:
597 case WPS_TOKEN_RTC_MONTH:
598 case WPS_TOKEN_RTC_MINUTE:
599 case WPS_TOKEN_RTC_SECOND:
600 case WPS_TOKEN_RTC_AM_PM_UPPER:
601 case WPS_TOKEN_RTC_AM_PM_LOWER:
602 case WPS_TOKEN_RTC_YEAR_2_DIGITS:
603 return "--";
604 case WPS_TOKEN_RTC_YEAR_4_DIGITS:
605 return "----";
606 case WPS_TOKEN_RTC_WEEKDAY_NAME:
607 case WPS_TOKEN_RTC_MONTH_NAME:
608 return "---";
609 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON:
610 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN:
611 return "-";
612#endif
613
614#ifdef HAVE_LCD_CHARCELLS
615 case WPS_TOKEN_PROGRESSBAR:
616 {
617 char *end = utf8encode(data->wps_progress_pat[0], buf);
618 *end = '\0';
619 return buf;
620 }
621
622 case WPS_TOKEN_PLAYER_PROGRESSBAR:
623 if(is_new_player())
624 {
625 /* we need 11 characters (full line) for
626 progress-bar */
627 strlcpy(buf, " ", buf_size);
628 }
629 else
630 {
631 /* Tell the user if we have an OldPlayer */
632 strlcpy(buf, " <Old LCD> ", buf_size);
633 }
634 return buf;
635#endif
636
637#ifdef HAVE_TAGCACHE
638 case WPS_TOKEN_DATABASE_PLAYCOUNT:
639 if (intval) {
640 *intval = id3->playcount + 1;
641 }
642 snprintf(buf, buf_size, "%ld", id3->playcount);
643 return buf;
644
645 case WPS_TOKEN_DATABASE_RATING:
646 if (intval) {
647 *intval = id3->rating + 1;
648 }
649 snprintf(buf, buf_size, "%d", id3->rating);
650 return buf;
651
652 case WPS_TOKEN_DATABASE_AUTOSCORE:
653 if (intval)
654 *intval = id3->score + 1;
655
656 snprintf(buf, buf_size, "%d", id3->score);
657 return buf;
658#endif
659
660#if (CONFIG_CODEC == SWCODEC)
661 case WPS_TOKEN_CROSSFADE:
662 if (intval)
663 *intval = global_settings.crossfade + 1;
664 snprintf(buf, buf_size, "%d", global_settings.crossfade);
665 return buf;
666
667 case WPS_TOKEN_REPLAYGAIN:
668 {
669 int val;
670
671 if (global_settings.replaygain_type == REPLAYGAIN_OFF)
672 val = 1; /* off */
673 else
674 {
675 int type =
676 get_replaygain_mode(id3->track_gain_string != NULL,
677 id3->album_gain_string != NULL);
678 if (type < 0)
679 val = 6; /* no tag */
680 else
681 val = type + 2;
682
683 if (global_settings.replaygain_type == REPLAYGAIN_SHUFFLE)
684 val += 2;
685 }
686
687 if (intval)
688 *intval = val;
689
690 switch (val)
691 {
692 case 1:
693 case 6:
694 return "+0.00 dB";
695 break;
696 case 2:
697 case 4:
698 strlcpy(buf, id3->track_gain_string, buf_size);
699 break;
700 case 3:
701 case 5:
702 strlcpy(buf, id3->album_gain_string, buf_size);
703 break;
704 }
705 return buf;
706 }
707#endif /* (CONFIG_CODEC == SWCODEC) */
708
709#if (CONFIG_CODEC != MAS3507D)
710 case WPS_TOKEN_SOUND_PITCH:
711 {
712 int val = sound_get_pitch();
713 snprintf(buf, buf_size, "%d.%d",
714 val / 10, val % 10);
715 return buf;
716 }
717#endif
718
719 case WPS_TOKEN_MAIN_HOLD:
720#ifdef HAS_BUTTON_HOLD
721 if (button_hold())
722#else
723 if (is_keys_locked())
724#endif /*hold switch or softlock*/
725 return "h";
726 else
727 return NULL;
728
729#ifdef HAS_REMOTE_BUTTON_HOLD
730 case WPS_TOKEN_REMOTE_HOLD:
731 if (remote_button_hold())
732 return "r";
733 else
734 return NULL;
735#endif
736
737#if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
738 case WPS_TOKEN_VLED_HDD:
739 if(led_read(HZ/2))
740 return "h";
741 else
742 return NULL;
743#endif
744 case WPS_TOKEN_BUTTON_VOLUME:
745 if (data->button_time_volume &&
746 TIME_BEFORE(current_tick, data->button_time_volume +
747 token->value.i * TIMEOUT_UNIT))
748 return "v";
749 return NULL;
750 case WPS_TOKEN_LASTTOUCH:
751#ifdef HAVE_TOUCHSCREEN
752 if (TIME_BEFORE(current_tick, token->value.i * TIMEOUT_UNIT +
753 touchscreen_last_touch()))
754 return "t";
755#endif
756 return NULL;
757
758 case WPS_TOKEN_SETTING:
759 {
760 if (intval)
761 {
762 /* Handle contionals */
763 const struct settings_list *s = settings+token->value.i;
764 switch (s->flags&F_T_MASK)
765 {
766 case F_T_INT:
767 case F_T_UINT:
768 if (s->flags&F_RGB)
769 /* %?St|name|<#000000|#000001|...|#FFFFFF> */
770 /* shouldn't overflow since colors are stored
771 * on 16 bits ...
772 * but this is pretty useless anyway */
773 *intval = *(int*)s->setting + 1;
774 else if (s->cfg_vals == NULL)
775 /* %?St|name|<1st choice|2nd choice|...> */
776 *intval = (*(int*)s->setting-s->int_setting->min)
777 /s->int_setting->step + 1;
778 else
779 /* %?St|name|<1st choice|2nd choice|...> */
780 /* Not sure about this one. cfg_name/vals are
781 * indexed from 0 right? */
782 *intval = *(int*)s->setting + 1;
783 break;
784 case F_T_BOOL:
785 /* %?St|name|<if true|if false> */
786 *intval = *(bool*)s->setting?1:2;
787 break;
788 case F_T_CHARPTR:
789 /* %?St|name|<if non empty string|if empty>
790 * The string's emptyness discards the setting's
791 * prefix and suffix */
792 *intval = ((char*)s->setting)[0]?1:2;
793 break;
794 default:
795 /* This shouldn't happen ... but you never know */
796 *intval = -1;
797 break;
798 }
799 }
800 cfg_to_string(token->value.i,buf,buf_size);
801 return buf;
802 }
803
804 default:
805 return NULL;
806 }
807}