summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristi Scarborough <christi@coraline.org>2005-11-17 20:20:01 +0000
committerChristi Scarborough <christi@coraline.org>2005-11-17 20:20:01 +0000
commitf8cc3211a60940228dade59436ba7ce2003445a3 (patch)
tree115c9e57f2545bc8b4d8f9ff72c1608488e8faab
parent4c0b83f5e913820bbcf203fee1606d9910925946 (diff)
downloadrockbox-f8cc3211a60940228dade59436ba7ce2003445a3.tar.gz
rockbox-f8cc3211a60940228dade59436ba7ce2003445a3.zip
And the rest of the files too
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7935 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/gui/gwps-common.c2024
-rw-r--r--apps/gui/gwps-common.h27
-rw-r--r--apps/gui/gwps.c853
-rw-r--r--apps/gui/gwps.h351
4 files changed, 3255 insertions, 0 deletions
diff --git a/apps/gui/gwps-common.c b/apps/gui/gwps-common.c
new file mode 100644
index 0000000000..ed3e852661
--- /dev/null
+++ b/apps/gui/gwps-common.c
@@ -0,0 +1,2024 @@
+#include "gwps-common.h"
+#include "gwps.h"
+#include "font.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "system.h"
+#include "settings.h"
+#include "audio.h"
+#include "status.h"
+#include "power.h"
+#include "powermgmt.h"
+#include "sound.h"
+#ifdef HAVE_LCD_CHARCELLS
+#include "hwcompat.h"
+#endif
+#include "mp3_playback.h"
+#include "backlight.h"
+#include "lang.h"
+#include "misc.h"
+
+#include "statusbar.h"
+#include "splash.h"
+#include "scrollbar.h"
+#ifdef HAVE_LCD_BITMAP
+#include "peakmeter.h"
+/* Image stuff */
+#include "bmp.h"
+#include "atoi.h"
+#endif
+
+#ifdef HAVE_LCD_CHARCELLS
+static bool draw_player_progress(struct gui_wps *gwps);
+static void draw_player_fullbar(struct gui_wps *gwps,
+ char* buf, int buf_size);
+#endif
+
+#define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */
+ /* 3% of 30min file == 54s step size */
+#define MIN_FF_REWIND_STEP 500
+
+/* Format time into buf.
+ *
+ * buf - buffer to format to.
+ * buf_size - size of buffer.
+ * time - time to format, in milliseconds.
+ */
+void gui_wps_format_time(char* buf, int buf_size, long time)
+{
+ if ( time < 3600000 ) {
+ snprintf(buf, buf_size, "%d:%02d",
+ (int) (time % 3600000 / 60000), (int) (time % 60000 / 1000));
+ } else {
+ snprintf(buf, buf_size, "%d:%02d:%02d",
+ (int) (time / 3600000), (int) (time % 3600000 / 60000),
+ (int) (time % 60000 / 1000));
+ }
+}
+
+/* Extract a part from a path.
+ *
+ * buf - buffer extract part to.
+ * buf_size - size of buffer.
+ * path - path to extract from.
+ * level - what to extract. 0 is file name, 1 is parent of file, 2 is
+ * parent of parent, etc.
+ *
+ * Returns buf if the desired level was found, NULL otherwise.
+ */
+static char* get_dir(char* buf, int buf_size, const char* path, int level)
+{
+ const char* sep;
+ const char* last_sep;
+ int len;
+
+ sep = path + strlen(path);
+ last_sep = sep;
+
+ while (sep > path)
+ {
+ if ('/' == *(--sep))
+ {
+ if (!level)
+ {
+ break;
+ }
+
+ level--;
+ last_sep = sep - 1;
+ }
+ }
+
+ if (level || (last_sep <= sep))
+ {
+ return NULL;
+ }
+
+ len = MIN(last_sep - sep, buf_size - 1);
+ strncpy(buf, sep + 1, len);
+ buf[len] = 0;
+ return buf;
+}
+
+/* Get the tag specified by the two characters at fmt.
+ *
+ * cid3 - ID3 data to get tag values from.
+ * nid3 - next-song ID3 data to get tag values from.
+ * tag - string (of two characters) specifying the tag to get.
+ * buf - buffer to certain tags, such as track number, play time or
+ * directory name.
+ * buf_size - size of buffer.
+ * flags - returns the type of the line. See constants i wps-display.h
+ *
+ * Returns the tag. NULL indicates the tag wasn't available.
+ */
+static char* get_tag(struct wps_data* wps_data,
+ struct mp3entry* cid3,
+ struct mp3entry* nid3,
+ const char* tag,
+ char* buf,
+ int buf_size,
+ unsigned char* tag_len,
+ unsigned short* subline_time_mult,
+ unsigned char* flags,
+ int *intval)
+{
+ struct mp3entry *id3 = cid3; /* default to current song */
+#ifndef HAVE_LCD_CHARCELLS
+ (void)wps_data;
+#endif
+ if ((0 == tag[0]) || (0 == tag[1]))
+ {
+ *tag_len = 0;
+ return NULL;
+ }
+
+ *tag_len = 2;
+
+ *intval = 0;
+
+ switch (tag[0])
+ {
+ case 'I': /* ID3 Information */
+ id3 = nid3; /* display next-song data */
+ *flags |= WPS_REFRESH_DYNAMIC;
+ if(!id3)
+ return NULL; /* no such info (yet) */
+ /* fall-through */
+ case 'i': /* ID3 Information */
+ *flags |= WPS_REFRESH_STATIC;
+ switch (tag[1])
+ {
+ case 't': /* ID3 Title */
+ return id3->title;
+
+ case 'a': /* ID3 Artist */
+ return id3->artist;
+
+ case 'n': /* ID3 Track Number */
+ if (id3->track_string)
+ return id3->track_string;
+
+ if (id3->tracknum) {
+ snprintf(buf, buf_size, "%d", id3->tracknum);
+ return buf;
+ }
+ return NULL;
+
+ case 'd': /* ID3 Album/Disc */
+ return id3->album;
+
+ case 'c': /* ID3 Composer */
+ return id3->composer;
+
+ case 'y': /* year */
+ if( id3->year_string )
+ return id3->year_string;
+
+ if (id3->year) {
+ snprintf(buf, buf_size, "%d", id3->year);
+ return buf;
+ }
+ return NULL;
+
+ case 'g': /* genre */
+ return id3_get_genre(id3);
+
+ case 'v': /* id3 version */
+ switch (id3->id3version) {
+ case ID3_VER_1_0:
+ return "1";
+
+ case ID3_VER_1_1:
+ return "1.1";
+
+ case ID3_VER_2_2:
+ return "2.2";
+
+ case ID3_VER_2_3:
+ return "2.3";
+
+ case ID3_VER_2_4:
+ return "2.4";
+
+ default:
+ return NULL;
+ }
+ }
+ break;
+
+ case 'F': /* File Information */
+ id3 = nid3;
+ *flags |= WPS_REFRESH_DYNAMIC;
+ if(!id3)
+ return NULL; /* no such info (yet) */
+ /* fall-through */
+ case 'f': /* File Information */
+ *flags |= WPS_REFRESH_STATIC;
+ switch(tag[1])
+ {
+ case 'v': /* VBR file? */
+ return id3->vbr ? "(avg)" : NULL;
+
+ case 'b': /* File Bitrate */
+ if(id3->bitrate)
+ snprintf(buf, buf_size, "%d", id3->bitrate);
+ else
+ snprintf(buf, buf_size, "?");
+ return buf;
+
+ case 'f': /* File Frequency */
+ snprintf(buf, buf_size, "%ld", id3->frequency);
+ return buf;
+
+ case 'p': /* File Path */
+ return id3->path;
+
+ case 'm': /* File Name - With Extension */
+ return get_dir(buf, buf_size, id3->path, 0);
+
+ case 'n': /* File Name */
+ if (get_dir(buf, buf_size, id3->path, 0))
+ {
+ /* Remove extension */
+ char* sep = strrchr(buf, '.');
+
+ if (NULL != sep)
+ {
+ *sep = 0;
+ }
+
+ return buf;
+ }
+ else
+ {
+ return NULL;
+ }
+
+ case 's': /* File Size (in kilobytes) */
+ snprintf(buf, buf_size, "%ld", id3->filesize / 1024);
+ return buf;
+
+ case 'c': /* File Codec */
+ if(id3->codectype == AFMT_UNKNOWN)
+ *intval = AFMT_NUM_CODECS;
+ else
+ *intval = id3->codectype;
+ return id3_get_codec(id3);
+ }
+ break;
+
+ case 'p': /* Playlist/Song Information */
+ switch(tag[1])
+ {
+ case 'b': /* progress bar */
+ *flags |= WPS_REFRESH_PLAYER_PROGRESS;
+#ifdef HAVE_LCD_CHARCELLS
+ snprintf(buf, buf_size, "%c", wps_data->wps_progress_pat[0]);
+ wps_data->full_line_progressbar=0;
+ return buf;
+#else
+ return "\x01";
+#endif
+ case 'f': /* full-line progress bar */
+#ifdef HAVE_LCD_CHARCELLS
+ if(is_new_player()) {
+ *flags |= WPS_REFRESH_PLAYER_PROGRESS;
+ *flags |= WPS_REFRESH_DYNAMIC;
+ wps_data->full_line_progressbar=1;
+ /* we need 11 characters (full line) for
+ progress-bar */
+ snprintf(buf, buf_size, " ");
+ }
+ else
+ {
+ /* Tell the user if we have an OldPlayer */
+ snprintf(buf, buf_size, " <Old LCD> ");
+ }
+ return buf;
+#endif
+ case 'p': /* Playlist Position */
+ *flags |= WPS_REFRESH_STATIC;
+ snprintf(buf, buf_size, "%d", playlist_get_display_index());
+ return buf;
+
+ case 'n': /* Playlist Name (without path) */
+ *flags |= WPS_REFRESH_STATIC;
+ return playlist_name(NULL, buf, buf_size);
+
+ case 'e': /* Playlist Total Entries */
+ *flags |= WPS_REFRESH_STATIC;
+ snprintf(buf, buf_size, "%d", playlist_amount());
+ return buf;
+
+ case 'c': /* Current Time in Song */
+ *flags |= WPS_REFRESH_DYNAMIC;
+ gui_wps_format_time(buf, buf_size,
+ id3->elapsed + wps_state.ff_rewind_count);
+ return buf;
+
+ case 'r': /* Remaining Time in Song */
+ *flags |= WPS_REFRESH_DYNAMIC;
+ gui_wps_format_time(buf, buf_size,
+ id3->length - id3->elapsed - wps_state.ff_rewind_count);
+ return buf;
+
+ case 't': /* Total Time */
+ *flags |= WPS_REFRESH_STATIC;
+ gui_wps_format_time(buf, buf_size, id3->length);
+ return buf;
+
+#ifdef HAVE_LCD_BITMAP
+ case 'm': /* Peak Meter */
+ *flags |= WPS_REFRESH_PEAK_METER;
+ return "\x01";
+#endif
+ case 's': /* shuffle */
+ *flags |= WPS_REFRESH_DYNAMIC;
+ if ( global_settings.playlist_shuffle )
+ return "s";
+ else
+ return NULL;
+ break;
+
+ case 'v': /* volume */
+ *flags |= WPS_REFRESH_DYNAMIC;
+ snprintf(buf, buf_size, "%d%%", global_settings.volume);
+ *intval = global_settings.volume / 10 + 1;
+ return buf;
+
+ }
+ break;
+
+ case 'm':
+ switch (tag[1])
+ {
+ case 'm': /* playback repeat mode */
+ *flags |= WPS_REFRESH_DYNAMIC;
+ *intval = global_settings.repeat_mode + 1;
+ snprintf(buf, buf_size, "%d", *intval);
+ return buf;
+
+ /* playback status */
+ case 'p': /* play */
+ *flags |= WPS_REFRESH_DYNAMIC;
+ int status = audio_status();
+ *intval = 1;
+ if (status == AUDIO_STATUS_PLAY && \
+ !(status & AUDIO_STATUS_PAUSE))
+ *intval = 2;
+ if (audio_status() & AUDIO_STATUS_PAUSE && \
+ (! status_get_ffmode()))
+ *intval = 3;
+ if (status_get_ffmode() == STATUS_FASTFORWARD)
+ *intval = 4;
+ if (status_get_ffmode() == STATUS_FASTBACKWARD)
+ *intval = 5;
+ snprintf(buf, buf_size, "%d", *intval);
+ return buf;
+
+#if CONFIG_KEYPAD == IRIVER_H100_PAD
+ case 'h': /* hold */
+ *flags |= WPS_REFRESH_DYNAMIC;
+ if (button_hold())
+ return "h";
+ else
+ return NULL;
+ case 'r': /* remote hold */
+ *flags |= WPS_REFRESH_DYNAMIC;
+ if (remote_button_hold())
+ return "r";
+ else
+ return NULL;
+#endif
+ }
+ break;
+
+ case 'b': /* battery info */
+ *flags |= WPS_REFRESH_DYNAMIC;
+ switch (tag[1]) {
+ case 'l': /* battery level */
+ {
+ int l = battery_level();
+ if (l > -1)
+ {
+ snprintf(buf, buf_size, "%d%%", l);
+ *intval = l / 20 + 1;
+ }
+ else
+ {
+ *intval = 6;
+ return "?%";
+ }
+ return buf;
+ }
+
+ case 't': /* estimated battery time */
+ {
+ int t = battery_time();
+ if (t >= 0)
+ snprintf(buf, buf_size, "%dh %dm", t / 60, t % 60);
+ else
+ strncpy(buf, "?h ?m", buf_size);
+ return buf;
+ }
+
+ case 'p': /* External power plugged in? */
+ {
+ if(charger_inserted())
+ return "p";
+ else
+ return NULL;
+ }
+ }
+ break;
+
+ case 'D': /* Directory path information */
+ id3 = nid3; /* next song please! */
+ *flags |= WPS_REFRESH_DYNAMIC;
+ if(!id3)
+ return NULL; /* no such info (yet) */
+ /* fall-through */
+ case 'd': /* Directory path information */
+ {
+ int level = tag[1] - '0';
+ *flags |= WPS_REFRESH_STATIC;
+ /* d1 through d9 */
+ if ((0 < level) && (9 > level))
+ {
+ return get_dir(buf, buf_size, id3->path, level);
+ }
+ }
+ break;
+
+ case 't': /* set sub line time multiplier */
+ {
+ int d = 1;
+ int time_mult = 0;
+ bool have_point = false;
+ bool have_tenth = false;
+
+ while (((tag[d] >= '0') &&
+ (tag[d] <= '9')) ||
+ (tag[d] == '.'))
+ {
+ if (tag[d] != '.')
+ {
+ time_mult = time_mult * 10;
+ time_mult = time_mult + tag[d] - '0';
+ if (have_point)
+ {
+ have_tenth = true;
+ d++;
+ break;
+ }
+ }
+ else
+ {
+ have_point = true;
+ }
+ d++;
+ }
+
+ if (have_tenth == false)
+ time_mult *= 10;
+
+ *subline_time_mult = time_mult;
+ *tag_len = d;
+
+ buf[0] = 0;
+ return buf;
+ }
+ break;
+ case 'r': /* Runtime database Information */
+ switch(tag[1])
+ {
+ case 'p': /* Playcount */
+ *flags |= WPS_REFRESH_STATIC;
+ snprintf(buf, buf_size, "%ld", cid3->playcount);
+ return buf;
+ case 'r': /* Rating */
+ *flags |= WPS_REFRESH_STATIC;
+ *intval = cid3->rating+1;
+ snprintf(buf, buf_size, "%d", cid3->rating);
+ return buf;
+ }
+ break;
+ }
+ return NULL;
+}
+
+#ifdef HAVE_LCD_BITMAP
+/* clears the area where the image was shown */
+static void clear_image_pos(struct gui_wps *gwps, int n)
+{
+ if(!gwps) return;
+ struct wps_data *data = gwps->data;
+ gwps->display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
+ gwps->display->fillrect(data->img[n].x, data->img[n].y,
+ data->img[n].w, data->img[n].h);
+ gwps->display->set_drawmode(DRMODE_SOLID);
+}
+#endif
+/* Skip to the end of the current %? conditional.
+ *
+ * fmt - string to skip it. Should point to somewhere after the leading
+ * "<" char (and before or at the last ">").
+ * num - number of |'s to skip, or 0 to skip to the end (the ">").
+ *
+ * Returns the new position in fmt.
+ */
+static const char* skip_conditional(struct gui_wps *gwps, const char* fmt, int num)
+{
+ int level = 1;
+ int count = num;
+ const char *last_alternative = NULL;
+#ifdef HAVE_LCD_BITMAP
+ struct wps_data *data = NULL;
+ if(gwps) data = gwps->data;
+ int last_x=-1, last_y=-1, last_w=-1, last_h=-1;
+#else
+ (void)gwps;
+#endif
+ while (*fmt)
+ {
+ switch (*fmt++)
+ {
+ case '%':
+#ifdef HAVE_LCD_BITMAP
+ if(data && *(fmt) == 'x' && *(fmt+1) == 'd' )
+ {
+ fmt +=2;
+ int n = *fmt;
+ if(n >= 'a' && n <= 'z')
+ n -= 'a';
+ if(n >= 'A' && n <= 'Z')
+ n = n - 'A' + 26;
+ if(last_x != data->img[n].x || last_y != data->img[n].y
+ || last_w != data->img[n].w || last_h != data->img[n].h)
+ {
+ last_x = data->img[n].x;
+ last_y = data->img[n].y;
+ last_w = data->img[n].w;
+ last_h = data->img[n].h;
+ clear_image_pos(gwps,n);
+ }
+ }
+#endif
+ break;
+
+ case '|':
+ if(1 == level) {
+ last_alternative = fmt;
+ if(num) {
+ count--;
+ if(count == 0)
+ return fmt;
+ continue;
+ }
+ }
+ continue;
+
+ case '>':
+ if (0 == --level)
+ {
+ /* We're just skipping to the end */
+ if(num == 0)
+ return fmt;
+
+ /* If we are parsing an enum, we'll return the selected
+ item. If there weren't enough items in the enum, we'll
+ return the last one found. */
+ if(count && last_alternative)
+ {
+ return last_alternative;
+ }
+ return fmt - 1;
+ }
+ continue;
+
+ default:
+ continue;
+ }
+
+ switch (*fmt++)
+ {
+ case 0:
+ case '%':
+ case '|':
+ case '<':
+ case '>':
+ break;
+
+ case '?':
+ while (*fmt && ('<' != *fmt))
+ fmt++;
+
+ if ('<' == *fmt)
+ fmt++;
+
+ level++;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return fmt;
+}
+
+/* Generate the display based on id3 information and format string.
+ *
+ * buf - char buffer to write the display to.
+ * buf_size - the size of buffer.
+ * id3 - the ID3 data to format with.
+ * nid3 - the ID3 data of the next song (might by NULL)
+ * fmt - format description.
+ * flags - returns the type of the line. See constants i wps-display.h
+ */
+static void format_display(struct gui_wps *gwps, char* buf,
+ int buf_size,
+ struct mp3entry* id3,
+ struct mp3entry* nid3, /* next song's id3 */
+ const char* fmt,
+ struct align_pos* align,
+ unsigned short* subline_time_mult,
+ unsigned char* flags)
+{
+ char temp_buf[128];
+ char* buf_start = buf;
+ char* buf_end = buf + buf_size - 1; /* Leave room for end null */
+ char* value = NULL;
+ int level = 0;
+ unsigned char tag_length;
+ int intval;
+ int cur_align;
+ char* cur_align_start;
+#ifdef HAVE_LCD_BITMAP
+ struct gui_img *img = gwps->data->img;
+ int n;
+#endif
+
+ cur_align_start = buf;
+ cur_align = WPS_ALIGN_LEFT;
+ *subline_time_mult = DEFAULT_SUBLINE_TIME_MULTIPLIER;
+
+ align->left = 0;
+ align->center = 0;
+ align->right = 0;
+
+ while (fmt && *fmt && buf < buf_end)
+ {
+ switch (*fmt)
+ {
+ case '%':
+ ++fmt;
+ break;
+
+ case '|':
+ case '>':
+ if (level > 0)
+ {
+ fmt = skip_conditional(NULL,fmt, 0);
+ level--;
+ continue;
+ }
+ /* Else fall through */
+
+ default:
+ *buf++ = *fmt++;
+ continue;
+ }
+
+ switch (*fmt)
+ {
+ case 0:
+ *buf++ = '%';
+ break;
+ case 'a':
+ ++fmt;
+ /* remember where the current aligned text started */
+ switch (cur_align)
+ {
+ case WPS_ALIGN_LEFT:
+ align->left = cur_align_start;
+ break;
+
+ case WPS_ALIGN_CENTER:
+ align->center = cur_align_start;
+ break;
+
+ case WPS_ALIGN_RIGHT:
+ align->right = cur_align_start;
+ break;
+ }
+ /* start a new alignment */
+ switch (*fmt)
+ {
+ case 'l':
+ cur_align = WPS_ALIGN_LEFT;
+ break;
+ case 'c':
+ cur_align = WPS_ALIGN_CENTER;
+ break;
+ case 'r':
+ cur_align = WPS_ALIGN_RIGHT;
+ break;
+ }
+ *buf++=0;
+ cur_align_start = buf;
+ ++fmt;
+ break;
+ case 's':
+ *flags |= WPS_REFRESH_SCROLL;
+ ++fmt;
+ break;
+
+ case 'x': /* image support */
+#ifdef HAVE_LCD_BITMAP
+ /* skip preload or regular image tag */
+ if ('l' == *(fmt+1) || '|' == *(fmt+1))
+ {
+ while (*fmt && *fmt != '\n')
+ fmt++;
+ }
+ else if ('d' == *(fmt+1) )
+ {
+ fmt+=2;
+
+ /* get the image ID */
+ n = *fmt;
+ if(n >= 'a' && n <= 'z')
+ n -= 'a';
+ if(n >= 'A' && n <= 'Z')
+ n = n - 'A' + 26;
+ if (n >= 0 && n < MAX_IMAGES && img[n].loaded) {
+ img[n].display = true;
+ }
+ }
+
+#endif
+ fmt++;
+ break;
+
+
+ case '%':
+ case '|':
+ case '<':
+ case '>':
+ case ';':
+ *buf++ = *fmt++;
+ break;
+
+ case '?':
+ fmt++;
+ value = get_tag(gwps->data, id3, nid3, fmt, temp_buf, sizeof(temp_buf),
+ &tag_length, subline_time_mult, flags,
+ &intval);
+
+ while (*fmt && ('<' != *fmt))
+ fmt++;
+
+ if ('<' == *fmt)
+ fmt++;
+
+ /* No value, so skip to else part, using a sufficiently high
+ value to "hit" the last part of the conditional */
+ if ((!value) || (!strlen(value)))
+ fmt = skip_conditional(gwps, fmt, 1000);
+ else
+ if(intval > 1) /* enum */
+ fmt = skip_conditional(gwps, fmt, intval - 1);
+
+ level++;
+ break;
+
+ default:
+ value = get_tag(gwps->data, id3, nid3, fmt, temp_buf, sizeof(temp_buf),
+ &tag_length, subline_time_mult, flags,
+ &intval);
+ fmt += tag_length;
+
+ if (value)
+ {
+ while (*value && (buf < buf_end))
+ *buf++ = *value++;
+ }
+ }
+ }
+
+ /* remember where the current aligned text started */
+ switch (cur_align)
+ {
+ case WPS_ALIGN_LEFT:
+ align->left = cur_align_start;
+ break;
+
+ case WPS_ALIGN_CENTER:
+ align->center = cur_align_start;
+ break;
+
+ case WPS_ALIGN_RIGHT:
+ align->right = cur_align_start;
+ break;
+ }
+
+ *buf = 0;
+
+ /* if resulting line is an empty line, set the subline time to 0 */
+ if (buf - buf_start == 0)
+ *subline_time_mult = 0;
+
+ /* If no flags have been set, the line didn't contain any format codes.
+ We still want to refresh it. */
+ if(*flags == 0)
+ *flags = WPS_REFRESH_STATIC;
+}
+
+/* fades the volume */
+void fade(bool fade_in)
+{
+ unsigned fp_global_vol = global_settings.volume << 8;
+ unsigned fp_step = fp_global_vol / 30;
+
+ if (fade_in) {
+ /* fade in */
+ unsigned fp_volume = 0;
+
+ /* zero out the sound */
+ sound_set_volume(0);
+
+ sleep(HZ/10); /* let audio thread run */
+ audio_resume();
+
+ while (fp_volume < fp_global_vol) {
+ fp_volume += fp_step;
+ sleep(1);
+ sound_set_volume(fp_volume >> 8);
+ }
+ sound_set_volume(global_settings.volume);
+ }
+ else {
+ /* fade out */
+ unsigned fp_volume = fp_global_vol;
+
+ while (fp_volume > fp_step) {
+ fp_volume -= fp_step;
+ sleep(1);
+ sound_set_volume(fp_volume >> 8);
+ }
+ audio_pause();
+#ifndef SIMULATOR
+ /* let audio thread run and wait for the mas to run out of data */
+ while (!mp3_pause_done())
+#endif
+ sleep(HZ/10);
+
+ /* reset volume to what it was before the fade */
+ sound_set_volume(global_settings.volume);
+ }
+}
+
+/* Set format string to use for WPS, splitting it into lines */
+void gui_wps_format(struct wps_data *data, const char *bmpdir, size_t bmpdirlen)
+{
+ if(!data) return;
+ char* buf = data->format_buffer;
+ char* start_of_line = data->format_buffer;
+ int line = 0;
+ int subline;
+#ifndef HAVE_LCD_BITMAP
+ /* no bitmap lcd == no bitmap loading */
+ (void)bmpdir;
+ (void)bmpdirlen;
+#else
+ unsigned char* img_buf_ptr = data->img_buf; /* where are in image buffer? */
+
+ int img_buf_free = IMG_BUFSIZE; /* free space in image buffer */
+#endif
+ for (line=0; line<WPS_MAX_LINES; line++)
+ {
+ for (subline=0; subline<WPS_MAX_SUBLINES; subline++)
+ {
+ data->format_lines[line][subline] = 0;
+ data->time_mult[line][subline] = 0;
+ }
+ data->subline_expire_time[line] = 0;
+ data->curr_subline[line] = SUBLINE_RESET;
+ }
+
+ line = 0;
+ subline = 0;
+ data->format_lines[line][subline] = buf;
+
+ while ((*buf) && (line < WPS_MAX_LINES))
+ {
+ switch (*buf)
+ {
+ /*
+ * skip % sequences so "%;" doesn't start a new subline
+ * don't skip %x lines (pre-load bitmaps)
+ */
+ case '%':
+ if (*(buf+1) != 'x')
+ buf++;
+ break;
+
+ case '\r': /* CR */
+ *buf = 0;
+ break;
+
+ case '\n': /* LF */
+ *buf = 0;
+
+ if (*start_of_line != '#') /* A comment? */
+ line++;
+
+ if (line < WPS_MAX_LINES)
+ {
+ /* the next line starts on the next byte */
+ subline = 0;
+ data->format_lines[line][subline] = buf+1;
+ start_of_line = data->format_lines[line][subline];
+ }
+ break;
+
+ case ';': /* start a new subline */
+ *buf = 0;
+ subline++;
+ if (subline < WPS_MAX_SUBLINES)
+ {
+ data->format_lines[line][subline] = buf+1;
+ }
+ else /* exceeded max sublines, skip rest of line */
+ {
+ while (*(++buf))
+ {
+ if ((*buf == '\r') || (*buf == '\n'))
+ {
+ break;
+ }
+ }
+ buf--;
+ subline = 0;
+ }
+ break;
+
+ case 'x':
+#ifdef HAVE_LCD_BITMAP
+ /* Preload images so the %xd# tag can display it */
+ {
+ int ret = 0;
+ int n;
+ char *ptr = buf+1;
+ char *pos = NULL;
+ char imgname[MAX_PATH];
+ char qual = *ptr;
+ if (qual == 'l' || qual == '|') /* format:
+ %x|n|filename.bmp|x|y|
+ or
+ %xl|n|filename.bmp|x|y|
+ */
+ {
+ ptr = strchr(ptr, '|') + 1;
+ pos = strchr(ptr, '|');
+ if (pos)
+ {
+ /* get the image ID */
+ n = *ptr;
+ if(n >= 'a' && n <= 'z')
+ n -= 'a';
+ if(n >= 'A' && n <= 'Z')
+ n = n - 'A' + 26;
+
+ if(n < 0 || n >= MAX_IMAGES)
+ {
+ /* Skip the rest of the line */
+ while(*buf != '\n')
+ buf++;
+ break;
+ }
+ ptr = pos+1;
+
+ /* check the image number and load state */
+ if (!data->img[n].loaded)
+ {
+ /* get filename */
+ pos = strchr(ptr, '|');
+ if ((pos - ptr) <
+ (int)sizeof(imgname)-ROCKBOX_DIR_LEN-2)
+ {
+ memcpy(imgname, bmpdir, bmpdirlen);
+ imgname[bmpdirlen] = '/';
+ memcpy(&imgname[bmpdirlen+1],
+ ptr, pos - ptr);
+ imgname[bmpdirlen+1+pos-ptr] = 0;
+ }
+ else
+ /* filename too long */
+ imgname[0] = 0;
+
+ ptr = pos+1;
+
+ /* get x-position */
+ pos = strchr(ptr, '|');
+ if (pos)
+ data->img[n].x = atoi(ptr);
+ else
+ {
+ /* weird syntax, bail out */
+ buf++;
+ break;
+ }
+
+ /* get y-position */
+ ptr = pos+1;
+ pos = strchr(ptr, '|');
+ if (pos)
+ data->img[n].y = atoi(ptr);
+ else
+ {
+ /* weird syntax, bail out */
+ buf++;
+ break;
+ }
+
+ pos++;
+
+ /* reposition buf pointer to next WPS element */
+ while (*pos && *pos != ';' &&
+ *pos != '\r' && *pos != '\n')
+ pos++;
+
+ buf = pos;
+
+ /* load the image */
+ ret = read_bmp_file(imgname, &data->img[n].w,
+ &data->img[n].h, img_buf_ptr,
+ img_buf_free);
+ if (ret > 0)
+ {
+ data->img[n].ptr = img_buf_ptr;
+ img_buf_ptr += ret;
+ img_buf_free -= ret;
+ data->img[n].loaded = true;
+ if(qual == '|')
+ data->img[n].always_display = true;
+ }
+ }
+ buf++;
+ }
+ }
+ }
+#endif
+ break;
+ }
+ buf++;
+ }
+}
+
+#ifdef HAVE_LCD_BITMAP
+/* Display images */
+static void wps_display_images(struct gui_wps *gwps)
+{
+ if(!gwps || !gwps->data || !gwps->display) return;
+ int n;
+ struct wps_data *data = gwps->data;
+ struct screen *display = gwps->display;
+ for (n = 0; n < MAX_IMAGES; n++) {
+ if (data->img[n].loaded && data->img[n].display) {
+ if(data->img[n].always_display)
+ display->set_drawmode(DRMODE_FG);
+ else
+ display->set_drawmode(DRMODE_SOLID);
+
+ display->mono_bitmap(data->img[n].ptr, data->img[n].x,
+ data->img[n].y, data->img[n].w, data->img[n].h);
+ display->update_rect(data->img[n].x, data->img[n].y,
+ data->img[n].w, data->img[n].h);
+ }
+ }
+ display->set_drawmode(DRMODE_SOLID);
+}
+#endif
+
+void gui_wps_reset(struct gui_wps *gui_wps)
+{
+ if(!gui_wps || !gui_wps->data) return;
+ gui_wps->data->wps_loaded = false;
+ memset(&gui_wps->data->format_buffer, 0, sizeof (gui_wps->data->format_buffer));
+}
+
+bool gui_wps_refresh(struct gui_wps *gwps, int ffwd_offset,
+ unsigned char refresh_mode)
+{
+ char buf[MAX_PATH];
+ unsigned char flags;
+ int i;
+ bool update_line;
+ bool only_one_subline;
+ bool new_subline_refresh;
+ int search;
+ int search_start;
+ struct wps_data *data = gwps->data;
+ struct wps_state *state = gwps->state;
+ struct screen *display = gwps->display;
+ if(!gwps || !data || !state || !display){
+ return false;
+ }
+#ifdef HAVE_LCD_BITMAP
+ int h = font_get(FONT_UI)->height;
+ int offset = global_settings.statusbar ? STATUSBAR_HEIGHT : 0;
+ /* to find out wether the peak meter is enabled we
+ assume it wasn't until we find a line that contains
+ the peak meter. We can't use peak_meter_enabled itself
+ because that would mean to turn off the meter thread
+ temporarily. (That shouldn't matter unless yield
+ or sleep is called but who knows...)
+ */
+ bool enable_pm = false;
+
+ /* Set images to not to be displayed */
+ for (i = 0; i < MAX_IMAGES; i++) {
+ data->img[i].display = false;
+ }
+#endif
+ /* reset to first subline if refresh all flag is set */
+ if (refresh_mode == WPS_REFRESH_ALL)
+ {
+ for (i=0; i<WPS_MAX_LINES; i++)
+ {
+ data->curr_subline[i] = SUBLINE_RESET;
+ }
+ }
+
+#ifdef HAVE_LCD_CHARCELLS
+ for (i=0; i<8; i++) {
+ if (data->wps_progress_pat[i]==0)
+ data->wps_progress_pat[i]=display->get_locked_pattern();
+ }
+#endif
+
+ if (!state->id3)
+ {
+ display->stop_scroll();
+ return false;
+ }
+
+ state->ff_rewind_count = ffwd_offset;
+
+ for (i = 0; i < WPS_MAX_LINES; i++)
+ {
+ new_subline_refresh = false;
+ only_one_subline = false;
+
+ /* if time to advance to next sub-line */
+ if (TIME_AFTER(current_tick, data->subline_expire_time[i] - 1) ||
+ (data->curr_subline[i] == SUBLINE_RESET))
+ {
+ /* search all sublines until the next subline with time > 0
+ is found or we get back to the subline we started with */
+ if (data->curr_subline[i] == SUBLINE_RESET)
+ search_start = 0;
+ else
+ search_start = data->curr_subline[i];
+ for (search=0; search<WPS_MAX_SUBLINES; search++)
+ {
+ data->curr_subline[i]++;
+
+ /* wrap around if beyond last defined subline or WPS_MAX_SUBLINES */
+ if ((!data->format_lines[i][data->curr_subline[i]]) ||
+ (data->curr_subline[i] == WPS_MAX_SUBLINES))
+ {
+ if (data->curr_subline[i] == 1)
+ only_one_subline = true;
+ data->curr_subline[i] = 0;
+ }
+
+ /* if back where we started after search or
+ only one subline is defined on the line */
+ if (((search > 0) && (data->curr_subline[i] == search_start)) ||
+ only_one_subline)
+ {
+ /* no other subline with a time > 0 exists */
+ data->subline_expire_time[i] = current_tick + 100 * HZ;
+ break;
+ }
+ else
+ {
+ /* get initial time multiplier and
+ line type flags for this subline */
+ format_display(gwps, buf, sizeof(buf),
+ state->id3, state->nid3,
+ data->format_lines[i][data->curr_subline[i]],
+ &data->format_align[i][data->curr_subline[i]],
+ &data->time_mult[i][data->curr_subline[i]],
+ &data->line_type[i][data->curr_subline[i]]);
+
+ /* only use this subline if subline time > 0 */
+ if (data->time_mult[i][data->curr_subline[i]] > 0)
+ {
+ new_subline_refresh = true;
+ data->subline_expire_time[i] = current_tick +
+ BASE_SUBLINE_TIME * data->time_mult[i][data->curr_subline[i]];
+ break;
+ }
+ }
+ }
+
+ }
+
+ update_line = false;
+
+ if ( !data->format_lines[i][data->curr_subline[i]] )
+ break;
+
+ if ((data->line_type[i][data->curr_subline[i]] & refresh_mode) ||
+ (refresh_mode == WPS_REFRESH_ALL) ||
+ new_subline_refresh)
+ {
+ flags = 0;
+#ifdef HAVE_LCD_BITMAP
+ int left_width, left_xpos;
+ int center_width, center_xpos;
+ int right_width, right_xpos;
+ int space_width;
+ int string_height;
+ int ypos;
+#endif
+
+ format_display(gwps, buf, sizeof(buf),
+ state->id3, state->nid3,
+ data->format_lines[i][data->curr_subline[i]],
+ &data->format_align[i][data->curr_subline[i]],
+ &data->time_mult[i][data->curr_subline[i]],
+ &flags);
+ data->line_type[i][data->curr_subline[i]] = flags;
+
+#ifdef HAVE_LCD_BITMAP
+ /* progress */
+ if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
+ {
+#define PROGRESS_BAR_HEIGHT 6 /* this should probably be defined elsewhere; config-*.h perhaps? */
+ int sby = i*h + offset + (h > 7 ? (h - 6) / 2 : 1);
+ gui_scrollbar_draw(display, 0, sby, display->width, PROGRESS_BAR_HEIGHT,
+ state->id3->length?state->id3->length:1, 0,
+ state->id3->length?state->id3->elapsed + state->ff_rewind_count:0,
+ HORIZONTAL);
+#ifdef AB_REPEAT_ENABLE
+ if ( ab_repeat_mode_enabled() )
+ ab_draw_markers(state->id3->length, 0, sby, LCD_WIDTH, PROGRESS_BAR_HEIGHT);
+#endif
+ update_line = true;
+ }
+ if (flags & refresh_mode & WPS_REFRESH_PEAK_METER && display->height >= LCD_HEIGHT) {
+ /* peak meter */
+ int peak_meter_y;
+
+ update_line = true;
+ peak_meter_y = i * h + offset;
+
+ /* The user might decide to have the peak meter in the last
+ line so that it is only displayed if no status bar is
+ visible. If so we neither want do draw nor enable the
+ peak meter. */
+ if (peak_meter_y + h <= LCD_HEIGHT) {
+ /* found a line with a peak meter -> remember that we must
+ enable it later */
+ enable_pm = true;
+ peak_meter_draw(0, peak_meter_y, LCD_WIDTH,
+ MIN(h, LCD_HEIGHT - peak_meter_y));
+ }
+ }
+#else
+ /* progress */
+ if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS) {
+ if (data->full_line_progressbar)
+ draw_player_fullbar(gwps, buf, sizeof(buf));
+ else
+ draw_player_progress(gwps);
+ }
+#endif
+#ifdef HAVE_LCD_BITMAP
+ /* calculate different string sizes and positions */
+ display->getstringsize(" ", &space_width, &string_height);
+ if (data->format_align[i][data->curr_subline[i]].left != 0) {
+ display->getstringsize(data->format_align[i][data->curr_subline[i]].left,
+ &left_width, &string_height);
+ }
+ else {
+ left_width = 0;
+ }
+ left_xpos = 0;
+
+ if (data->format_align[i][data->curr_subline[i]].center != 0) {
+ display->getstringsize(data->format_align[i][data->curr_subline[i]].center,
+ &center_width, &string_height);
+ }
+ else {
+ center_width = 0;
+ }
+ center_xpos=(display->width - center_width) / 2;
+
+ if (data->format_align[i][data->curr_subline[i]].right != 0) {
+ display->getstringsize(data->format_align[i][data->curr_subline[i]].right,
+ &right_width, &string_height);
+ }
+ else {
+ right_width = 0;
+ }
+ right_xpos = (display->width - right_width);
+
+ /* Checks for overlapping strings.
+ If needed the overlapping strings will be merged, separated by a
+ space */
+
+ /* CASE 1: left and centered string overlap */
+ /* there is a left string, need to merge left and center */
+ if ((left_width != 0 && center_width != 0) &&
+ (left_xpos + left_width + space_width > center_xpos)) {
+ /* replace the former separator '\0' of left and
+ center string with a space */
+ *(--data->format_align[i][data->curr_subline[i]].center) = ' ';
+ /* calculate the new width and position of the merged string */
+ left_width = left_width + space_width + center_width;
+ left_xpos = 0;
+ /* there is no centered string anymore */
+ center_width = 0;
+ }
+ /* there is no left string, move center to left */
+ if ((left_width == 0 && center_width != 0) &&
+ (left_xpos + left_width > center_xpos)) {
+ /* move the center string to the left string */
+ data->format_align[i][data->curr_subline[i]].left =
+ data->format_align[i][data->curr_subline[i]].center;
+ /* calculate the new width and position of the string */
+ left_width = center_width;
+ left_xpos = 0;
+ /* there is no centered string anymore */
+ center_width = 0;
+ }
+
+ /* CASE 2: centered and right string overlap */
+ /* there is a right string, need to merge center and right */
+ if ((center_width != 0 && right_width != 0) &&
+ (center_xpos + center_width + space_width > right_xpos)) {
+ /* replace the former separator '\0' of center and
+ right string with a space */
+ *(--data->format_align[i][data->curr_subline[i]].right) = ' ';
+ /* move the center string to the right after merge */
+ data->format_align[i][data->curr_subline[i]].right =
+ data->format_align[i][data->curr_subline[i]].center;
+ /* calculate the new width and position of the merged string */
+ right_width = center_width + space_width + right_width;
+ right_xpos = (display->width - right_width);
+ /* there is no centered string anymore */
+ center_width = 0;
+ }
+ /* there is no right string, move center to right */
+ if ((center_width != 0 && right_width == 0) &&
+ (center_xpos + center_width > right_xpos)) {
+ /* move the center string to the right string */
+ data->format_align[i][data->curr_subline[i]].right =
+ data->format_align[i][data->curr_subline[i]].center;
+ /* calculate the new width and position of the string */
+ right_width = center_width;
+ right_xpos = (display->width - right_width);
+ /* there is no centered string anymore */
+ center_width = 0;
+ }
+
+ /* CASE 3: left and right overlap
+ There is no center string anymore, either there never
+ was one or it has been merged in case 1 or 2 */
+ /* there is a left string, need to merge left and right */
+ if ((left_width != 0 && center_width == 0 && right_width != 0) &&
+ (left_xpos + left_width + space_width > right_xpos)) {
+ /* replace the former separator '\0' of left and
+ right string with a space */
+ *(--data->format_align[i][data->curr_subline[i]].right) = ' ';
+ /* calculate the new width and position of the string */
+ left_width = left_width + space_width + right_width;
+ left_xpos = 0;
+ /* there is no right string anymore */
+ right_width = 0;
+ }
+ /* there is no left string, move right to left */
+ if ((left_width == 0 && center_width == 0 && right_width != 0) &&
+ (left_xpos + left_width > right_xpos)) {
+ /* move the right string to the left string */
+ data->format_align[i][data->curr_subline[i]].left =
+ data->format_align[i][data->curr_subline[i]].right;
+ /* calculate the new width and position of the string */
+ left_width = right_width;
+ left_xpos = 0;
+ /* there is no right string anymore */
+ right_width = 0;
+ }
+
+#endif
+
+ if (flags & WPS_REFRESH_SCROLL) {
+
+ /* scroll line */
+ if ((refresh_mode & WPS_REFRESH_SCROLL) ||
+ new_subline_refresh) {
+#ifdef HAVE_LCD_BITMAP
+ ypos = (i*string_height)+display->getymargin();
+ update_line = true;
+
+ if (left_width>display->width) {
+ display->puts_scroll(0, i,
+ data->format_align[i][data->curr_subline[i]].left);
+ } else {
+ /* clear the line first */
+ display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
+ display->fillrect(0, ypos, display->width, string_height);
+ display->set_drawmode(DRMODE_SOLID);
+
+ /* Nasty hack: we output an empty scrolling string,
+ which will reset the scroller for that line */
+ display->puts_scroll(0, i, "");
+
+ /* print aligned strings */
+ if (left_width != 0)
+ {
+ display->putsxy(left_xpos, ypos,
+ data->format_align[i][data->curr_subline[i]].left);
+ }
+ if (center_width != 0)
+ {
+ display->putsxy(center_xpos, ypos,
+ data->format_align[i][data->curr_subline[i]].center);
+ }
+ if (right_width != 0)
+ {
+ display->putsxy(right_xpos, ypos,
+ data->format_align[i][data->curr_subline[i]].right);
+ }
+ }
+#else
+ display->puts_scroll(0, i, buf);
+ update_line = true;
+#endif
+ }
+ }
+ else if (flags & (WPS_REFRESH_DYNAMIC | WPS_REFRESH_STATIC))
+ {
+ /* dynamic / static line */
+ if ((refresh_mode & (WPS_REFRESH_DYNAMIC|WPS_REFRESH_STATIC)) ||
+ new_subline_refresh)
+ {
+#ifdef HAVE_LCD_BITMAP
+ ypos = (i*string_height)+display->getymargin();
+ update_line = true;
+
+ /* clear the line first */
+ display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
+ display->fillrect(0, ypos, display->width, string_height);
+ display->set_drawmode(DRMODE_SOLID);
+
+ /* Nasty hack: we output an empty scrolling string,
+ which will reset the scroller for that line */
+ display->puts_scroll(0, i, "");
+
+ /* print aligned strings */
+ if (left_width != 0)
+ {
+ display->putsxy(left_xpos, ypos,
+ data->format_align[i][data->curr_subline[i]].left);
+ }
+ if (center_width != 0)
+ {
+ display->putsxy(center_xpos, ypos,
+ data->format_align[i][data->curr_subline[i]].center);
+ }
+ if (right_width != 0)
+ {
+ display->putsxy(right_xpos, ypos,
+ data->format_align[i][data->curr_subline[i]].right);
+ }
+#else
+ update_line = true;
+ display->puts(0, i, buf);
+#endif
+ }
+ }
+ }
+#ifdef HAVE_LCD_BITMAP
+ if (update_line) {
+ display->update_rect(0, i*h + offset, display->width, h);
+ }
+#endif
+ }
+
+#ifdef HAVE_LCD_BITMAP
+ /* Display all images */
+ for (i = 0; i < MAX_IMAGES; i++) {
+ if(data->img[i].always_display)
+ data->img[i].display = data->img[i].always_display;
+ }
+ wps_display_images(gwps);
+
+ /* Now we know wether the peak meter is used.
+ So we can enable / disable the peak meter thread */
+ data->peak_meter_enabled = enable_pm;
+#endif
+
+#if defined(CONFIG_BACKLIGHT) && !defined(SIMULATOR)
+ if (global_settings.caption_backlight && state->id3) {
+ /* turn on backlight n seconds before track ends, and turn it off n
+ seconds into the new track. n == backlight_timeout, or 5s */
+ int n =
+ backlight_timeout_value[global_settings.backlight_timeout] * 1000;
+
+ if ( n < 1000 )
+ n = 5000; /* use 5s if backlight is always on or off */
+
+ if ((state->id3->elapsed < 1000) ||
+ ((state->id3->length - state->id3->elapsed) < (unsigned)n))
+ backlight_on();
+ }
+#endif
+ return true;
+}
+
+#ifdef HAVE_LCD_CHARCELLS
+static bool draw_player_progress(struct gui_wps *gwps)
+{
+ char player_progressbar[7];
+ char binline[36];
+ int songpos = 0;
+ int i,j;
+ struct wps_state *state = gwps->state;
+ if (!state->id3)
+ return false;
+
+ memset(binline, 1, sizeof binline);
+ memset(player_progressbar, 1, sizeof player_progressbar);
+
+ if(state->id3->elapsed >= state->id3->length)
+ songpos = 0;
+ else
+ {
+ if(state->wps_time_countup == false)
+ songpos = ((state->id3->elapsed - state->ff_rewind_count) * 36) /
+ state->id3->length;
+ else
+ songpos = ((state->id3->elapsed + state->ff_rewind_count) * 36) /
+ state->id3->length;
+ }
+ for (i=0; i < songpos; i++)
+ binline[i] = 0;
+
+ for (i=0; i<=6; i++) {
+ for (j=0;j<5;j++) {
+ player_progressbar[i] <<= 1;
+ player_progressbar[i] += binline[i*5+j];
+ }
+ }
+ gwps->display->define_pattern(gwps->data->wps_progress_pat[0], player_progressbar);
+ return true;
+}
+
+static char map_fullbar_char(char ascii_val)
+{
+ if (ascii_val >= '0' && ascii_val <= '9') {
+ return(ascii_val - '0');
+ }
+ else if (ascii_val == ':') {
+ return(10);
+ }
+ else
+ return(11); /* anything besides a number or ':' is mapped to <blank> */
+}
+
+static void draw_player_fullbar(struct gui_wps *gwps, char* buf, int buf_size)
+{
+ int i,j,lcd_char_pos;
+
+ char player_progressbar[7];
+ char binline[36];
+ static const char numbers[12][4][3]={
+ {{1,1,1},{1,0,1},{1,0,1},{1,1,1}},/*0*/
+ {{0,1,0},{1,1,0},{0,1,0},{0,1,0}},/*1*/
+ {{1,1,1},{0,0,1},{0,1,0},{1,1,1}},/*2*/
+ {{1,1,1},{0,0,1},{0,1,1},{1,1,1}},/*3*/
+ {{1,0,0},{1,1,0},{1,1,1},{0,1,0}},/*4*/
+ {{1,1,1},{1,1,0},{0,0,1},{1,1,0}},/*5*/
+ {{1,1,1},{1,0,0},{1,1,1},{1,1,1}},/*6*/
+ {{1,1,1},{0,0,1},{0,1,0},{1,0,0}},/*7*/
+ {{1,1,1},{1,1,1},{1,0,1},{1,1,1}},/*8*/
+ {{1,1,1},{1,1,1},{0,0,1},{1,1,1}},/*9*/
+ {{0,0,0},{0,1,0},{0,0,0},{0,1,0}},/*:*/
+ {{0,0,0},{0,0,0},{0,0,0},{0,0,0}} /*<blank>*/
+ };
+
+ int songpos = 0;
+ int digits[6];
+ int time;
+ char timestr[7];
+
+ struct wps_state *state = gwps->state;
+
+ for (i=0; i < buf_size; i++)
+ buf[i] = ' ';
+
+ if(state->id3->elapsed >= state->id3->length)
+ songpos = 55;
+ else {
+ if(state->wps_time_countup == false)
+ songpos = ((state->id3->elapsed - state->ff_rewind_count) * 55) /
+ state->id3->length;
+ else
+ songpos = ((state->id3->elapsed + state->ff_rewind_count) * 55) /
+ state->id3->length;
+ }
+
+ time=(state->id3->elapsed + state->ff_rewind_count);
+
+ memset(timestr, 0, sizeof(timestr));
+ gui_wps_format_time(timestr, sizeof(timestr), time);
+ for(lcd_char_pos=0; lcd_char_pos<6; lcd_char_pos++) {
+ digits[lcd_char_pos] = map_fullbar_char(timestr[lcd_char_pos]);
+ }
+
+ /* build the progressbar-icons */
+ for (lcd_char_pos=0; lcd_char_pos<6; lcd_char_pos++) {
+ memset(binline, 0, sizeof binline);
+ memset(player_progressbar, 0, sizeof player_progressbar);
+
+ /* make the character (progressbar & digit)*/
+ for (i=0; i<7; i++) {
+ for (j=0;j<5;j++) {
+ /* make the progressbar */
+ if (lcd_char_pos==(songpos/5)) {
+ /* partial */
+ if ((j<(songpos%5))&&(i>4))
+ binline[i*5+j] = 1;
+ else
+ binline[i*5+j] = 0;
+ }
+ else {
+ if (lcd_char_pos<(songpos/5)) {
+ /* full character */
+ if (i>4)
+ binline[i*5+j] = 1;
+ }
+ }
+ /* insert the digit */
+ if ((j<3)&&(i<4)) {
+ if (numbers[digits[lcd_char_pos]][i][j]==1)
+ binline[i*5+j] = 1;
+ }
+ }
+ }
+
+ for (i=0; i<=6; i++) {
+ for (j=0;j<5;j++) {
+ player_progressbar[i] <<= 1;
+ player_progressbar[i] += binline[i*5+j];
+ }
+ }
+
+ gwps->display->define_pattern(gwps->data->wps_progress_pat[lcd_char_pos+1],player_progressbar);
+ buf[lcd_char_pos]=gwps->data->wps_progress_pat[lcd_char_pos+1];
+
+ }
+
+ /* make rest of the progressbar if necessary */
+ if (songpos/5>5) {
+
+ /* set the characters positions that use the full 5 pixel wide bar */
+ for (lcd_char_pos=6; lcd_char_pos < (songpos/5); lcd_char_pos++)
+ buf[lcd_char_pos] = 0x86; /* '_' */
+
+ /* build the partial bar character for the tail character position */
+ memset(binline, 0, sizeof binline);
+ memset(player_progressbar, 0, sizeof player_progressbar);
+
+ for (i=5; i<7; i++) {
+ for (j=0;j<5;j++) {
+ if (j<(songpos%5)) {
+ binline[i*5+j] = 1;
+ }
+ }
+ }
+
+ for (i=0; i<7; i++) {
+ for (j=0;j<5;j++) {
+ player_progressbar[i] <<= 1;
+ player_progressbar[i] += binline[i*5+j];
+ }
+ }
+
+ gwps->display->define_pattern(gwps->data->wps_progress_pat[7],player_progressbar);
+
+ buf[songpos/5]=gwps->data->wps_progress_pat[7];
+ }
+}
+#endif
+
+/* set volume
+ return true if screen restore is needed
+ return false otherwise
+*/
+bool setvol(void)
+{
+ if (global_settings.volume < sound_min(SOUND_VOLUME))
+ global_settings.volume = sound_min(SOUND_VOLUME);
+ if (global_settings.volume > sound_max(SOUND_VOLUME))
+ global_settings.volume = sound_max(SOUND_VOLUME);
+ sound_set_volume(global_settings.volume);
+ gui_syncstatusbar_draw(&statusbars, false);
+ int i;
+ FOR_NB_SCREENS(i)
+ gui_wps_refresh(&gui_syncwps.gui_wps[i], 0, WPS_REFRESH_NON_STATIC);
+ settings_save();
+#ifdef HAVE_LCD_CHARCELLS
+ gui_syncsplash(0, false, "Vol: %d %% ",
+ sound_val2phys(SOUND_VOLUME, global_settings.volume));
+ return true;
+#endif
+ return false;
+}
+
+bool ffwd_rew(int button)
+{
+ static const int ff_rew_steps[] = {
+ 1000, 2000, 3000, 4000,
+ 5000, 6000, 8000, 10000,
+ 15000, 20000, 25000, 30000,
+ 45000, 60000
+ };
+
+ unsigned int step = 0; /* current ff/rewind step */
+ unsigned int max_step = 0; /* maximum ff/rewind step */
+ int ff_rewind_count = 0; /* current ff/rewind count (in ticks) */
+ int direction = -1; /* forward=1 or backward=-1 */
+ long accel_tick = 0; /* next time at which to bump the step size */
+ bool exit = false;
+ bool usb = false;
+ int i = 0;
+
+ while (!exit) {
+ switch ( button ) {
+ case WPS_FFWD:
+#ifdef WPS_RC_FFWD
+ case WPS_RC_FFWD:
+#endif
+ direction = 1;
+ case WPS_REW:
+#ifdef WPS_RC_REW
+ case WPS_RC_REW:
+#endif
+ if (wps_state.ff_rewind)
+ {
+ if (direction == 1)
+ {
+ /* fast forwarding, calc max step relative to end */
+ max_step =
+ (wps_state.id3->length - (wps_state.id3->elapsed + ff_rewind_count)) *
+ FF_REWIND_MAX_PERCENT / 100;
+ }
+ else
+ {
+ /* rewinding, calc max step relative to start */
+ max_step = (wps_state.id3->elapsed + ff_rewind_count) *
+ FF_REWIND_MAX_PERCENT / 100;
+ }
+
+ max_step = MAX(max_step, MIN_FF_REWIND_STEP);
+
+ if (step > max_step)
+ step = max_step;
+
+ ff_rewind_count += step * direction;
+
+ if (global_settings.ff_rewind_accel != 0 &&
+ current_tick >= accel_tick)
+ {
+ step *= 2;
+ accel_tick = current_tick +
+ global_settings.ff_rewind_accel*HZ;
+ }
+ }
+ else
+ {
+ if ( (audio_status() & AUDIO_STATUS_PLAY) &&
+ wps_state.id3 && wps_state.id3->length )
+ {
+ if (!wps_state.paused)
+ audio_pause();
+#if CONFIG_KEYPAD == PLAYER_PAD
+ FOR_NB_SCREENS(i)
+ gui_syncwps.gui_wps[i].display->stop_scroll();
+#endif
+ if (direction > 0)
+ status_set_ffmode(STATUS_FASTFORWARD);
+ else
+ status_set_ffmode(STATUS_FASTBACKWARD);
+
+ wps_state.ff_rewind = true;
+
+ step = ff_rew_steps[global_settings.ff_rewind_min_step];
+
+ accel_tick = current_tick +
+ global_settings.ff_rewind_accel*HZ;
+ }
+ else
+ break;
+ }
+
+ if (direction > 0) {
+ if ((wps_state.id3->elapsed + ff_rewind_count) > wps_state.id3->length)
+ ff_rewind_count = wps_state.id3->length - wps_state.id3->elapsed;
+ }
+ else {
+ if ((int)(wps_state.id3->elapsed + ff_rewind_count) < 0)
+ ff_rewind_count = -wps_state.id3->elapsed;
+ }
+
+ if(wps_state.wps_time_countup == false){
+ FOR_NB_SCREENS(i)
+ gui_wps_refresh(&gui_syncwps.gui_wps[i], ff_rewind_count,
+ WPS_REFRESH_PLAYER_PROGRESS |
+ WPS_REFRESH_DYNAMIC);
+ }
+ else{
+ FOR_NB_SCREENS(i)
+ gui_wps_refresh(&gui_syncwps.gui_wps[i], -ff_rewind_count,
+ WPS_REFRESH_PLAYER_PROGRESS |
+ WPS_REFRESH_DYNAMIC);
+ }
+
+ break;
+
+ case WPS_PREV:
+ case WPS_NEXT:
+#ifdef WPS_RC_PREV
+ case WPS_RC_PREV:
+ case WPS_RC_NEXT:
+#endif
+ audio_ff_rewind(wps_state.id3->elapsed+ff_rewind_count);
+ ff_rewind_count = 0;
+ wps_state.ff_rewind = false;
+ status_set_ffmode(0);
+ if (!wps_state.paused)
+ audio_resume();
+#ifdef HAVE_LCD_CHARCELLS
+ gui_wps_display();
+#endif
+ exit = true;
+ break;
+
+ default:
+ if(default_event_handler(button) == SYS_USB_CONNECTED) {
+ status_set_ffmode(0);
+ usb = true;
+ exit = true;
+ }
+ break;
+ }
+ if (!exit)
+ button = button_get(true);
+ }
+
+ /* let audio thread update id3->elapsed before calling wps_refresh */
+ yield();
+ FOR_NB_SCREENS(i)
+ gui_wps_refresh(&gui_syncwps.gui_wps[i], 0, WPS_REFRESH_ALL);
+ return usb;
+}
+
+bool gui_wps_display(void)
+{
+ int i;
+ if (!wps_state.id3 && !(audio_status() & AUDIO_STATUS_PLAY))
+ {
+ global_settings.resume_index = -1;
+#ifdef HAVE_LCD_CHARCELLS
+ gui_syncsplash(HZ, true, str(LANG_END_PLAYLIST_PLAYER));
+#else
+ gui_syncstatusbar_draw(&statusbars, true);
+ gui_syncsplash(HZ, true, str(LANG_END_PLAYLIST_RECORDER));
+#endif
+ return true;
+ }
+ else
+ {
+ FOR_NB_SCREENS(i)
+ {
+ gui_syncwps.gui_wps[i].display->clear_display();
+ if (!gui_syncwps.gui_wps[i].data->wps_loaded) {
+ if ( !gui_syncwps.gui_wps[i].data->format_buffer[0] ) {
+ /* set the default wps for the main-screen */
+ if(i == 0){
+#ifdef HAVE_LCD_BITMAP
+ wps_data_load(gui_syncwps.gui_wps[i].data,
+ "%s%?it<%?in<%in. |>%it|%fn>\n"
+ "%s%?ia<%ia|%?d2<%d2|(root)>>\n"
+ "%s%?id<%id|%?d1<%d1|(root)>> %?iy<(%iy)|>\n"
+ "\n"
+ "%al%pc/%pt%ar[%pp:%pe]\n"
+ "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n"
+ "%pb\n"
+ "%pm\n", false, false);
+#else
+ wps_data_load(gui_syncwps.gui_wps[i].data,
+ "%s%pp/%pe: %?it<%it|%fn> - %?ia<%ia|%d2> - %?id<%id|%d1>\n"
+ "%pc%?ps<*|/>%pt\n", false, false);
+#endif
+ }
+#if NB_SCREENS == 2
+ /* set the default wps for the remote-screen */
+ else if(i == 1)
+ {
+ wps_data_load(gui_syncwps.gui_wps[i].data,
+ "%s%?ia<%ia|%?d2<%d2|(root)>>\n"
+ "%s%?it<%?in<%in. |>%it|%fn>\n"
+ "%al%pc/%pt%ar[%pp:%pe]\n"
+ "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n"
+ "%pb", false, false);
+ }
+#endif
+ }
+ }
+ }
+ }
+ yield();
+ FOR_NB_SCREENS(i)
+ gui_wps_refresh(&gui_syncwps.gui_wps[i], 0, WPS_REFRESH_ALL);
+ gui_syncstatusbar_draw(&statusbars, true);
+ FOR_NB_SCREENS(i)
+ {
+#ifdef HAVE_LCD_BITMAP
+ wps_display_images(&gui_syncwps.gui_wps[i]);
+ gui_syncwps.gui_wps[i].display->update();
+#endif
+ }
+ return false;
+}
+
+bool update(struct gui_wps *gwps)
+{
+ bool track_changed = audio_has_changed_track();
+ bool retcode = false;
+
+ gwps->state->nid3 = audio_next_track();
+ if (track_changed)
+ {
+ gwps->display->stop_scroll();
+ gwps->state->id3 = audio_current_track();
+ if (gui_wps_display())
+ retcode = true;
+ else{
+ gui_wps_refresh(gwps, 0, WPS_REFRESH_ALL);
+ }
+
+ if (gwps->state->id3)
+ memcpy(gwps->state->current_track_path, gwps->state->id3->path,
+ sizeof(gwps->state->current_track_path));
+ }
+
+ if (gwps->state->id3){
+ gui_wps_refresh(gwps, 0, WPS_REFRESH_NON_STATIC);
+ }
+
+ gui_syncstatusbar_draw(&statusbars, false);
+
+ return retcode;
+}
+
+#ifdef WPS_KEYLOCK
+void display_keylock_text(bool locked)
+{
+ char* s;
+ int i;
+ FOR_NB_SCREENS(i)
+ gui_syncwps.gui_wps[i].display->stop_scroll();
+
+#ifdef HAVE_LCD_CHARCELLS
+ if(locked)
+ s = str(LANG_KEYLOCK_ON_PLAYER);
+ else
+ s = str(LANG_KEYLOCK_OFF_PLAYER);
+#else
+ if(locked)
+ s = str(LANG_KEYLOCK_ON_RECORDER);
+ else
+ s = str(LANG_KEYLOCK_OFF_RECORDER);
+#endif
+ gui_syncsplash(HZ, true, s);
+}
+
+void waitfor_nokey(void)
+{
+ /* wait until all keys are released */
+ while (button_get(false) != BUTTON_NONE)
+ yield();
+}
+#endif
+
diff --git a/apps/gui/gwps-common.h b/apps/gui/gwps-common.h
new file mode 100644
index 0000000000..5cf052898d
--- /dev/null
+++ b/apps/gui/gwps-common.h
@@ -0,0 +1,27 @@
+#ifndef _GWPS_COMMON_
+#define _GWPS_COMMON_
+#include <stdbool.h>
+#include <sys/types.h> /* for size_t */
+
+/* to avoid the unnecessary include if gwps.h */
+struct mp3entry;
+struct gui_img;
+struct wps_data;
+struct gui_wps;
+struct align_pos;
+
+void gui_wps_format_time(char* buf, int buf_size, long time);
+void fade(bool fade_in);
+void gui_wps_format(struct wps_data *data, const char *bmpdir, size_t bmpdirlen);
+bool gui_wps_refresh(struct gui_wps *gwps, int ffwd_offset,
+ unsigned char refresh_mode);
+bool gui_wps_display(void);
+bool setvol(void);
+bool update(struct gui_wps *gwps);
+bool ffwd_rew(int button);
+#ifdef WPS_KEYLOCK
+void display_keylock_text(bool locked);
+void waitfor_nokey(void);
+#endif
+#endif
+
diff --git a/apps/gui/gwps.c b/apps/gui/gwps.c
new file mode 100644
index 0000000000..4fc9acbece
--- /dev/null
+++ b/apps/gui/gwps.c
@@ -0,0 +1,853 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "system.h"
+#include "file.h"
+#include "lcd.h"
+#include "font.h"
+#include "backlight.h"
+#include "button.h"
+#include "kernel.h"
+#include "tree.h"
+#include "debug.h"
+#include "sprintf.h"
+#include "settings.h"
+#include "gwps.h"
+#include "gwps-common.h"
+#include "audio.h"
+#include "usb.h"
+#include "status.h"
+#include "main_menu.h"
+#include "ata.h"
+#include "screens.h"
+#include "playlist.h"
+#ifdef HAVE_LCD_BITMAP
+#include "icons.h"
+#include "peakmeter.h"
+#endif
+#include "action.h"
+#include "lang.h"
+#include "bookmark.h"
+#include "misc.h"
+#include "sound.h"
+#include "onplay.h"
+#include "abrepeat.h"
+#include "playback.h"
+
+#include "statusbar.h"
+#include "splash.h"
+
+#define WPS_DEFAULTCFG WPS_DIR "/rockbox_default.wps"
+#define RWPS_DEFAULTCFG WPS_DIR "/rockbox_default.rwps"
+/* currently only on wps_state is needed */
+struct wps_state wps_state;
+struct gui_syncwps gui_syncwps;
+struct wps_data wps_datas[NB_SCREENS];
+
+bool keys_locked = false;
+
+long gui_wps_show()
+{
+ long button = 0, lastbutton = 0;
+ bool ignore_keyup = true;
+ bool restore = false;
+ long restoretimer = 0; /* timer to delay screen redraw temporarily */
+ bool exit = false;
+ bool update_track = false;
+ unsigned long right_lastclick = 0;
+ unsigned long left_lastclick = 0;
+ int i;
+
+ wps_state_init();
+
+#ifdef HAVE_LCD_CHARCELLS
+ status_set_audio(true);
+ status_set_param(false);
+#else
+ FOR_NB_SCREENS(i)
+ {
+ if(global_settings.statusbar)
+ gui_syncwps.gui_wps[i].display->setmargins(0, STATUSBAR_HEIGHT);
+ else
+ gui_syncwps.gui_wps[i].display->setmargins(0, 0);
+ }
+#endif
+
+#ifdef AB_REPEAT_ENABLE
+ ab_repeat_init();
+ ab_reset_markers();
+#endif
+
+ if(audio_status() & AUDIO_STATUS_PLAY)
+ {
+ wps_state.id3 = audio_current_track();
+ wps_state.nid3 = audio_next_track();
+ if (wps_state.id3) {
+ if (gui_wps_display())
+ return 0;
+ FOR_NB_SCREENS(i)
+ gui_wps_refresh(&gui_syncwps.gui_wps[i], 0, WPS_REFRESH_ALL);
+ wps_state_update_ctp(wps_state.id3->path);
+ }
+
+ restore = true;
+ }
+ while ( 1 )
+ {
+ bool audio_paused = (audio_status() & AUDIO_STATUS_PAUSE)?true:false;
+
+ /* did someone else (i.e power thread) change audio pause mode? */
+ if (wps_state.paused != audio_paused) {
+ wps_state.paused = audio_paused;
+
+ /* if another thread paused audio, we are probably in car mode,
+ about to shut down. lets save the settings. */
+ if (wps_state.paused) {
+ settings_save();
+#if !defined(HAVE_RTC) && !defined(HAVE_SW_POWEROFF)
+ ata_flush();
+#endif
+ }
+ }
+
+#ifdef HAVE_LCD_BITMAP
+ /* when the peak meter is enabled we want to have a
+ few extra updates to make it look smooth. On the
+ other hand we don't want to waste energy if it
+ isn't displayed */
+ bool pm=false;
+ FOR_NB_SCREENS(i)
+ {
+ if(gui_syncwps.gui_wps[i].data->peak_meter_enabled)
+ pm = true;
+ }
+
+ if (pm) {
+ long next_refresh = current_tick;
+ long next_big_refresh = current_tick + HZ / 5;
+ button = BUTTON_NONE;
+ while (TIME_BEFORE(current_tick, next_big_refresh)) {
+ button = button_get(false);
+ if (button != BUTTON_NONE) {
+ break;
+ }
+ peak_meter_peek();
+ sleep(0); /* Sleep until end of current tick. */
+
+ if (TIME_AFTER(current_tick, next_refresh)) {
+ FOR_NB_SCREENS(i)
+ gui_wps_refresh(&gui_syncwps.gui_wps[i], 0, WPS_REFRESH_PEAK_METER);
+ next_refresh += HZ / PEAK_METER_FPS;
+ }
+ }
+
+ }
+
+ /* The peak meter is disabled
+ -> no additional screen updates needed */
+ else {
+ button = button_get_w_tmo(HZ/5);
+ }
+#else
+ button = button_get_w_tmo(HZ/5);
+#endif
+
+ /* discard first event if it's a button release */
+ if (button && ignore_keyup)
+ {
+ ignore_keyup = false;
+ /* Negative events are system events */
+ if (button >= 0 && button & BUTTON_REL )
+ continue;
+ }
+
+#ifdef WPS_KEYLOCK
+ /* ignore non-remote buttons when keys are locked */
+ if (keys_locked &&
+ ! ((button < 0) ||
+ (button == BUTTON_NONE) ||
+ ((button & WPS_KEYLOCK) == WPS_KEYLOCK) ||
+ (button & BUTTON_REMOTE)
+ ))
+ {
+ if (!(button & BUTTON_REL))
+ display_keylock_text(true);
+ restore = true;
+ button = BUTTON_NONE;
+ }
+#endif
+
+ /* Exit if audio has stopped playing. This can happen if using the
+ sleep timer with the charger plugged or if starting a recording
+ from F1 */
+ if (!audio_status())
+ exit = true;
+
+ switch(button)
+ {
+#ifdef WPS_CONTEXT
+ case WPS_CONTEXT:
+#ifdef WPS_RC_CONTEXT
+ case WPS_RC_CONTEXT:
+#endif
+ onplay(wps_state.id3->path, TREE_ATTR_MPA, CONTEXT_WPS);
+ restore = true;
+ break;
+#endif
+
+#ifdef WPS_RC_BROWSE
+ case WPS_RC_BROWSE:
+#endif
+ case WPS_BROWSE:
+#ifdef WPS_BROWSE_PRE
+ if ((lastbutton != WPS_BROWSE_PRE)
+#ifdef WPS_RC_BROWSE_PRE
+ && (lastbutton != WPS_RC_BROWSE_PRE)
+#endif
+ )
+ break;
+#endif
+#ifdef HAVE_LCD_CHARCELLS
+ status_set_record(false);
+ status_set_audio(false);
+#endif
+ FOR_NB_SCREENS(i)
+ gui_syncwps.gui_wps[i].display->stop_scroll();
+
+ /* set dir browser to current playing song */
+ if (global_settings.browse_current &&
+ wps_state.current_track_path[0] != '\0')
+ set_current_file(wps_state.current_track_path);
+
+ return 0;
+ break;
+
+ /* play/pause */
+ case WPS_PAUSE:
+#ifdef WPS_PAUSE_PRE
+ if (lastbutton != WPS_PAUSE_PRE)
+ break;
+#endif
+#ifdef WPS_RC_PAUSE
+ case WPS_RC_PAUSE:
+#ifdef WPS_RC_PAUSE_PRE
+ if ((button == WPS_RC_PAUSE) && (lastbutton != WPS_RC_PAUSE_PRE))
+ break;
+#endif
+#endif
+ if ( wps_state.paused )
+ {
+ wps_state.paused = false;
+ if ( global_settings.fade_on_stop )
+ fade(1);
+ else
+ audio_resume();
+ }
+ else
+ {
+ wps_state.paused = true;
+ if ( global_settings.fade_on_stop )
+ fade(0);
+ else
+ audio_pause();
+ settings_save();
+#if !defined(HAVE_RTC) && !defined(HAVE_SW_POWEROFF)
+ ata_flush(); /* make sure resume info is saved */
+#endif
+ }
+ break;
+
+ /* volume up */
+ case WPS_INCVOL:
+ case WPS_INCVOL | BUTTON_REPEAT:
+#ifdef WPS_RC_INCVOL
+ case WPS_RC_INCVOL:
+ case WPS_RC_INCVOL | BUTTON_REPEAT:
+#endif
+ global_settings.volume++;
+ if (setvol()) {
+ restore = true;
+ restoretimer = current_tick + HZ;
+ }
+ break;
+
+ /* volume down */
+ case WPS_DECVOL:
+ case WPS_DECVOL | BUTTON_REPEAT:
+#ifdef WPS_RC_DECVOL
+ case WPS_RC_DECVOL:
+ case WPS_RC_DECVOL | BUTTON_REPEAT:
+#endif
+ global_settings.volume--;
+ if (setvol()) {
+ restore = true;
+ restoretimer = current_tick + HZ;
+ }
+ break;
+
+ /* fast forward / rewind */
+#ifdef WPS_RC_FFWD
+ case WPS_RC_FFWD:
+#endif
+ case WPS_FFWD:
+#ifdef WPS_NEXT_DIR
+ if (current_tick - right_lastclick < HZ)
+ {
+ audio_next_dir();
+ right_lastclick = 0;
+ break;
+ }
+#endif
+#ifdef WPS_RC_REW
+ case WPS_RC_REW:
+#endif
+ case WPS_REW:
+#ifdef WPS_PREV_DIR
+ if (current_tick - left_lastclick < HZ)
+ {
+ audio_prev_dir();
+ left_lastclick = 0;
+ break;
+ }
+#endif
+ ffwd_rew(button);
+ break;
+
+ /* prev / restart */
+ case WPS_PREV:
+#ifdef WPS_PREV_PRE
+ if (lastbutton != WPS_PREV_PRE)
+ break;
+#endif
+#ifdef WPS_RC_PREV
+ case WPS_RC_PREV:
+#ifdef WPS_RC_PREV_PRE
+ if ((button == WPS_RC_PREV) && (lastbutton != WPS_RC_PREV_PRE))
+ break;
+#endif
+#endif
+ left_lastclick = current_tick;
+ update_track = true;
+
+#ifdef AB_REPEAT_ENABLE
+ /* if we're in A/B repeat mode and the current position
+ is past the A marker, jump back to the A marker... */
+ if ( ab_repeat_mode_enabled() && ab_after_A_marker(wps_state.id3->elapsed) )
+ {
+ ab_jump_to_A_marker();
+ break;
+ }
+ /* ...otherwise, do it normally */
+#endif
+
+ if (!wps_state.id3 || (wps_state.id3->elapsed < 3*1000)) {
+ audio_prev();
+ }
+ else {
+ if (!wps_state.paused)
+ audio_pause();
+
+ audio_ff_rewind(0);
+
+ if (!wps_state.paused)
+ audio_resume();
+ }
+ break;
+
+#ifdef WPS_NEXT_DIR
+#ifdef WPS_RC_NEXT_DIR
+ case WPS_RC_NEXT_DIR:
+#endif
+ case WPS_NEXT_DIR:
+ audio_next_dir();
+ break;
+#endif
+#ifdef WPS_PREV_DIR
+#ifdef WPS_RC_PREV_DIR
+ case WPS_RC_PREV_DIR:
+#endif
+ case WPS_PREV_DIR:
+ audio_prev_dir();
+ break;
+#endif
+
+ /* next */
+ case WPS_NEXT:
+#ifdef WPS_NEXT_PRE
+ if (lastbutton != WPS_NEXT_PRE)
+ break;
+#endif
+#ifdef WPS_RC_NEXT
+ case WPS_RC_NEXT:
+#ifdef WPS_RC_NEXT_PRE
+ if ((button == WPS_RC_NEXT) && (lastbutton != WPS_RC_NEXT_PRE))
+ break;
+#endif
+#endif
+ right_lastclick = current_tick;
+ update_track = true;
+
+#ifdef AB_REPEAT_ENABLE
+ /* if we're in A/B repeat mode and the current position is
+ before the A marker, jump to the A marker... */
+ if ( ab_repeat_mode_enabled() && ab_before_A_marker(wps_state.id3->elapsed) )
+ {
+ ab_jump_to_A_marker();
+ break;
+ }
+ /* ...otherwise, do it normally */
+#endif
+
+ audio_next();
+ break;
+
+#ifdef WPS_MENU
+ /* menu key functions */
+ case WPS_MENU:
+#ifdef WPS_MENU_PRE
+ if (lastbutton != WPS_MENU_PRE)
+ break;
+#endif
+#ifdef WPS_RC_MENU
+ case WPS_RC_MENU:
+#ifdef WPS_RC_MENU_PRE
+ if ((button == WPS_RC_MENU) && (lastbutton != WPS_RC_MENU_PRE))
+ break;
+#endif
+#endif
+ FOR_NB_SCREENS(i)
+ gui_syncwps.gui_wps[i].display->stop_scroll();
+
+ if (main_menu())
+ return true;
+#ifdef HAVE_LCD_BITMAP
+ FOR_NB_SCREENS(i)
+ {
+ if(global_settings.statusbar)
+ gui_syncwps.gui_wps[i].display->setmargins(0, STATUSBAR_HEIGHT);
+ else
+ gui_syncwps.gui_wps[i].display->setmargins(0, 0);
+ }
+#endif
+ restore = true;
+ break;
+#endif /* WPS_MENU */
+
+#ifdef WPS_KEYLOCK
+ /* key lock */
+ case WPS_KEYLOCK:
+ case WPS_KEYLOCK | BUTTON_REPEAT:
+ keys_locked = !keys_locked;
+ display_keylock_text(keys_locked);
+ restore = true;
+ waitfor_nokey();
+ break;
+#endif
+
+#if (CONFIG_KEYPAD == RECORDER_PAD) || (CONFIG_KEYPAD == IRIVER_H100_PAD)
+ /* play settings */
+ case WPS_QUICK:
+#ifdef WPS_RC_QUICK
+ case WPS_RC_QUICK:
+#endif
+ if (quick_screen(CONTEXT_WPS, WPS_QUICK))
+ return SYS_USB_CONNECTED;
+ restore = true;
+ lastbutton = 0;
+ break;
+
+ /* screen settings */
+#ifdef BUTTON_F3
+ case BUTTON_F3:
+ if (quick_screen(CONTEXT_WPS, BUTTON_F3))
+ return SYS_USB_CONNECTED;
+ restore = true;
+ break;
+#endif
+
+ /* pitch screen */
+#if CONFIG_KEYPAD == RECORDER_PAD
+ case BUTTON_ON | BUTTON_UP:
+ case BUTTON_ON | BUTTON_DOWN:
+ if (2 == pitch_screen())
+ return SYS_USB_CONNECTED;
+ restore = true;
+ break;
+#endif
+#endif
+
+#ifdef AB_REPEAT_ENABLE
+
+#ifdef WPS_AB_SET_A_MARKER
+ /* set A marker for A-B repeat */
+ case WPS_AB_SET_A_MARKER:
+ if (ab_repeat_mode_enabled())
+ ab_set_A_marker(wps_state.id3->elapsed);
+ break;
+#endif
+
+#ifdef WPS_AB_SET_B_MARKER
+ /* set B marker for A-B repeat and jump to A */
+ case WPS_AB_SET_B_MARKER:
+ if (ab_repeat_mode_enabled())
+ {
+ ab_set_B_marker(wps_state.id3->elapsed);
+ ab_jump_to_A_marker();
+ update_track = true;
+ }
+ break;
+#endif
+
+#ifdef WPS_AB_RESET_AB_MARKERS
+ /* reset A&B markers */
+ case WPS_AB_RESET_AB_MARKERS:
+ if (ab_repeat_mode_enabled())
+ {
+ ab_reset_markers();
+ update_track = true;
+ }
+ break;
+#endif
+
+#endif /* AB_REPEAT_ENABLE */
+
+ /* stop and exit wps */
+#ifdef WPS_EXIT
+ case WPS_EXIT:
+# ifdef WPS_EXIT_PRE
+ if (lastbutton != WPS_EXIT_PRE)
+ break;
+# endif
+ exit = true;
+#ifdef WPS_RC_EXIT
+ case WPS_RC_EXIT:
+#ifdef WPS_RC_EXIT_PRE
+ if (lastbutton != WPS_RC_EXIT_PRE)
+ break;
+#endif
+ exit = true;
+#endif
+ break;
+#endif
+
+#ifdef WPS_ID3
+ case WPS_ID3:
+ browse_id3();
+ restore = true;
+ break;
+#endif
+
+ case BUTTON_NONE: /* Timeout */
+ update_track = true;
+ break;
+
+ default:
+ if(default_event_handler(button) == SYS_USB_CONNECTED)
+ return SYS_USB_CONNECTED;
+ update_track = true;
+ break;
+ }
+
+ if (update_track)
+ {
+ bool upt = false;
+ FOR_NB_SCREENS(i){
+ if(update(&gui_syncwps.gui_wps[i]))
+ upt = true;
+ }
+ if (upt)
+ {
+ /* set dir browser to current playing song */
+ if (global_settings.browse_current &&
+ wps_state.current_track_path[0] != '\0')
+ set_current_file(wps_state.current_track_path);
+
+ return 0;
+ }
+ update_track = false;
+ }
+
+ if (exit) {
+#ifdef HAVE_LCD_CHARCELLS
+ status_set_record(false);
+ status_set_audio(false);
+#endif
+ if (global_settings.fade_on_stop)
+ fade(0);
+
+ FOR_NB_SCREENS(i)
+ gui_syncwps.gui_wps[i].display->stop_scroll();
+ bookmark_autobookmark();
+ audio_stop();
+#ifdef AB_REPEAT_ENABLE
+ ab_reset_markers();
+#endif
+
+ /* Keys can be locked when exiting, so either unlock here
+ or implement key locking in tree.c too */
+ keys_locked=false;
+
+ /* set dir browser to current playing song */
+ if (global_settings.browse_current &&
+ wps_state.current_track_path[0] != '\0')
+ set_current_file(wps_state.current_track_path);
+
+ return 0;
+ }
+
+ if ( button )
+ ata_spin();
+
+ if (restore &&
+ ((restoretimer == 0) ||
+ (restoretimer < current_tick)))
+ {
+ restore = false;
+ restoretimer = 0;
+ if (gui_wps_display())
+ {
+ /* set dir browser to current playing song */
+ if (global_settings.browse_current &&
+ wps_state.current_track_path[0] != '\0')
+ set_current_file(wps_state.current_track_path);
+
+ return 0;
+ }
+
+ if (wps_state.id3){
+ FOR_NB_SCREENS(i)
+ gui_wps_refresh(&gui_syncwps.gui_wps[i], 0, WPS_REFRESH_NON_STATIC);
+ }
+ }
+ if (button != BUTTON_NONE)
+ lastbutton = button;
+ }
+ return 0; /* unreachable - just to reduce compiler warnings */
+}
+
+/* needs checking if needed end*/
+
+/* wps_data*/
+/* initial setup of wps_data */
+void wps_data_init(struct wps_data *wps_data)
+{
+ int i;
+#ifdef HAVE_LCD_BITMAP
+ for (i = 0; i < MAX_IMAGES; i++) {
+ wps_data->img[i].loaded = false;
+ wps_data->img[i].display = false;
+ wps_data->img[i].always_display = false;
+ }
+#else /* HAVE_LCD_CHARCELLS */
+ for(i = 0; i < 8; i++)
+ wps_data->wps_progress_pat[i] = 0;
+ wps_data->full_line_progressbar = 0;
+#endif
+ wps_data->format_buffer[0] = '\0';
+ wps_data->wps_loaded = false;
+ wps_data->peak_meter_enabled = false;
+}
+
+#ifdef HAVE_LCD_BITMAP
+/* Clear the WPS image cache */
+static void wps_clear(struct wps_data *data )
+{
+ int i;
+ /* set images to unloaded and not displayed */
+ for (i = 0; i < MAX_IMAGES; i++) {
+ data->img[i].loaded = false;
+ data->img[i].display = false;
+ data->img[i].always_display = false;
+ }
+}
+#else
+#define wps_clear(a)
+#endif
+
+static void wps_reset(struct wps_data *data)
+{
+ data->wps_loaded = false;
+ memset(&data->format_buffer, 0, sizeof data->format_buffer);
+ wps_clear(data);
+}
+
+/* to setup up the wps-data from a format-buffer (isfile = false)
+ from a (wps-)file (isfile = true)*/
+bool wps_data_load(struct wps_data *wps_data, const char *buf, bool isfile, bool display)
+{
+ int i, s;
+ int fd;
+ if(!wps_data || !buf) return false;
+ if(!isfile)
+ {
+ wps_clear(wps_data);
+ strncpy(wps_data->format_buffer, buf, sizeof(wps_data->format_buffer));
+ wps_data->format_buffer[sizeof(wps_data->format_buffer) - 1] = 0;
+ gui_wps_format(wps_data, NULL, 0);
+ return true;
+ }
+ else
+ {
+ /*
+ * Hardcode loading WPS_DEFAULTCFG to cause a reset ideally this
+ * wants to be a virtual file. Feel free to modify dirbrowse()
+ * if you're feeling brave.
+ */
+ if (! strcmp(buf, WPS_DEFAULTCFG) || !strcmp(buf, RWPS_DEFAULTCFG) ) {
+ wps_reset(wps_data);
+ return false;
+ }
+ size_t bmpdirlen;
+ char *bmpdir = strrchr(buf, '.');
+ bmpdirlen = bmpdir - buf;
+
+ fd = open(buf, O_RDONLY);
+
+ if (fd >= 0)
+ {
+ int numread = read(fd, wps_data->format_buffer, sizeof(wps_data->format_buffer) - 1);
+
+ if (numread > 0)
+ {
+#ifdef HAVE_LCD_BITMAP
+ wps_clear(wps_data);
+#endif
+ wps_data->format_buffer[numread] = 0;
+ gui_wps_format(wps_data, buf, bmpdirlen);
+ }
+
+ close(fd);
+
+ if ( display ) {
+ bool any_defined_line;
+ int z;
+ FOR_NB_SCREENS(z)
+ screens[z].clear_display();
+#ifdef HAVE_LCD_BITMAP
+ FOR_NB_SCREENS(z)
+ screens[z].setmargins(0,0);
+#endif
+ for (s=0; s<WPS_MAX_SUBLINES; s++)
+ {
+ any_defined_line = false;
+ for (i=0; i<WPS_MAX_LINES; i++)
+ {
+ if (wps_data->format_lines[i][s])
+ {
+ if (*(wps_data->format_lines[i][s]) == 0)
+ {
+ FOR_NB_SCREENS(z)
+ screens[z].puts(0,i," ");
+ }
+ else
+ {
+ FOR_NB_SCREENS(z)
+ screens[z].puts(0,i,wps_data->format_lines[i][s]);
+ }
+ any_defined_line = true;
+ }
+ else
+ {
+ FOR_NB_SCREENS(z)
+ screens[z].puts(0,i," ");
+ }
+ }
+ if (any_defined_line)
+ {
+#ifdef HAVE_LCD_BITMAP
+ FOR_NB_SCREENS(z)
+ screens[z].update();
+#endif
+ sleep(HZ/2);
+ }
+ }
+ }
+ wps_data->wps_loaded = true;
+
+ return numread > 0;
+ }
+ }
+
+ return false;
+}
+
+/* wps_data end */
+
+/* wps_state */
+struct wps_state wps_state;
+
+void wps_state_init(void)
+{
+ wps_state.ff_rewind = false;
+ wps_state.paused = false;
+ wps_state.id3 = NULL;
+ wps_state.nid3 = NULL;
+ wps_state.current_track_path[0] = '\0';
+}
+void wps_state_update_ff_rew(bool ff_rew)
+{
+ wps_state.ff_rewind = ff_rew;
+}
+void wps_state_update_paused(bool paused)
+{
+ wps_state.paused = paused;
+}
+void wps_state_update_ctp(const char *path)
+{
+ memcpy(wps_state.current_track_path, path, sizeof(wps_state.current_track_path));
+}
+void wps_state_update_id3_nid3(struct mp3entry *id3, struct mp3entry *nid3)
+{
+ wps_state.id3 = id3;
+ wps_state.nid3 = nid3;
+}
+/* wps_state end*/
+
+/* initial setup of a wps */
+void gui_wps_init(struct gui_wps *gui_wps)
+{
+ gui_wps->data = NULL;
+ gui_wps->display = NULL;
+ /* Currently no seperate wps_state needed/possible
+ so use the only aviable ( "global" ) one */
+ gui_wps->state = &wps_state;
+}
+
+/* connects a wps with a format-description of the displayed content */
+void gui_wps_set_data(struct gui_wps *gui_wps, struct wps_data *data)
+{
+ gui_wps->data = data;
+}
+
+/* connects a wps with a screen */
+void gui_wps_set_disp(struct gui_wps *gui_wps, struct screen *display)
+{
+ gui_wps->display = display;
+}
+/* gui_wps end */
+
+void gui_sync_data_wps_init(void)
+{
+ int i;
+ FOR_NB_SCREENS(i)
+ wps_data_init(&wps_datas[i]);
+}
+
+void gui_sync_wps_screen_init(void)
+{
+ int i;
+ FOR_NB_SCREENS(i)
+ gui_wps_set_disp(&gui_syncwps.gui_wps[i], &screens[i]);
+}
+
+void gui_sync_wps_init(void)
+{
+ int i;
+ FOR_NB_SCREENS(i)
+ {
+ gui_wps_init(&gui_syncwps.gui_wps[i]);
+ gui_wps_set_data(&gui_syncwps.gui_wps[i], &wps_datas[i]);
+ }
+}
+
diff --git a/apps/gui/gwps.h b/apps/gui/gwps.h
new file mode 100644
index 0000000000..b5fddd556f
--- /dev/null
+++ b/apps/gui/gwps.h
@@ -0,0 +1,351 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2002 Jerome Kuptz
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef _WPS_H
+#define _WPS_H
+
+#include "screen_access.h"
+#include "id3.h"
+#include "playlist.h"
+
+
+/* button definitions */
+#if (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
+ (CONFIG_KEYPAD == IRIVER_H300_PAD)
+#define WPS_NEXT (BUTTON_RIGHT | BUTTON_REL)
+#define WPS_NEXT_PRE BUTTON_RIGHT
+#define WPS_PREV (BUTTON_LEFT | BUTTON_REL)
+#define WPS_PREV_PRE BUTTON_LEFT
+#define WPS_FFWD (BUTTON_RIGHT | BUTTON_REPEAT)
+#define WPS_REW (BUTTON_LEFT | BUTTON_REPEAT)
+#define WPS_INCVOL BUTTON_UP
+#define WPS_DECVOL BUTTON_DOWN
+#define WPS_PAUSE (BUTTON_ON | BUTTON_REL)
+#define WPS_PAUSE_PRE BUTTON_ON
+#define WPS_MENU (BUTTON_MODE | BUTTON_REL)
+#define WPS_MENU_PRE BUTTON_MODE
+#define WPS_BROWSE (BUTTON_SELECT | BUTTON_REL)
+#define WPS_BROWSE_PRE BUTTON_SELECT
+#define WPS_EXIT (BUTTON_OFF | BUTTON_REL)
+#define WPS_EXIT_PRE BUTTON_OFF
+#define WPS_ID3 (BUTTON_MODE | BUTTON_ON)
+#define WPS_CONTEXT (BUTTON_SELECT | BUTTON_REPEAT)
+#define WPS_QUICK (BUTTON_MODE | BUTTON_REPEAT)
+#define WPS_NEXT_DIR (BUTTON_RIGHT | BUTTON_ON)
+#define WPS_PREV_DIR (BUTTON_LEFT | BUTTON_ON)
+
+#define WPS_RC_NEXT_DIR (BUTTON_RC_BITRATE | BUTTON_REL)
+#define WPS_RC_PREV_DIR (BUTTON_RC_SOURCE | BUTTON_REL)
+#define WPS_RC_NEXT (BUTTON_RC_FF | BUTTON_REL)
+#define WPS_RC_NEXT_PRE BUTTON_RC_FF
+#define WPS_RC_PREV (BUTTON_RC_REW | BUTTON_REL)
+#define WPS_RC_PREV_PRE BUTTON_RC_REW
+#define WPS_RC_FFWD (BUTTON_RC_FF | BUTTON_REPEAT)
+#define WPS_RC_REW (BUTTON_RC_REW | BUTTON_REPEAT)
+#define WPS_RC_PAUSE BUTTON_RC_ON
+#define WPS_RC_INCVOL BUTTON_RC_VOL_UP
+#define WPS_RC_DECVOL BUTTON_RC_VOL_DOWN
+#define WPS_RC_EXIT (BUTTON_RC_STOP | BUTTON_REL)
+#define WPS_RC_EXIT_PRE BUTTON_RC_STOP
+#define WPS_RC_MENU (BUTTON_RC_MODE | BUTTON_REL)
+#define WPS_RC_MENU_PRE BUTTON_RC_MODE
+#define WPS_RC_BROWSE (BUTTON_RC_MENU | BUTTON_REL)
+#define WPS_RC_BROWSE_PRE BUTTON_RC_MENU
+#define WPS_RC_CONTEXT (BUTTON_RC_MENU | BUTTON_REPEAT)
+
+#elif CONFIG_KEYPAD == RECORDER_PAD
+#define WPS_NEXT (BUTTON_RIGHT | BUTTON_REL)
+#define WPS_NEXT_PRE BUTTON_RIGHT
+#define WPS_PREV (BUTTON_LEFT | BUTTON_REL)
+#define WPS_PREV_PRE BUTTON_LEFT
+#define WPS_FFWD (BUTTON_RIGHT | BUTTON_REPEAT)
+#define WPS_REW (BUTTON_LEFT | BUTTON_REPEAT)
+#define WPS_INCVOL BUTTON_UP
+#define WPS_DECVOL BUTTON_DOWN
+#define WPS_PAUSE_PRE BUTTON_PLAY
+#define WPS_PAUSE (BUTTON_PLAY | BUTTON_REL)
+#define WPS_MENU (BUTTON_F1 | BUTTON_REL)
+#define WPS_MENU_PRE BUTTON_F1
+#define WPS_BROWSE (BUTTON_ON | BUTTON_REL)
+#define WPS_BROWSE_PRE BUTTON_ON
+#define WPS_EXIT BUTTON_OFF
+#define WPS_KEYLOCK (BUTTON_F1 | BUTTON_DOWN)
+#define WPS_ID3 (BUTTON_F1 | BUTTON_ON)
+#define WPS_CONTEXT (BUTTON_PLAY | BUTTON_REPEAT)
+#define WPS_QUICK BUTTON_F2
+
+#ifdef AB_REPEAT_ENABLE
+#define WPS_AB_SET_A_MARKER (BUTTON_ON | BUTTON_LEFT)
+#define WPS_AB_SET_B_MARKER (BUTTON_ON | BUTTON_RIGHT)
+#define WPS_AB_RESET_AB_MARKERS (BUTTON_ON | BUTTON_OFF)
+#endif
+
+#define WPS_RC_NEXT BUTTON_RC_RIGHT
+#define WPS_RC_PREV BUTTON_RC_LEFT
+#define WPS_RC_PAUSE BUTTON_RC_PLAY
+#define WPS_RC_INCVOL BUTTON_RC_VOL_UP
+#define WPS_RC_DECVOL BUTTON_RC_VOL_DOWN
+#define WPS_RC_EXIT BUTTON_RC_STOP
+
+#elif CONFIG_KEYPAD == PLAYER_PAD
+#define WPS_NEXT (BUTTON_RIGHT | BUTTON_REL)
+#define WPS_NEXT_PRE BUTTON_RIGHT
+#define WPS_PREV (BUTTON_LEFT | BUTTON_REL)
+#define WPS_PREV_PRE BUTTON_LEFT
+#define WPS_FFWD (BUTTON_RIGHT | BUTTON_REPEAT)
+#define WPS_REW (BUTTON_LEFT | BUTTON_REPEAT)
+#define WPS_INCVOL (BUTTON_MENU | BUTTON_RIGHT)
+#define WPS_DECVOL (BUTTON_MENU | BUTTON_LEFT)
+#define WPS_PAUSE_PRE BUTTON_PLAY
+#define WPS_PAUSE (BUTTON_PLAY | BUTTON_REL)
+#define WPS_MENU (BUTTON_MENU | BUTTON_REL)
+#define WPS_MENU_PRE BUTTON_MENU
+#define WPS_BROWSE (BUTTON_ON | BUTTON_REL)
+#define WPS_BROWSE_PRE BUTTON_ON
+#define WPS_EXIT BUTTON_STOP
+#define WPS_KEYLOCK (BUTTON_MENU | BUTTON_STOP)
+#define WPS_ID3 (BUTTON_MENU | BUTTON_ON)
+#define WPS_CONTEXT (BUTTON_PLAY | BUTTON_REPEAT)
+
+#ifdef AB_REPEAT_ENABLE
+#define WPS_AB_SET_A_MARKER (BUTTON_ON | BUTTON_LEFT)
+#define WPS_AB_SET_B_MARKER (BUTTON_ON | BUTTON_RIGHT)
+#define WPS_AB_RESET_AB_MARKERS (BUTTON_ON | BUTTON_STOP)
+#endif
+
+#define WPS_RC_NEXT BUTTON_RC_RIGHT
+#define WPS_RC_PREV BUTTON_RC_LEFT
+#define WPS_RC_PAUSE BUTTON_RC_PLAY
+#define WPS_RC_INCVOL BUTTON_RC_VOL_UP
+#define WPS_RC_DECVOL BUTTON_RC_VOL_DOWN
+#define WPS_RC_EXIT BUTTON_RC_STOP
+
+#elif CONFIG_KEYPAD == ONDIO_PAD
+#define WPS_NEXT (BUTTON_RIGHT | BUTTON_REL)
+#define WPS_NEXT_PRE BUTTON_RIGHT
+#define WPS_PREV (BUTTON_LEFT | BUTTON_REL)
+#define WPS_PREV_PRE BUTTON_LEFT
+#define WPS_FFWD (BUTTON_RIGHT | BUTTON_REPEAT)
+#define WPS_REW (BUTTON_LEFT | BUTTON_REPEAT)
+#define WPS_INCVOL BUTTON_UP
+#define WPS_DECVOL BUTTON_DOWN
+#define WPS_PAUSE BUTTON_OFF
+/* #define WPS_MENU Ondio can't have both main menu and context menu in wps */
+#define WPS_BROWSE (BUTTON_MENU | BUTTON_REL)
+#define WPS_BROWSE_PRE BUTTON_MENU
+#define WPS_KEYLOCK (BUTTON_MENU | BUTTON_DOWN)
+#define WPS_EXIT (BUTTON_OFF | BUTTON_REPEAT)
+#define WPS_CONTEXT (BUTTON_MENU | BUTTON_REPEAT)
+
+#elif CONFIG_KEYPAD == GMINI100_PAD
+#define WPS_NEXT (BUTTON_RIGHT | BUTTON_REL)
+#define WPS_NEXT_PRE BUTTON_RIGHT
+#define WPS_PREV (BUTTON_LEFT | BUTTON_REL)
+#define WPS_PREV_PRE BUTTON_LEFT
+#define WPS_FFWD (BUTTON_RIGHT | BUTTON_REPEAT)
+#define WPS_REW (BUTTON_LEFT | BUTTON_REPEAT)
+#define WPS_INCVOL BUTTON_UP
+#define WPS_DECVOL BUTTON_DOWN
+#define WPS_PAUSE BUTTON_PLAY
+#define WPS_MENU (BUTTON_MENU | BUTTON_REL)
+#define WPS_MENU_PRE BUTTON_MENU
+#define WPS_BROWSE (BUTTON_ON | BUTTON_REL)
+#define WPS_BROWSE_PRE BUTTON_ON
+#define WPS_EXIT BUTTON_OFF
+#define WPS_KEYLOCK (BUTTON_MENU | BUTTON_DOWN)
+#define WPS_ID3 (BUTTON_MENU | BUTTON_ON)
+
+#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_NANO_PAD)
+
+/* TODO: Check WPS button assignments */
+
+#define WPS_NEXT (BUTTON_RIGHT | BUTTON_REL)
+#define WPS_NEXT_PRE BUTTON_RIGHT
+#define WPS_PREV (BUTTON_LEFT | BUTTON_REL)
+#define WPS_PREV_PRE BUTTON_LEFT
+#define WPS_FFWD (BUTTON_RIGHT | BUTTON_REPEAT)
+#define WPS_REW (BUTTON_LEFT | BUTTON_REPEAT)
+#define WPS_INCVOL BUTTON_UP
+#define WPS_DECVOL BUTTON_DOWN
+#define WPS_PAUSE BUTTON_OFF
+/* #define WPS_MENU iPod can't have both main menu and context menu in wps */
+#define WPS_BROWSE (BUTTON_MENU | BUTTON_REL)
+#define WPS_BROWSE_PRE BUTTON_MENU
+#define WPS_KEYLOCK (BUTTON_MENU | BUTTON_DOWN)
+#define WPS_EXIT (BUTTON_OFF | BUTTON_REPEAT)
+#define WPS_CONTEXT (BUTTON_MENU | BUTTON_REPEAT)
+
+#endif
+
+/* constants used in line_type and as refresh_mode for wps_refresh */
+#define WPS_REFRESH_STATIC 1 /* line doesn't change over time */
+#define WPS_REFRESH_DYNAMIC 2 /* line may change (e.g. time flag) */
+#define WPS_REFRESH_SCROLL 4 /* line scrolls */
+#define WPS_REFRESH_PLAYER_PROGRESS 8 /* line contains a progress bar */
+#define WPS_REFRESH_PEAK_METER 16 /* line contains a peak meter */
+#define WPS_REFRESH_ALL 0xff /* to refresh all line types */
+/* to refresh only those lines that change over time */
+#define WPS_REFRESH_NON_STATIC (WPS_REFRESH_ALL & ~WPS_REFRESH_STATIC & ~WPS_REFRESH_SCROLL)
+
+/* alignments */
+#define WPS_ALIGN_RIGHT 32
+#define WPS_ALIGN_CENTER 64
+#define WPS_ALIGN_LEFT 128
+
+
+extern bool keys_locked;
+/* wps_data*/
+
+#ifdef HAVE_LCD_BITMAP
+struct gui_img{
+ unsigned char* ptr; /* pointer */
+ int x; /* x-pos */
+ int y; /* y-pos */
+ int w; /* width */
+ int h; /* height */
+ bool loaded; /* load state */
+ bool display; /* is to be displayed */
+ bool always_display; /* not using the preload/display mechanism */
+};
+#endif
+
+struct align_pos {
+ char* left;
+ char* center;
+ char* right;
+};
+
+#ifdef HAVE_LCD_BITMAP
+#define MAX_IMAGES (26*2) /* a-z and A-Z */
+#define IMG_BUFSIZE (LCD_HEIGHT * LCD_WIDTH * MAX_IMAGES/25) / 8
+#define WPS_MAX_LINES (LCD_HEIGHT/5+1)
+#define FORMAT_BUFFER_SIZE 3072
+#else
+#define WPS_MAX_LINES 2
+#define FORMAT_BUFFER_SIZE 400
+#endif
+#define WPS_MAX_SUBLINES 12
+#define DEFAULT_SUBLINE_TIME_MULTIPLIER 20 /* (10ths of sec) */
+#define BASE_SUBLINE_TIME 10 /* base time that multiplier is applied to
+ (1/HZ sec, or 100ths of sec) */
+#define SUBLINE_RESET -1
+
+/* wps_data
+ this struct old all necessary data which describes the
+ viewable content of a wps */
+struct wps_data
+{
+#ifdef HAVE_LCD_BITMAP
+ struct gui_img img[MAX_IMAGES];
+ unsigned char img_buf[IMG_BUFSIZE];
+#endif
+#ifdef HAVE_LCD_CHARCELLS
+ unsigned char wps_progress_pat[8];
+ bool full_line_progressbar;
+#endif
+ char format_buffer[FORMAT_BUFFER_SIZE];
+ char* format_lines[WPS_MAX_LINES][WPS_MAX_SUBLINES];
+ struct align_pos format_align[WPS_MAX_LINES][WPS_MAX_SUBLINES];
+ unsigned char line_type[WPS_MAX_LINES][WPS_MAX_SUBLINES];
+ unsigned short time_mult[WPS_MAX_LINES][WPS_MAX_SUBLINES];
+ long subline_expire_time[WPS_MAX_LINES];
+ int curr_subline[WPS_MAX_LINES];
+ bool wps_loaded;
+ bool peak_meter_enabled;
+};
+
+/* initial setup of wps_data */
+void wps_data_init(struct wps_data *wps_data);
+
+/* to setup up the wps-data from a format-buffer (isfile = false)
+ from a (wps-)file (isfile = true)*/
+bool wps_data_load(struct wps_data *wps_data, const char *buf, bool isfile, bool display);
+
+/* wps_data end */
+
+/* wps_state
+ holds the data which belongs to the current played track,
+ the track which will be played afterwards, current path to the track
+ and some status infos */
+struct wps_state
+{
+ bool ff_rewind;
+ bool paused;
+ int ff_rewind_count;
+ bool wps_time_countup;
+ struct mp3entry* id3;
+ struct mp3entry* nid3;
+ char current_track_path[MAX_PATH+1];
+};
+
+/* initial setup of wps_data */
+void wps_state_init(void);
+
+/* change the ff/rew-status
+ if ff_rew = true then we are in skipping mode
+ else we are in normal mode */
+void wps_state_update_ff_rew(bool ff_rew);
+
+/* change the paused-status
+ to indicate if playback is currently paused or not */
+void wps_state_update_paused(bool paused);
+
+/* change the path to the current played track */
+void wps_state_update_ctp(const char *path);
+
+/* change the tag-information of the current played track
+ and the following track */
+void wps_state_update_id3_nid3(struct mp3entry *id3, struct mp3entry *nid3);
+/* wps_state end*/
+
+/* gui_wps
+ defines a wps with it's data, state,
+ and the screen on which the wps-content should be drawn */
+struct gui_wps
+{
+ struct screen * display;
+ struct wps_data *data;
+ struct wps_state *state;
+};
+
+/* initial setup of a wps */
+void gui_wps_init(struct gui_wps *gui_wps);
+
+/* connects a wps with a format-description of the displayed content */
+void gui_wps_set_data(struct gui_wps *gui_wps, struct wps_data *data);
+
+/* connects a wps with a screen */
+void gui_wps_set_disp(struct gui_wps *gui_wps, struct screen *display);
+/* gui_wps end */
+
+struct gui_syncwps
+{
+ struct gui_wps gui_wps[NB_SCREENS];
+};
+long gui_wps_show(void);
+
+/* currently only on wps_state is needed */
+extern struct wps_state wps_state;
+extern struct gui_syncwps gui_syncwps;
+extern struct wps_data wps_datas[NB_SCREENS];
+
+void gui_sync_wps_init(void);
+void gui_sync_data_wps_init(void);
+void gui_sync_wps_screen_init(void);
+
+#endif