From 5e5fc64cb2e74024e15cb33eab6b832610c2a60b Mon Sep 17 00:00:00 2001 From: Jonathan Gordon Date: Mon, 27 Jul 2009 07:21:05 +0000 Subject: Start of some apps/ and wps cleanup work... Move everything related to the actual drawing of the wps into apps/gui/wps_engine, things related to the actual screen are in apps/gui/music_screen.c (names are temporary unless noone comes up with anything better) No real code changes. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@22062 a1c6a512-1295-4272-9138-f99709370657 --- apps/gui/gwps-common.c | 2071 ----------------------------------- apps/gui/gwps-common.h | 53 - apps/gui/gwps.c | 984 ----------------- apps/gui/gwps.h | 546 --------- apps/gui/music_screen.c | 1213 ++++++++++++++++++++ apps/gui/music_screen.h | 37 + apps/gui/wps_debug.c | 641 ----------- apps/gui/wps_engine/gwps.h | 546 +++++++++ apps/gui/wps_engine/wps_debug.c | 641 +++++++++++ apps/gui/wps_engine/wps_display.c | 1099 +++++++++++++++++++ apps/gui/wps_engine/wps_engine.h | 49 + apps/gui/wps_engine/wps_internals.h | 556 ++++++++++ apps/gui/wps_engine/wps_parser.c | 1843 +++++++++++++++++++++++++++++++ apps/gui/wps_engine/wps_tokens.c | 807 ++++++++++++++ apps/gui/wps_parser.c | 1843 ------------------------------- 15 files changed, 6791 insertions(+), 6138 deletions(-) delete mode 100644 apps/gui/gwps-common.c delete mode 100644 apps/gui/gwps-common.h delete mode 100644 apps/gui/gwps.c delete mode 100644 apps/gui/gwps.h create mode 100644 apps/gui/music_screen.c create mode 100644 apps/gui/music_screen.h delete mode 100644 apps/gui/wps_debug.c create mode 100644 apps/gui/wps_engine/gwps.h create mode 100644 apps/gui/wps_engine/wps_debug.c create mode 100644 apps/gui/wps_engine/wps_display.c create mode 100644 apps/gui/wps_engine/wps_engine.h create mode 100644 apps/gui/wps_engine/wps_internals.h create mode 100644 apps/gui/wps_engine/wps_parser.c create mode 100644 apps/gui/wps_engine/wps_tokens.c delete mode 100644 apps/gui/wps_parser.c (limited to 'apps/gui') diff --git a/apps/gui/gwps-common.c b/apps/gui/gwps-common.c deleted file mode 100644 index 721682f248..0000000000 --- a/apps/gui/gwps-common.c +++ /dev/null @@ -1,2071 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2002-2007 Björn Stenberg - * Copyright (C) 2007-2008 Nicolas Pennequin - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ****************************************************************************/ -#include "gwps-common.h" -#include "font.h" -#include -#include -#include -#include "system.h" -#include "settings.h" -#include "settings_list.h" -#include "rbunicode.h" -#include "rtc.h" -#include "audio.h" -#include "status.h" -#include "power.h" -#include "powermgmt.h" -#include "sound.h" -#include "debug.h" -#ifdef HAVE_LCD_CHARCELLS -#include "hwcompat.h" -#endif -#include "abrepeat.h" -#include "mp3_playback.h" -#include "lang.h" -#include "misc.h" -#include "splash.h" -#include "scrollbar.h" -#include "led.h" -#include "lcd.h" -#ifdef HAVE_LCD_BITMAP -#include "peakmeter.h" -/* Image stuff */ -#include "bmp.h" -#include "albumart.h" -#endif -#include "dsp.h" -#include "action.h" -#include "cuesheet.h" -#include "playlist.h" -#if CONFIG_CODEC == SWCODEC -#include "playback.h" -#endif -#include "backdrop.h" -#include "viewport.h" - -#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 - -/* Timeout unit expressed in HZ. In WPS, all timeouts are given in seconds - (possibly with a decimal fraction) but stored as integer values. - E.g. 2.5 is stored as 25. This means 25 tenth of a second, i.e. 25 units. -*/ -#define TIMEOUT_UNIT (HZ/10) /* I.e. 0.1 sec */ -#define DEFAULT_SUBLINE_TIME_MULTIPLIER 20 /* In TIMEOUT_UNIT's */ - -bool wps_fading_out = false; -void fade(bool fade_in, bool updatewps) -{ - int fp_global_vol = global_settings.volume << 8; - int fp_min_vol = sound_min(SOUND_VOLUME) << 8; - int fp_step = (fp_global_vol - fp_min_vol) / 30; - int i; - wps_fading_out = !fade_in; - if (fade_in) { - /* fade in */ - int fp_volume = fp_min_vol; - - /* zero out the sound */ - sound_set_volume(fp_min_vol >> 8); - - sleep(HZ/10); /* let audio thread run */ - audio_resume(); - - while (fp_volume < fp_global_vol - fp_step) { - fp_volume += fp_step; - sound_set_volume(fp_volume >> 8); - if (updatewps) - { - FOR_NB_SCREENS(i) - gui_wps_redraw(&gui_wps[i], 0, WPS_REFRESH_NON_STATIC); - } - sleep(1); - } - sound_set_volume(global_settings.volume); - } - else { - /* fade out */ - int fp_volume = fp_global_vol; - - while (fp_volume > fp_min_vol + fp_step) { - fp_volume -= fp_step; - sound_set_volume(fp_volume >> 8); - if (updatewps) - { - FOR_NB_SCREENS(i) - gui_wps_redraw(&gui_wps[i], 0, WPS_REFRESH_NON_STATIC); - } - sleep(1); - } - audio_pause(); - wps_fading_out = false; -#if CONFIG_CODEC != SWCODEC -#ifndef SIMULATOR - /* let audio thread run and wait for the mas to run out of data */ - while (!mp3_pause_done()) -#endif - sleep(HZ/10); -#endif - - /* reset volume to what it was before the fade */ - sound_set_volume(global_settings.volume); - } -} - -bool update_onvol_change(struct gui_wps * gwps) -{ - gui_wps_redraw(gwps, 0, WPS_REFRESH_NON_STATIC); - -#ifdef HAVE_LCD_CHARCELLS - splashf(0, "Vol: %3d dB", - sound_val2phys(SOUND_VOLUME, global_settings.volume)); - return true; -#endif - return false; -} - -bool ffwd_rew(int button) -{ - 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 */ - bool exit = false; - bool usb = false; - int i = 0; - const long ff_rw_accel = (global_settings.ff_rewind_accel + 3); - - if (button == ACTION_NONE) - { - status_set_ffmode(0); - return usb; - } - while (!exit) - { - switch ( button ) - { - case ACTION_WPS_SEEKFWD: - direction = 1; - case ACTION_WPS_SEEKBACK: - 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; - - /* smooth seeking by multiplying step by: 1 + (2 ^ -accel) */ - step += step >> ff_rw_accel; - } - else - { - if ( (audio_status() & AUDIO_STATUS_PLAY) && - wps_state.id3 && wps_state.id3->length ) - { - if (!wps_state.paused) -#if (CONFIG_CODEC == SWCODEC) - audio_pre_ff_rewind(); -#else - audio_pause(); -#endif -#if CONFIG_KEYPAD == PLAYER_PAD - FOR_NB_SCREENS(i) - 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 = 1000 * global_settings.ff_rewind_min_step; - } - 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; - } - - FOR_NB_SCREENS(i) - gui_wps_redraw(&gui_wps[i], - (wps_state.wps_time_countup == false)? - ff_rewind_count:-ff_rewind_count, - WPS_REFRESH_PLAYER_PROGRESS | - WPS_REFRESH_DYNAMIC); - - break; - - case ACTION_WPS_STOPSEEK: - wps_state.id3->elapsed = wps_state.id3->elapsed+ff_rewind_count; - audio_ff_rewind(wps_state.id3->elapsed); - ff_rewind_count = 0; - wps_state.ff_rewind = false; - status_set_ffmode(0); -#if (CONFIG_CODEC != SWCODEC) - if (!wps_state.paused) - audio_resume(); -#endif -#ifdef HAVE_LCD_CHARCELLS - FOR_NB_SCREENS(i) - gui_wps_redraw(&gui_wps[i],0, WPS_REFRESH_ALL); -#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 = get_action(CONTEXT_WPS|ALLOW_SOFTLOCK,TIMEOUT_BLOCK); -#ifdef HAVE_TOUCHSCREEN - if (button == ACTION_TOUCHSCREEN) - button = wps_get_touchaction(gui_wps[SCREEN_MAIN].data); -#endif - } - } - return usb; -} - -bool gui_wps_display(struct gui_wps *gwps) -{ - struct screen *display = gwps->display; - struct wps_data *data = gwps->data; - int screen = display->screen_type; - - /* Update the values in the first (default) viewport - in case the user - has modified the statusbar or colour settings */ -#if LCD_DEPTH > 1 - if (display->depth > 1) - { - data->viewports[0].vp.fg_pattern = display->get_foreground(); - data->viewports[0].vp.bg_pattern = display->get_background(); - } -#endif - display->clear_display(); - if (!data->wps_loaded) { - if ( !data->num_tokens ) { - /* set the default wps for the main-screen */ - if(screen == SCREEN_MAIN) - { -#if LCD_DEPTH > 1 - unload_wps_backdrop(); -#endif - wps_data_load(data, - display, -#ifdef HAVE_LCD_BITMAP - "%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 %?iv<(id3v%iv)|(no id3)>\n" - "%pb\n" - "%pm\n", false); -#else - "%s%pp/%pe: %?it<%it|%fn> - %?ia<%ia|%d2> - %?id<%id|%d1>\n" - "%pc%?ps<*|/>%pt\n", false); -#endif - } -#ifdef HAVE_REMOTE_LCD - /* set the default wps for the remote-screen */ - else if(screen == SCREEN_REMOTE) - { -#if LCD_REMOTE_DEPTH > 1 - unload_remote_wps_backdrop(); -#endif - wps_data_load(data, - display, - "%s%?ia<%ia|%?d2<%d2|(root)>>\n" - "%s%?it<%?in<%in. |>%it|%fn>\n" - "%al%pc/%pt%ar[%pp:%pe]\n" - "%fbkBit %?fv %?iv<(id3v%iv)|(no id3)>\n" - "%pb\n", false); - } -#endif - } - } - else - { -#if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1 - if (screen == SCREEN_REMOTE) - show_remote_wps_backdrop(); - else if (screen == SCREEN_MAIN) -#endif -#if LCD_DEPTH > 1 - show_wps_backdrop(); -#endif - } - return gui_wps_redraw(gwps, 0, WPS_REFRESH_ALL); -} - -bool gui_wps_update(struct gui_wps *gwps) -{ - struct mp3entry *id3 = gwps->state->id3; - bool retval; - bool cuesheet_update = (id3 != NULL ? cuesheet_subtrack_changed(id3) : false); - gwps->state->do_full_update = cuesheet_update || gwps->state->do_full_update; - retval = gui_wps_redraw(gwps, 0, - gwps->state->do_full_update ? - WPS_REFRESH_ALL : WPS_REFRESH_NON_STATIC); - return retval; -} - - -void display_keylock_text(bool locked) -{ - int i; - FOR_NB_SCREENS(i) - gui_wps[i].display->stop_scroll(); - - splash(HZ, locked ? ID2P(LANG_KEYLOCK_ON) : ID2P(LANG_KEYLOCK_OFF)); -} - -#ifdef HAVE_LCD_BITMAP - -static void draw_progressbar(struct gui_wps *gwps, - struct wps_viewport *wps_vp) - { - struct screen *display = gwps->display; - struct wps_state *state = gwps->state; - struct progressbar *pb = wps_vp->pb; - int y = pb->y; - - if (y < 0) - { - int line_height = font_get(wps_vp->vp.font)->height; - /* center the pb in the line, but only if the line is higher than the pb */ - int center = (line_height-pb->height)/2; - /* if Y was not set calculate by font height,Y is -line_number-1 */ - y = (-y -1)*line_height + (0 > center ? 0 : center); - } - - if (pb->have_bitmap_pb) - gui_bitmap_scrollbar_draw(display, pb->bm, - pb->x, y, pb->width, pb->bm.height, - state->id3->length ? state->id3->length : 1, 0, - state->id3->length ? state->id3->elapsed - + state->ff_rewind_count : 0, - HORIZONTAL); - else - gui_scrollbar_draw(display, pb->x, y, pb->width, pb->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() && state->id3->length != 0 ) - ab_draw_markers(display, state->id3->length, - pb->x, pb->x + pb->width, y, pb->height); -#endif - - if (state->id3->cuesheet) - cue_draw_markers(display, state->id3->cuesheet, state->id3->length, - pb->x, pb->x + pb->width, y+1, pb->height-2); -} - -/* 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].bm.width, data->img[n].subimage_height); - gwps->display->set_drawmode(DRMODE_SOLID); -} - -static void wps_draw_image(struct gui_wps *gwps, int n, int subimage) -{ - struct screen *display = gwps->display; - struct wps_data *data = gwps->data; - if(data->img[n].always_display) - display->set_drawmode(DRMODE_FG); - else - display->set_drawmode(DRMODE_SOLID); - -#if LCD_DEPTH > 1 - if(data->img[n].bm.format == FORMAT_MONO) { -#endif - display->mono_bitmap_part(data->img[n].bm.data, - 0, data->img[n].subimage_height * subimage, - data->img[n].bm.width, data->img[n].x, - data->img[n].y, data->img[n].bm.width, - data->img[n].subimage_height); -#if LCD_DEPTH > 1 - } else { - display->transparent_bitmap_part((fb_data *)data->img[n].bm.data, - 0, data->img[n].subimage_height * subimage, - data->img[n].bm.width, data->img[n].x, - data->img[n].y, data->img[n].bm.width, - data->img[n].subimage_height); - } -#endif -} - -static void wps_display_images(struct gui_wps *gwps, struct viewport* vp) -{ - if(!gwps || !gwps->data || !gwps->display) - return; - - int n; - struct wps_data *data = gwps->data; - struct screen *display = gwps->display; - - for (n = 0; n < MAX_IMAGES; n++) - { - if (data->img[n].loaded) - { - if (data->img[n].display >= 0) - { - wps_draw_image(gwps, n, data->img[n].display); - } else if (data->img[n].always_display && data->img[n].vp == vp) - { - wps_draw_image(gwps, n, 0); - } - } - } - display->set_drawmode(DRMODE_SOLID); -} - -#else /* HAVE_LCD_CHARCELL */ - -static bool draw_player_progress(struct gui_wps *gwps) -{ - struct wps_state *state = gwps->state; - struct screen *display = gwps->display; - unsigned char progress_pattern[7]; - int pos = 0; - int i; - - if (!state->id3) - return false; - - if (state->id3->length) - pos = 36 * (state->id3->elapsed + state->ff_rewind_count) - / state->id3->length; - - for (i = 0; i < 7; i++, pos -= 5) - { - if (pos <= 0) - progress_pattern[i] = 0x1fu; - else if (pos >= 5) - progress_pattern[i] = 0x00u; - else - progress_pattern[i] = 0x1fu >> pos; - } - - display->define_pattern(gwps->data->wps_progress_pat[0], progress_pattern); - return true; -} - -static void draw_player_fullbar(struct gui_wps *gwps, char* buf, int buf_size) -{ - static const unsigned char numbers[10][4] = { - {0x0e, 0x0a, 0x0a, 0x0e}, /* 0 */ - {0x04, 0x0c, 0x04, 0x04}, /* 1 */ - {0x0e, 0x02, 0x04, 0x0e}, /* 2 */ - {0x0e, 0x02, 0x06, 0x0e}, /* 3 */ - {0x08, 0x0c, 0x0e, 0x04}, /* 4 */ - {0x0e, 0x0c, 0x02, 0x0c}, /* 5 */ - {0x0e, 0x08, 0x0e, 0x0e}, /* 6 */ - {0x0e, 0x02, 0x04, 0x08}, /* 7 */ - {0x0e, 0x0e, 0x0a, 0x0e}, /* 8 */ - {0x0e, 0x0e, 0x02, 0x0e}, /* 9 */ - }; - - struct wps_state *state = gwps->state; - struct screen *display = gwps->display; - struct wps_data *data = gwps->data; - unsigned char progress_pattern[7]; - char timestr[10]; - int time; - int time_idx = 0; - int pos = 0; - int pat_idx = 1; - int digit, i, j; - bool softchar; - - if (!state->id3 || buf_size < 34) /* worst case: 11x UTF-8 char + \0 */ - return; - - time = state->id3->elapsed + state->ff_rewind_count; - if (state->id3->length) - pos = 55 * time / state->id3->length; - - memset(timestr, 0, sizeof(timestr)); - format_time(timestr, sizeof(timestr)-2, time); - timestr[strlen(timestr)] = ':'; /* always safe */ - - for (i = 0; i < 11; i++, pos -= 5) - { - softchar = false; - memset(progress_pattern, 0, sizeof(progress_pattern)); - - if ((digit = timestr[time_idx])) - { - softchar = true; - digit -= '0'; - - if (timestr[time_idx + 1] == ':') /* ones, left aligned */ - { - memcpy(progress_pattern, numbers[digit], 4); - time_idx += 2; - } - else /* tens, shifted right */ - { - for (j = 0; j < 4; j++) - progress_pattern[j] = numbers[digit][j] >> 1; - - if (time_idx > 0) /* not the first group, add colon in front */ - { - progress_pattern[1] |= 0x10u; - progress_pattern[3] |= 0x10u; - } - time_idx++; - } - - if (pos >= 5) - progress_pattern[5] = progress_pattern[6] = 0x1fu; - } - - if (pos > 0 && pos < 5) - { - softchar = true; - progress_pattern[5] = progress_pattern[6] = (~0x1fu >> pos) & 0x1fu; - } - - if (softchar && pat_idx < 8) - { - display->define_pattern(data->wps_progress_pat[pat_idx], - progress_pattern); - buf = utf8encode(data->wps_progress_pat[pat_idx], buf); - pat_idx++; - } - else if (pos <= 0) - buf = utf8encode(' ', buf); - else - buf = utf8encode(0xe115, buf); /* 2/7 _ */ - } - *buf = '\0'; -} - -#endif /* HAVE_LCD_CHARCELL */ - -static char* get_codectype(const struct mp3entry* id3) -{ - if (id3->codectype < AFMT_NUM_CODECS) { - return (char*)audio_formats[id3->codectype].label; - } else { - return NULL; - } -} - -/* 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); - strlcpy(buf, sep + 1, len + 1); - return buf; -} - -/* Return the tag found at index i and write its value in buf. - The return value is buf if the tag had a value, or NULL if not. - - intval is used with conditionals/enums: when this function is called, - intval should contain the number of options in the conditional/enum. - When this function returns, intval is -1 if the tag is non numeric or, - if the tag is numeric, *intval is the enum case we want to go to (between 1 - and the original value of *intval, inclusive). - When not treating a conditional/enum, intval should be NULL. -*/ -static const char *get_token_value(struct gui_wps *gwps, - struct wps_token *token, - char *buf, int buf_size, - int *intval) -{ - if (!gwps) - return NULL; - - struct wps_data *data = gwps->data; - struct wps_state *state = gwps->state; - - if (!data || !state) - return NULL; - - struct mp3entry *id3; - - if (token->next) - id3 = state->nid3; - else - id3 = state->id3; - - if (!id3) - return NULL; - -#if CONFIG_RTC - struct tm* tm = NULL; - - /* if the token is an RTC one, update the time - and do the necessary checks */ - if (token->type >= WPS_TOKENS_RTC_BEGIN - && token->type <= WPS_TOKENS_RTC_END) - { - tm = get_time(); - - if (!valid_time(tm)) - return NULL; - } -#endif - - int limit = 1; - if (intval) - { - limit = *intval; - *intval = -1; - } - - switch (token->type) - { - case WPS_TOKEN_CHARACTER: - return &(token->value.c); - - case WPS_TOKEN_STRING: - return data->strings[token->value.i]; - - case WPS_TOKEN_TRACK_TIME_ELAPSED: - format_time(buf, buf_size, - id3->elapsed + state->ff_rewind_count); - return buf; - - case WPS_TOKEN_TRACK_TIME_REMAINING: - format_time(buf, buf_size, - id3->length - id3->elapsed - - state->ff_rewind_count); - return buf; - - case WPS_TOKEN_TRACK_LENGTH: - format_time(buf, buf_size, id3->length); - return buf; - - case WPS_TOKEN_PLAYLIST_ENTRIES: - snprintf(buf, buf_size, "%d", playlist_amount()); - return buf; - - case WPS_TOKEN_PLAYLIST_NAME: - return playlist_name(NULL, buf, buf_size); - - case WPS_TOKEN_PLAYLIST_POSITION: - snprintf(buf, buf_size, "%d", playlist_get_display_index()); - return buf; - - case WPS_TOKEN_PLAYLIST_SHUFFLE: - if ( global_settings.playlist_shuffle ) - return "s"; - else - return NULL; - break; - - case WPS_TOKEN_VOLUME: - snprintf(buf, buf_size, "%d", global_settings.volume); - if (intval) - { - if (global_settings.volume == sound_min(SOUND_VOLUME)) - { - *intval = 1; - } - else if (global_settings.volume == 0) - { - *intval = limit - 1; - } - else if (global_settings.volume > 0) - { - *intval = limit; - } - else - { - *intval = (limit - 3) * (global_settings.volume - - sound_min(SOUND_VOLUME) - 1) - / (-1 - sound_min(SOUND_VOLUME)) + 2; - } - } - return buf; - - case WPS_TOKEN_TRACK_ELAPSED_PERCENT: - if (id3->length <= 0) - return NULL; - - if (intval) - { - *intval = limit * (id3->elapsed + state->ff_rewind_count) - / id3->length + 1; - } - snprintf(buf, buf_size, "%d", - 100*(id3->elapsed + state->ff_rewind_count) / id3->length); - return buf; - - case WPS_TOKEN_METADATA_ARTIST: - return id3->artist; - - case WPS_TOKEN_METADATA_COMPOSER: - return id3->composer; - - case WPS_TOKEN_METADATA_ALBUM: - return id3->album; - - case WPS_TOKEN_METADATA_ALBUM_ARTIST: - return id3->albumartist; - - case WPS_TOKEN_METADATA_GROUPING: - return id3->grouping; - - case WPS_TOKEN_METADATA_GENRE: - return id3->genre_string; - - case WPS_TOKEN_METADATA_DISC_NUMBER: - if (id3->disc_string) - return id3->disc_string; - if (id3->discnum) { - snprintf(buf, buf_size, "%d", id3->discnum); - return buf; - } - return NULL; - - case WPS_TOKEN_METADATA_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 WPS_TOKEN_METADATA_TRACK_TITLE: - return id3->title; - - case WPS_TOKEN_METADATA_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; - } - - case WPS_TOKEN_METADATA_YEAR: - if( id3->year_string ) - return id3->year_string; - - if (id3->year) { - snprintf(buf, buf_size, "%d", id3->year); - return buf; - } - return NULL; - - case WPS_TOKEN_METADATA_COMMENT: - return id3->comment; - -#ifdef HAVE_ALBUMART - case WPS_TOKEN_ALBUMART_DISPLAY: - draw_album_art(gwps, audio_current_aa_hid(), false); - return NULL; - - case WPS_TOKEN_ALBUMART_FOUND: - if (audio_current_aa_hid() >= 0) { - return "C"; - } - return NULL; -#endif - - case WPS_TOKEN_FILE_BITRATE: - if(id3->bitrate) - snprintf(buf, buf_size, "%d", id3->bitrate); - else - return "?"; - return buf; - - case WPS_TOKEN_FILE_CODEC: - if (intval) - { - if(id3->codectype == AFMT_UNKNOWN) - *intval = AFMT_NUM_CODECS; - else - *intval = id3->codectype; - } - return get_codectype(id3); - - case WPS_TOKEN_FILE_FREQUENCY: - snprintf(buf, buf_size, "%ld", id3->frequency); - return buf; - - case WPS_TOKEN_FILE_FREQUENCY_KHZ: - /* ignore remainders < 100, so 22050 Hz becomes just 22k */ - if ((id3->frequency % 1000) < 100) - snprintf(buf, buf_size, "%ld", id3->frequency / 1000); - else - snprintf(buf, buf_size, "%ld.%d", - id3->frequency / 1000, - (id3->frequency % 1000) / 100); - return buf; - - case WPS_TOKEN_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 WPS_TOKEN_FILE_NAME_WITH_EXTENSION: - return get_dir(buf, buf_size, id3->path, 0); - - case WPS_TOKEN_FILE_PATH: - return id3->path; - - case WPS_TOKEN_FILE_SIZE: - snprintf(buf, buf_size, "%ld", id3->filesize / 1024); - return buf; - - case WPS_TOKEN_FILE_VBR: - return id3->vbr ? "(avg)" : NULL; - - case WPS_TOKEN_FILE_DIRECTORY: - return get_dir(buf, buf_size, id3->path, token->value.i); - - case WPS_TOKEN_BATTERY_PERCENT: - { - int l = battery_level(); - - if (intval) - { - limit = MAX(limit, 2); - if (l > -1) { - /* First enum is used for "unknown level". */ - *intval = (limit - 1) * l / 100 + 2; - } else { - *intval = 1; - } - } - - if (l > -1) { - snprintf(buf, buf_size, "%d", l); - return buf; - } else { - return "?"; - } - } - - case WPS_TOKEN_BATTERY_VOLTS: - { - unsigned int v = battery_voltage(); - snprintf(buf, buf_size, "%d.%02d", v / 1000, (v % 1000) / 10); - return buf; - } - - case WPS_TOKEN_BATTERY_TIME: - { - int t = battery_time(); - if (t >= 0) - snprintf(buf, buf_size, "%dh %dm", t / 60, t % 60); - else - return "?h ?m"; - return buf; - } - -#if CONFIG_CHARGING - case WPS_TOKEN_BATTERY_CHARGER_CONNECTED: - { - if(charger_input_state==CHARGER) - return "p"; - else - return NULL; - } -#endif -#if CONFIG_CHARGING >= CHARGING_MONITOR - case WPS_TOKEN_BATTERY_CHARGING: - { - if (charge_state == CHARGING || charge_state == TOPOFF) { - return "c"; - } else { - return NULL; - } - } -#endif - case WPS_TOKEN_BATTERY_SLEEPTIME: - { - if (get_sleep_timer() == 0) - return NULL; - else - { - format_time(buf, buf_size, get_sleep_timer() * 1000); - return buf; - } - } - - case WPS_TOKEN_PLAYBACK_STATUS: - { - int status = audio_status(); - int mode = 1; - if (status == AUDIO_STATUS_PLAY) - mode = 2; - if (wps_fading_out || - (status & AUDIO_STATUS_PAUSE && !status_get_ffmode())) - mode = 3; - if (status_get_ffmode() == STATUS_FASTFORWARD) - mode = 4; - if (status_get_ffmode() == STATUS_FASTBACKWARD) - mode = 5; - - if (intval) { - *intval = mode; - } - - snprintf(buf, buf_size, "%d", mode-1); - return buf; - } - - case WPS_TOKEN_REPEAT_MODE: - if (intval) - *intval = global_settings.repeat_mode + 1; - snprintf(buf, buf_size, "%d", global_settings.repeat_mode); - return buf; - - case WPS_TOKEN_RTC_PRESENT: -#if CONFIG_RTC - return "c"; -#else - return NULL; -#endif - -#if CONFIG_RTC - case WPS_TOKEN_RTC_12HOUR_CFG: - if (intval) - *intval = global_settings.timeformat + 1; - snprintf(buf, buf_size, "%d", global_settings.timeformat); - return buf; - - case WPS_TOKEN_RTC_DAY_OF_MONTH: - /* d: day of month (01..31) */ - snprintf(buf, buf_size, "%02d", tm->tm_mday); - return buf; - - case WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED: - /* e: day of month, blank padded ( 1..31) */ - snprintf(buf, buf_size, "%2d", tm->tm_mday); - return buf; - - case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED: - /* H: hour (00..23) */ - snprintf(buf, buf_size, "%02d", tm->tm_hour); - return buf; - - case WPS_TOKEN_RTC_HOUR_24: - /* k: hour ( 0..23) */ - snprintf(buf, buf_size, "%2d", tm->tm_hour); - return buf; - - case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED: - /* I: hour (01..12) */ - snprintf(buf, buf_size, "%02d", - (tm->tm_hour % 12 == 0) ? 12 : tm->tm_hour % 12); - return buf; - - case WPS_TOKEN_RTC_HOUR_12: - /* l: hour ( 1..12) */ - snprintf(buf, buf_size, "%2d", - (tm->tm_hour % 12 == 0) ? 12 : tm->tm_hour % 12); - return buf; - - case WPS_TOKEN_RTC_MONTH: - /* m: month (01..12) */ - if (intval) - *intval = tm->tm_mon + 1; - snprintf(buf, buf_size, "%02d", tm->tm_mon + 1); - return buf; - - case WPS_TOKEN_RTC_MINUTE: - /* M: minute (00..59) */ - snprintf(buf, buf_size, "%02d", tm->tm_min); - return buf; - - case WPS_TOKEN_RTC_SECOND: - /* S: second (00..59) */ - snprintf(buf, buf_size, "%02d", tm->tm_sec); - return buf; - - case WPS_TOKEN_RTC_YEAR_2_DIGITS: - /* y: last two digits of year (00..99) */ - snprintf(buf, buf_size, "%02d", tm->tm_year % 100); - return buf; - - case WPS_TOKEN_RTC_YEAR_4_DIGITS: - /* Y: year (1970...) */ - snprintf(buf, buf_size, "%04d", tm->tm_year + 1900); - return buf; - - case WPS_TOKEN_RTC_AM_PM_UPPER: - /* p: upper case AM or PM indicator */ - return tm->tm_hour/12 == 0 ? "AM" : "PM"; - - case WPS_TOKEN_RTC_AM_PM_LOWER: - /* P: lower case am or pm indicator */ - return tm->tm_hour/12 == 0 ? "am" : "pm"; - - case WPS_TOKEN_RTC_WEEKDAY_NAME: - /* a: abbreviated weekday name (Sun..Sat) */ - return str(LANG_WEEKDAY_SUNDAY + tm->tm_wday); - - case WPS_TOKEN_RTC_MONTH_NAME: - /* b: abbreviated month name (Jan..Dec) */ - return str(LANG_MONTH_JANUARY + tm->tm_mon); - - case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON: - /* u: day of week (1..7); 1 is Monday */ - if (intval) - *intval = (tm->tm_wday == 0) ? 7 : tm->tm_wday; - snprintf(buf, buf_size, "%1d", tm->tm_wday + 1); - return buf; - - case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN: - /* w: day of week (0..6); 0 is Sunday */ - if (intval) - *intval = tm->tm_wday + 1; - snprintf(buf, buf_size, "%1d", tm->tm_wday); - return buf; -#else - case WPS_TOKEN_RTC_DAY_OF_MONTH: - case WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED: - case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED: - case WPS_TOKEN_RTC_HOUR_24: - case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED: - case WPS_TOKEN_RTC_HOUR_12: - case WPS_TOKEN_RTC_MONTH: - case WPS_TOKEN_RTC_MINUTE: - case WPS_TOKEN_RTC_SECOND: - case WPS_TOKEN_RTC_AM_PM_UPPER: - case WPS_TOKEN_RTC_AM_PM_LOWER: - case WPS_TOKEN_RTC_YEAR_2_DIGITS: - return "--"; - case WPS_TOKEN_RTC_YEAR_4_DIGITS: - return "----"; - case WPS_TOKEN_RTC_WEEKDAY_NAME: - case WPS_TOKEN_RTC_MONTH_NAME: - return "---"; - case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON: - case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN: - return "-"; -#endif - -#ifdef HAVE_LCD_CHARCELLS - case WPS_TOKEN_PROGRESSBAR: - { - char *end = utf8encode(data->wps_progress_pat[0], buf); - *end = '\0'; - return buf; - } - - case WPS_TOKEN_PLAYER_PROGRESSBAR: - if(is_new_player()) - { - /* we need 11 characters (full line) for - progress-bar */ - strlcpy(buf, " ", buf_size); - } - else - { - /* Tell the user if we have an OldPlayer */ - strlcpy(buf, " ", buf_size); - } - return buf; -#endif - -#ifdef HAVE_TAGCACHE - case WPS_TOKEN_DATABASE_PLAYCOUNT: - if (intval) { - *intval = id3->playcount + 1; - } - snprintf(buf, buf_size, "%ld", id3->playcount); - return buf; - - case WPS_TOKEN_DATABASE_RATING: - if (intval) { - *intval = id3->rating + 1; - } - snprintf(buf, buf_size, "%d", id3->rating); - return buf; - - case WPS_TOKEN_DATABASE_AUTOSCORE: - if (intval) - *intval = id3->score + 1; - - snprintf(buf, buf_size, "%d", id3->score); - return buf; -#endif - -#if (CONFIG_CODEC == SWCODEC) - case WPS_TOKEN_CROSSFADE: - if (intval) - *intval = global_settings.crossfade + 1; - snprintf(buf, buf_size, "%d", global_settings.crossfade); - return buf; - - case WPS_TOKEN_REPLAYGAIN: - { - int val; - - if (global_settings.replaygain_type == REPLAYGAIN_OFF) - val = 1; /* off */ - else - { - int type = - get_replaygain_mode(id3->track_gain_string != NULL, - id3->album_gain_string != NULL); - if (type < 0) - val = 6; /* no tag */ - else - val = type + 2; - - if (global_settings.replaygain_type == REPLAYGAIN_SHUFFLE) - val += 2; - } - - if (intval) - *intval = val; - - switch (val) - { - case 1: - case 6: - return "+0.00 dB"; - break; - case 2: - case 4: - strlcpy(buf, id3->track_gain_string, buf_size); - break; - case 3: - case 5: - strlcpy(buf, id3->album_gain_string, buf_size); - break; - } - return buf; - } -#endif /* (CONFIG_CODEC == SWCODEC) */ - -#if (CONFIG_CODEC != MAS3507D) - case WPS_TOKEN_SOUND_PITCH: - { - int val = sound_get_pitch(); - snprintf(buf, buf_size, "%d.%d", - val / 10, val % 10); - return buf; - } -#endif - - case WPS_TOKEN_MAIN_HOLD: -#ifdef HAS_BUTTON_HOLD - if (button_hold()) -#else - if (is_keys_locked()) -#endif /*hold switch or softlock*/ - return "h"; - else - return NULL; - -#ifdef HAS_REMOTE_BUTTON_HOLD - case WPS_TOKEN_REMOTE_HOLD: - if (remote_button_hold()) - return "r"; - else - return NULL; -#endif - -#if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD) - case WPS_TOKEN_VLED_HDD: - if(led_read(HZ/2)) - return "h"; - else - return NULL; -#endif - case WPS_TOKEN_BUTTON_VOLUME: - if (data->button_time_volume && - TIME_BEFORE(current_tick, data->button_time_volume + - token->value.i * TIMEOUT_UNIT)) - return "v"; - return NULL; - case WPS_TOKEN_LASTTOUCH: -#ifdef HAVE_TOUCHSCREEN - if (TIME_BEFORE(current_tick, token->value.i * TIMEOUT_UNIT + - touchscreen_last_touch())) - return "t"; -#endif - return NULL; - - case WPS_TOKEN_SETTING: - { - if (intval) - { - /* Handle contionals */ - const struct settings_list *s = settings+token->value.i; - switch (s->flags&F_T_MASK) - { - case F_T_INT: - case F_T_UINT: - if (s->flags&F_RGB) - /* %?St|name|<#000000|#000001|...|#FFFFFF> */ - /* shouldn't overflow since colors are stored - * on 16 bits ... - * but this is pretty useless anyway */ - *intval = *(int*)s->setting + 1; - else if (s->cfg_vals == NULL) - /* %?St|name|<1st choice|2nd choice|...> */ - *intval = (*(int*)s->setting-s->int_setting->min) - /s->int_setting->step + 1; - else - /* %?St|name|<1st choice|2nd choice|...> */ - /* Not sure about this one. cfg_name/vals are - * indexed from 0 right? */ - *intval = *(int*)s->setting + 1; - break; - case F_T_BOOL: - /* %?St|name| */ - *intval = *(bool*)s->setting?1:2; - break; - case F_T_CHARPTR: - /* %?St|name| - * The string's emptyness discards the setting's - * prefix and suffix */ - *intval = ((char*)s->setting)[0]?1:2; - break; - default: - /* This shouldn't happen ... but you never know */ - *intval = -1; - break; - } - } - cfg_to_string(token->value.i,buf,buf_size); - return buf; - } - - default: - return NULL; - } -} - -/* Return the index to the end token for the conditional token at index. - The conditional token can be either a start token or a separator - (i.e. option) token. -*/ -static int find_conditional_end(struct wps_data *data, int index) -{ - int ret = index; - while (data->tokens[ret].type != WPS_TOKEN_CONDITIONAL_END) - ret = data->tokens[ret].value.i; - - /* ret now is the index to the end token for the conditional. */ - return ret; -} - -/* Evaluate the conditional that is at *token_index and return whether a skip - has ocurred. *token_index is updated with the new position. -*/ -static bool evaluate_conditional(struct gui_wps *gwps, int *token_index) -{ - if (!gwps) - return false; - - struct wps_data *data = gwps->data; - - int i, cond_end; - int cond_index = *token_index; - char result[128]; - const char *value; - unsigned char num_options = data->tokens[cond_index].value.i & 0xFF; - unsigned char prev_val = (data->tokens[cond_index].value.i & 0xFF00) >> 8; - - /* treat ?xx constructs as if they had 2 options. */ - if (num_options < 2) - num_options = 2; - - int intval = num_options; - /* get_token_value needs to know the number of options in the enum */ - value = get_token_value(gwps, &data->tokens[cond_index + 1], - result, sizeof(result), &intval); - - /* intval is now the number of the enum option we want to read, - starting from 1. If intval is -1, we check if value is empty. */ - if (intval == -1) - intval = (value && *value) ? 1 : num_options; - else if (intval > num_options || intval < 1) - intval = num_options; - - data->tokens[cond_index].value.i = (intval << 8) + num_options; - - /* skip to the appropriate enum case */ - int next = cond_index + 2; - for (i = 1; i < intval; i++) - { - next = data->tokens[next].value.i; - } - *token_index = next; - - if (prev_val == intval) - { - /* Same conditional case as previously. Return without clearing the - pictures */ - return false; - } - - cond_end = find_conditional_end(data, cond_index + 2); - for (i = cond_index + 3; i < cond_end; i++) - { -#ifdef HAVE_LCD_BITMAP - /* clear all pictures in the conditional and nested ones */ - if (data->tokens[i].type == WPS_TOKEN_IMAGE_PRELOAD_DISPLAY) - clear_image_pos(gwps, data->tokens[i].value.i & 0xFF); -#endif -#ifdef HAVE_ALBUMART - if (data->tokens[i].type == WPS_TOKEN_ALBUMART_DISPLAY) - draw_album_art(gwps, audio_current_aa_hid(), true); -#endif - } - - return true; -} - -/* Read a (sub)line to the given alignment format buffer. - linebuf is the buffer where the data is actually stored. - align is the alignment format that'll be used to display the text. - The return value indicates whether the line needs to be updated. -*/ -static bool get_line(struct gui_wps *gwps, - int line, int subline, - struct align_pos *align, - char *linebuf, - int linebuf_size) -{ - struct wps_data *data = gwps->data; - - char temp_buf[128]; - char *buf = linebuf; /* will always point to the writing position */ - char *linebuf_end = linebuf + linebuf_size - 1; - int i, last_token_idx; - bool update = false; - - /* alignment-related variables */ - int cur_align; - char* cur_align_start; - cur_align_start = buf; - cur_align = WPS_ALIGN_LEFT; - align->left = NULL; - align->center = NULL; - align->right = NULL; - - /* Process all tokens of the desired subline */ - last_token_idx = wps_last_token_index(data, line, subline); - for (i = wps_first_token_index(data, line, subline); - i <= last_token_idx; i++) - { - switch(data->tokens[i].type) - { - case WPS_TOKEN_CONDITIONAL: - /* place ourselves in the right conditional case */ - update |= evaluate_conditional(gwps, &i); - break; - - case WPS_TOKEN_CONDITIONAL_OPTION: - /* we've finished in the curent conditional case, - skip to the end of the conditional structure */ - i = find_conditional_end(data, i); - break; - -#ifdef HAVE_LCD_BITMAP - case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY: - { - struct gui_img *img = data->img; - int n = data->tokens[i].value.i & 0xFF; - int subimage = data->tokens[i].value.i >> 8; - - if (n >= 0 && n < MAX_IMAGES && img[n].loaded) - img[n].display = subimage; - break; - } -#endif - - case WPS_TOKEN_ALIGN_LEFT: - case WPS_TOKEN_ALIGN_CENTER: - case WPS_TOKEN_ALIGN_RIGHT: - /* 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 (data->tokens[i].type) - { - case WPS_TOKEN_ALIGN_LEFT: - cur_align = WPS_ALIGN_LEFT; - break; - case WPS_TOKEN_ALIGN_CENTER: - cur_align = WPS_ALIGN_CENTER; - break; - case WPS_TOKEN_ALIGN_RIGHT: - cur_align = WPS_ALIGN_RIGHT; - break; - default: - break; - } - *buf++ = 0; - cur_align_start = buf; - break; - case WPS_VIEWPORT_ENABLE: - { - char label = data->tokens[i].value.i; - int j; - char temp = VP_DRAW_HIDEABLE; - for(j=0;jnum_viewports;j++) - { - temp = VP_DRAW_HIDEABLE; - if ((data->viewports[j].hidden_flags&VP_DRAW_HIDEABLE) && - (data->viewports[j].label == label)) - { - if (data->viewports[j].hidden_flags&VP_DRAW_WASHIDDEN) - temp |= VP_DRAW_WASHIDDEN; - data->viewports[j].hidden_flags = temp; - } - } - } - break; - default: - { - /* get the value of the tag and copy it to the buffer */ - const char *value = get_token_value(gwps, &data->tokens[i], - temp_buf, sizeof(temp_buf), NULL); - if (value) - { - update = true; - while (*value && (buf < linebuf_end)) - *buf++ = *value++; - } - break; - } - } - } - - /* close the current alignment */ - 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; - } - - return update; -} - -static void get_subline_timeout(struct gui_wps *gwps, int line, int subline) -{ - struct wps_data *data = gwps->data; - int i; - int subline_idx = wps_subline_index(data, line, subline); - int last_token_idx = wps_last_token_index(data, line, subline); - - data->sublines[subline_idx].time_mult = DEFAULT_SUBLINE_TIME_MULTIPLIER; - - for (i = wps_first_token_index(data, line, subline); - i <= last_token_idx; i++) - { - switch(data->tokens[i].type) - { - case WPS_TOKEN_CONDITIONAL: - /* place ourselves in the right conditional case */ - evaluate_conditional(gwps, &i); - break; - - case WPS_TOKEN_CONDITIONAL_OPTION: - /* we've finished in the curent conditional case, - skip to the end of the conditional structure */ - i = find_conditional_end(data, i); - break; - - case WPS_TOKEN_SUBLINE_TIMEOUT: - data->sublines[subline_idx].time_mult = data->tokens[i].value.i; - break; - - default: - break; - } - } -} - -/* Calculates which subline should be displayed for the specified line - Returns true iff the subline must be refreshed */ -static bool update_curr_subline(struct gui_wps *gwps, int line) -{ - struct wps_data *data = gwps->data; - - int search, search_start, num_sublines; - bool reset_subline; - bool new_subline_refresh; - bool only_one_subline; - - num_sublines = data->lines[line].num_sublines; - reset_subline = (data->lines[line].curr_subline == SUBLINE_RESET); - new_subline_refresh = false; - only_one_subline = false; - - /* if time to advance to next sub-line */ - if (TIME_AFTER(current_tick, data->lines[line].subline_expire_time - 1) || - reset_subline) - { - /* search all sublines until the next subline with time > 0 - is found or we get back to the subline we started with */ - if (reset_subline) - search_start = 0; - else - search_start = data->lines[line].curr_subline; - - for (search = 0; search < num_sublines; search++) - { - data->lines[line].curr_subline++; - - /* wrap around if beyond last defined subline or WPS_MAX_SUBLINES */ - if (data->lines[line].curr_subline == num_sublines) - { - if (data->lines[line].curr_subline == 1) - only_one_subline = true; - data->lines[line].curr_subline = 0; - } - - /* if back where we started after search or - only one subline is defined on the line */ - if (((search > 0) && - (data->lines[line].curr_subline == search_start)) || - only_one_subline) - { - /* no other subline with a time > 0 exists */ - data->lines[line].subline_expire_time = (reset_subline ? - current_tick : - data->lines[line].subline_expire_time) + 100 * HZ; - break; - } - else - { - /* get initial time multiplier for this subline */ - get_subline_timeout(gwps, line, data->lines[line].curr_subline); - - int subline_idx = wps_subline_index(data, line, - data->lines[line].curr_subline); - - /* only use this subline if subline time > 0 */ - if (data->sublines[subline_idx].time_mult > 0) - { - new_subline_refresh = true; - data->lines[line].subline_expire_time = (reset_subline ? - current_tick : data->lines[line].subline_expire_time) + - TIMEOUT_UNIT*data->sublines[subline_idx].time_mult; - break; - } - } - } - } - - return new_subline_refresh; -} - -/* Display a line appropriately according to its alignment format. - format_align contains the text, separated between left, center and right. - line is the index of the line on the screen. - scroll indicates whether the line is a scrolling one or not. -*/ -static void write_line(struct screen *display, - struct align_pos *format_align, - int line, - bool scroll) -{ - int left_width = 0, left_xpos; - int center_width = 0, center_xpos; - int right_width = 0, right_xpos; - int ypos; - int space_width; - int string_height; - int scroll_width; - - /* calculate different string sizes and positions */ - display->getstringsize((unsigned char *)" ", &space_width, &string_height); - if (format_align->left != 0) { - display->getstringsize((unsigned char *)format_align->left, - &left_width, &string_height); - } - - if (format_align->right != 0) { - display->getstringsize((unsigned char *)format_align->right, - &right_width, &string_height); - } - - if (format_align->center != 0) { - display->getstringsize((unsigned char *)format_align->center, - ¢er_width, &string_height); - } - - left_xpos = 0; - right_xpos = (display->getwidth() - right_width); - center_xpos = (display->getwidth() + left_xpos - center_width) / 2; - - scroll_width = display->getwidth() - left_xpos; - - /* 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 */ - *(--format_align->center) = ' '; - /* calculate the new width and position of the merged string */ - left_width = left_width + space_width + center_width; - /* 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 */ - format_align->left = format_align->center; - /* calculate the new width and position of the string */ - left_width = center_width; - /* 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 */ - *(--format_align->right) = ' '; - /* move the center string to the right after merge */ - format_align->right = format_align->center; - /* calculate the new width and position of the merged string */ - right_width = center_width + space_width + right_width; - right_xpos = (display->getwidth() - 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 */ - format_align->right = format_align->center; - /* calculate the new width and position of the string */ - right_width = center_width; - right_xpos = (display->getwidth() - 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 */ - *(--format_align->right) = ' '; - /* calculate the new width and position of the string */ - left_width = left_width + space_width + right_width; - /* 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_width > right_xpos)) { - /* move the right string to the left string */ - format_align->left = format_align->right; - /* calculate the new width and position of the string */ - left_width = right_width; - /* there is no right string anymore */ - right_width = 0; - } - - ypos = (line * string_height); - - - if (scroll && ((left_width > scroll_width) || - (center_width > scroll_width) || - (right_width > scroll_width))) - { - display->puts_scroll(0, line, - (unsigned char *)format_align->left); - } - else - { -#ifdef HAVE_LCD_BITMAP - /* clear the line first */ - display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID); - display->fillrect(left_xpos, ypos, display->getwidth(), string_height); - display->set_drawmode(DRMODE_SOLID); -#endif - - /* Nasty hack: we output an empty scrolling string, - which will reset the scroller for that line */ - display->puts_scroll(0, line, (unsigned char *)""); - - /* print aligned strings */ - if (left_width != 0) - { - display->putsxy(left_xpos, ypos, - (unsigned char *)format_align->left); - } - if (center_width != 0) - { - display->putsxy(center_xpos, ypos, - (unsigned char *)format_align->center); - } - if (right_width != 0) - { - display->putsxy(right_xpos, ypos, - (unsigned char *)format_align->right); - } - } -} - -bool gui_wps_redraw(struct gui_wps *gwps, - int ffwd_offset, - unsigned refresh_mode) -{ - struct wps_data *data = gwps->data; - struct screen *display = gwps->display; - struct wps_state *state = gwps->state; - - if (!data || !state || !display) - return false; - - struct mp3entry *id3 = state->id3; - - if (!id3) - return false; - - int v, line, i, subline_idx; - unsigned flags; - char linebuf[MAX_PATH]; - - struct align_pos align; - align.left = NULL; - align.center = NULL; - align.right = NULL; - - bool update_line, new_subline_refresh; - -#ifdef HAVE_LCD_BITMAP - - /* 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; - -#endif - - /* reset to first subline if refresh all flag is set */ - if (refresh_mode == WPS_REFRESH_ALL) - { - display->set_viewport(&data->viewports[0].vp); - display->clear_viewport(); - - for (i = 0; i <= data->num_lines; i++) - { - data->lines[i].curr_subline = 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 - - state->ff_rewind_count = ffwd_offset; - - /* disable any viewports which are conditionally displayed */ - for (v = 0; v < data->num_viewports; v++) - { - if (data->viewports[v].hidden_flags&VP_DRAW_HIDEABLE) - { - if (data->viewports[v].hidden_flags&VP_DRAW_HIDDEN) - data->viewports[v].hidden_flags |= VP_DRAW_WASHIDDEN; - else - data->viewports[v].hidden_flags |= VP_DRAW_HIDDEN; - } - } - for (v = 0; v < data->num_viewports; v++) - { - struct wps_viewport *wps_vp = &(data->viewports[v]); - unsigned vp_refresh_mode = refresh_mode; - display->set_viewport(&wps_vp->vp); - -#ifdef HAVE_LCD_BITMAP - /* Set images to not to be displayed */ - for (i = 0; i < MAX_IMAGES; i++) - { - data->img[i].display = -1; - } -#endif - /* dont redraw the viewport if its disabled */ - if ((wps_vp->hidden_flags&VP_DRAW_HIDDEN)) - { - if (!(wps_vp->hidden_flags&VP_DRAW_WASHIDDEN)) - display->scroll_stop(&wps_vp->vp); - wps_vp->hidden_flags |= VP_DRAW_WASHIDDEN; - continue; - } - else if (((wps_vp->hidden_flags& - (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE)) - == (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE))) - { - vp_refresh_mode = WPS_REFRESH_ALL; - wps_vp->hidden_flags = VP_DRAW_HIDEABLE; - } - if (vp_refresh_mode == WPS_REFRESH_ALL) - { - display->clear_viewport(); - } - - for (line = wps_vp->first_line; - line <= wps_vp->last_line; line++) - { - memset(linebuf, 0, sizeof(linebuf)); - update_line = false; - - /* get current subline for the line */ - new_subline_refresh = update_curr_subline(gwps, line); - - subline_idx = wps_subline_index(data, line, - data->lines[line].curr_subline); - flags = data->sublines[subline_idx].line_type; - - if (vp_refresh_mode == WPS_REFRESH_ALL || (flags & vp_refresh_mode) - || new_subline_refresh) - { - /* get_line tells us if we need to update the line */ - update_line = get_line(gwps, line, data->lines[line].curr_subline, - &align, linebuf, sizeof(linebuf)); - } -#ifdef HAVE_LCD_BITMAP - /* peakmeter */ - if (flags & vp_refresh_mode & WPS_REFRESH_PEAK_METER) - { - /* the peakmeter should be alone on its line */ - update_line = false; - - int h = font_get(wps_vp->vp.font)->height; - int peak_meter_y = (line - wps_vp->first_line)* h; - - /* 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 <= display->getheight()) { - /* found a line with a peak meter -> remember that we must - enable it later */ - enable_pm = true; - peak_meter_enabled = true; - peak_meter_screen(gwps->display, 0, peak_meter_y, - MIN(h, display->getheight() - peak_meter_y)); - } - else - { - peak_meter_enabled = false; - } - } - -#else /* HAVE_LCD_CHARCELL */ - - /* progressbar */ - if (flags & vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS) - { - if (data->full_line_progressbar) - draw_player_fullbar(gwps, linebuf, sizeof(linebuf)); - else - draw_player_progress(gwps); - } -#endif - - if (update_line && - /* conditionals clear the line which means if the %Vd is put into the default - viewport there will be a blank line. - To get around this we dont allow any actual drawing to happen in the - deault vp if other vp's are defined */ - ((data->num_viewports>1 && v!=0) || data->num_viewports == 1)) - { - if (flags & WPS_REFRESH_SCROLL) - { - /* if the line is a scrolling one we don't want to update - too often, so that it has the time to scroll */ - if ((vp_refresh_mode & WPS_REFRESH_SCROLL) || new_subline_refresh) - write_line(display, &align, line - wps_vp->first_line, true); - } - else - write_line(display, &align, line - wps_vp->first_line, false); - } - } - -#ifdef HAVE_LCD_BITMAP - /* progressbar */ - if (vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS) - { - if (wps_vp->pb) - { - draw_progressbar(gwps, wps_vp); - } - } - /* Now display any images in this viewport */ - wps_display_images(gwps, &wps_vp->vp); -#endif - } - -#ifdef HAVE_LCD_BITMAP - data->peak_meter_enabled = enable_pm; -#endif - - if (refresh_mode & WPS_REFRESH_STATUSBAR) - { - gwps_draw_statusbars(); - } - /* Restore the default viewport */ - display->set_viewport(NULL); - - display->update(); - - return true; -} diff --git a/apps/gui/gwps-common.h b/apps/gui/gwps-common.h deleted file mode 100644 index 0d196b28e4..0000000000 --- a/apps/gui/gwps-common.h +++ /dev/null @@ -1,53 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2002 Björn Stenberg - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ****************************************************************************/ -#ifndef _GWPS_COMMON_ -#define _GWPS_COMMON_ -#include -#include /* for size_t */ - -#include "gwps.h" - -/* fades the volume, e.g. on pause or stop */ -void fade(bool fade_in, bool updatewps); - -/* Initially display the wps, can fall back to the built-in wps - * if the chosen wps is invalid. - * - * Return true on success, otherwise false */ -bool gui_wps_display(struct gui_wps *gui_wps); - -/* return true if screen restore is needed - return false otherwise */ -bool update_onvol_change(struct gui_wps * gwps); - -/* Update track info related stuff, handle cue sheets as well, and redraw */ -bool gui_wps_update(struct gui_wps *gwps); - -bool ffwd_rew(int button); -void display_keylock_text(bool locked); - -/* Refresh the WPS according to refresh_mode. - * - * Return true on success, otherwise false */ -bool gui_wps_redraw(struct gui_wps *gwps, - int ffwd_offset, - unsigned refresh_mode); -#endif diff --git a/apps/gui/gwps.c b/apps/gui/gwps.c deleted file mode 100644 index 10c2a6806b..0000000000 --- a/apps/gui/gwps.c +++ /dev/null @@ -1,984 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2002 Jerome Kuptz - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ****************************************************************************/ -#include -#include -#include -#include "config.h" - -#include "system.h" -#include "file.h" -#include "lcd.h" -#include "font.h" -#include "backlight.h" -#include "action.h" -#include "kernel.h" -#include "filetypes.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 "storage.h" -#include "screens.h" -#include "playlist.h" -#ifdef HAVE_LCD_BITMAP -#include "icons.h" -#include "peakmeter.h" -#endif -#include "lang.h" -#include "bookmark.h" -#include "misc.h" -#include "sound.h" -#include "onplay.h" -#include "abrepeat.h" -#include "playback.h" -#include "splash.h" -#include "cuesheet.h" -#include "ata_idle_notify.h" -#include "root_menu.h" -#include "backdrop.h" -#include "quickscreen.h" -#include "pitchscreen.h" -#include "appevents.h" -#include "viewport.h" -#include "pcmbuf.h" -#include "option_select.h" -#include "dsp.h" -#include "playlist_viewer.h" - -#define RESTORE_WPS_INSTANTLY 0l -#define RESTORE_WPS_NEXT_SECOND ((long)(HZ+current_tick)) -/* in milliseconds */ -#define DEFAULT_SKIP_TRESH 3000ul - -static int wpsbars; -/* currently only one wps_state is needed */ -struct wps_state wps_state; -struct gui_wps gui_wps[NB_SCREENS]; -static struct wps_data wps_datas[NB_SCREENS]; - -/* initial setup of wps_data */ -static void wps_state_init(void); -static void track_changed_callback(void *param); -static void nextid3available_callback(void* param); - - -#if defined(HAVE_BACKLIGHT) || defined(HAVE_REMOTE_LCD) -static void gwps_caption_backlight(struct wps_state *state) -{ - if (state && state->id3) - { -#ifdef HAVE_BACKLIGHT - if (global_settings.caption_backlight) - { - /* 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 = 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)) && - (state->paused == false)) - backlight_on(); - } -#endif -#ifdef HAVE_REMOTE_LCD - if (global_settings.remote_caption_backlight) - { - /* turn on remote backlight n seconds before track ends, and turn it - off n seconds into the new track. n == remote_backlight_timeout, - or 5s */ - int n = global_settings.remote_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)) && - (state->paused == false)) - remote_backlight_on(); - } -#endif - } -} -#endif - - -static void change_dir(int direction) -{ - if (global_settings.prevent_skip) - return; - - if (direction < 0) - audio_prev_dir(); - else if (direction > 0) - audio_next_dir(); - /* prevent the next dir to immediatly start being ffw'd */ - action_wait_for_release(); -} - -static void prev_track(unsigned long skip_thresh) -{ - if (wps_state.id3->elapsed < skip_thresh) - { - audio_prev(); - return; - } - else - { - if (wps_state.id3->cuesheet) - { - curr_cuesheet_skip(wps_state.id3->cuesheet, -1, wps_state.id3->elapsed); - return; - } - - if (!wps_state.paused) -#if (CONFIG_CODEC == SWCODEC) - audio_pre_ff_rewind(); -#else - audio_pause(); -#endif - - audio_ff_rewind(0); - -#if (CONFIG_CODEC != SWCODEC) - if (!wps_state.paused) - audio_resume(); -#endif - } -} - -static void next_track(void) -{ - /* take care of if we're playing a cuesheet */ - if (wps_state.id3->cuesheet) - { - if (curr_cuesheet_skip(wps_state.id3->cuesheet, 1, wps_state.id3->elapsed)) - { - /* if the result was false, then we really want - to skip to the next track */ - return; - } - } - - audio_next(); -} - -static void play_hop(int direction) -{ - unsigned long step = ((unsigned long)global_settings.skip_length)*1000; - unsigned long elapsed = wps_state.id3->elapsed; - unsigned long remaining = wps_state.id3->length - elapsed; - - if (!global_settings.prevent_skip && - (!step || - (direction > 0 && step >= remaining) || - (direction < 0 && elapsed < DEFAULT_SKIP_TRESH))) - { /* Do normal track skipping */ - if (direction > 0) - next_track(); - else if (direction < 0) - prev_track(DEFAULT_SKIP_TRESH); - return; - } - - if (direction == 1 && step >= remaining) - { -#if CONFIG_CODEC == SWCODEC - if(global_settings.beep) - pcmbuf_beep(1000, 150, 1500*global_settings.beep); -#endif - return; - } - else if ((direction == -1 && elapsed < step)) - { - elapsed = 0; - } - else - { - elapsed += step * direction; - } - if((audio_status() & AUDIO_STATUS_PLAY) && !wps_state.paused) - { -#if (CONFIG_CODEC == SWCODEC) - audio_pre_ff_rewind(); -#else - audio_pause(); -#endif - } - audio_ff_rewind(wps_state.id3->elapsed = elapsed); -#if (CONFIG_CODEC != SWCODEC) - if (!wps_state.paused) - audio_resume(); -#endif -} - -static void gwps_fix_statusbars(void) -{ -#ifdef HAVE_LCD_BITMAP - int i; - wpsbars = VP_SB_HIDE_ALL; - FOR_NB_SCREENS(i) - { - bool draw = false; - if (gui_wps[i].data->wps_sb_tag) - draw = gui_wps[i].data->show_sb_on_wps; - else if (statusbar_position(i) != STATUSBAR_OFF) - draw = true; - if (draw) - wpsbars |= (VP_SB_ONSCREEN(i) | VP_SB_IGNORE_SETTING(i)); - } -#else - wpsbars = VP_SB_ALLSCREENS; -#endif -} - -#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP) -/* - * If the user is unable to see the wps, because the display is deactivated, - * we suppress updates until the wps is activated again (the lcd driver will - * call this hook to issue an instant update) - * */ -static void wps_lcd_activation_hook(void) -{ - wps_state.do_full_update = true; - /* force timeout in wps main loop, so that the update is instantly */ - queue_post(&button_queue, BUTTON_NONE, 0); -} -#endif - -static void gwps_leave_wps(void) -{ - int i, oldbars = VP_SB_HIDE_ALL; - - FOR_NB_SCREENS(i) - gui_wps[i].display->stop_scroll(); - if (global_settings.statusbar) - oldbars = VP_SB_ALLSCREENS; - -#if LCD_DEPTH > 1 - show_main_backdrop(); -#endif -#if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1 - show_remote_main_backdrop(); -#endif - viewportmanager_set_statusbar(oldbars); -#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP) - /* Play safe and unregister the hook */ - lcd_activation_set_hook(NULL); -#endif -} - -void gwps_draw_statusbars(void) -{ - viewportmanager_set_statusbar(wpsbars); -} -#ifdef HAVE_TOUCHSCREEN -int wps_get_touchaction(struct wps_data *data) -{ - short x,y; - short vx, vy; - int type = action_get_touchscreen_press(&x, &y); - int i; - static int last_action = ACTION_NONE; - struct touchregion *r; - bool repeated = (type == BUTTON_REPEAT); - bool released = (type == BUTTON_REL); - for (i=0; itouchregion_count; i++) - { - r = &data->touchregion[i]; - /* make sure this region's viewport is visible */ - if (r->wvp->hidden_flags&VP_DRAW_HIDDEN) - continue; - /* reposition the touch inside the viewport */ - vx = x - r->wvp->vp.x; - vy = y - r->wvp->vp.y; - /* check if it's inside this viewport */ - if (vx >= 0 && vx < r->wvp->vp.x + r->wvp->vp.width && - vy >= 0 && vy < r->wvp->vp.y + r->wvp->vp.height) - { - /* now see if the point is inside this region */ - if (vx >= r->x && vx < r->x+r->width && - vy >= r->y && vy < r->y+r->height) - { - if ((repeated && r->repeat) || - (released && !r->repeat)) - { - last_action = r->action; - return r->action; - } - } - } - } - if ((last_action == ACTION_WPS_SEEKBACK || last_action == ACTION_WPS_SEEKFWD)) - return ACTION_WPS_STOPSEEK; - last_action = ACTION_TOUCHSCREEN; - return ACTION_TOUCHSCREEN; -} -#endif -/* The WPS can be left in two ways: - * a) call a function, which draws over the wps. In this case, the wps - * will be still active (i.e. the below function didn't return) - * b) return with a value evaluated by root_menu.c, in this case the wps - * is really left, and root_menu will handle the next screen - * - * In either way, call gwps_leave_wps(), in order to restore the correct - * "main screen" backdrops and statusbars - */ -long gui_wps_show(void) -{ - long button = 0; - bool restore = true; - long restoretimer = RESTORE_WPS_INSTANTLY; /* timer to delay screen redraw temporarily */ - bool exit = false; - bool bookmark = false; - bool update = false; - int i; - long last_left = 0, last_right = 0; - -#ifdef HAVE_LCD_CHARCELLS - status_set_audio(true); - status_set_param(false); -#endif - -#ifdef AB_REPEAT_ENABLE - ab_repeat_init(); - ab_reset_markers(); -#endif - wps_state_init(); - - 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_RAM) && !defined(HAVE_SW_POWEROFF) - call_storage_idle_notifys(true); -#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_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 = get_action(CONTEXT_WPS|ALLOW_SOFTLOCK,TIMEOUT_NOBLOCK); - if (button != ACTION_NONE) { - break; - } - peak_meter_peek(); - sleep(0); /* Sleep until end of current tick. */ - - if (TIME_AFTER(current_tick, next_refresh)) { - FOR_NB_SCREENS(i) - { - if(gui_wps[i].data->peak_meter_enabled) - gui_wps_redraw(&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 -#endif - { - button = get_action(CONTEXT_WPS|ALLOW_SOFTLOCK, - restore ? HZ/100 : HZ/5); - } - - /* Exit if audio has stopped playing. This happens e.g. at end of - playlist or if using the sleep timer. */ - if (!(audio_status() & AUDIO_STATUS_PLAY)) - exit = true; -#ifdef HAVE_TOUCHSCREEN - if (button == ACTION_TOUCHSCREEN) - button = wps_get_touchaction(gui_wps[SCREEN_MAIN].data); -#endif -/* The iPods/X5/M5 use a single button for the A-B mode markers, - defined as ACTION_WPSAB_SINGLE in their config files. */ -#ifdef ACTION_WPSAB_SINGLE - if (!global_settings.party_mode && ab_repeat_mode_enabled()) - { - static int wps_ab_state = 0; - if (button == ACTION_WPSAB_SINGLE) - { - switch (wps_ab_state) - { - case 0: /* set the A spot */ - button = ACTION_WPS_ABSETA_PREVDIR; - break; - case 1: /* set the B spot */ - button = ACTION_WPS_ABSETB_NEXTDIR; - break; - case 2: - button = ACTION_WPS_ABRESET; - break; - } - wps_ab_state = (wps_ab_state+1) % 3; - } - } -#endif - switch(button) - { - case ACTION_WPS_CONTEXT: - { - gwps_leave_wps(); - /* if music is stopped in the context menu we want to exit the wps */ - if (onplay(wps_state.id3->path, - FILE_ATTR_AUDIO, CONTEXT_WPS) == ONPLAY_MAINMENU - || !audio_status()) - return GO_TO_ROOT; - restore = true; - } - break; - - case ACTION_WPS_BROWSE: -#ifdef HAVE_LCD_CHARCELLS - status_set_record(false); - status_set_audio(false); -#endif - gwps_leave_wps(); - return GO_TO_PREVIOUS_BROWSER; - break; - - /* play/pause */ - case ACTION_WPS_PLAY: - if (global_settings.party_mode) - break; - if ( wps_state.paused ) - { - wps_state.paused = false; - if ( global_settings.fade_on_stop ) - fade(true, true); - else - audio_resume(); - } - else - { - wps_state.paused = true; - if ( global_settings.fade_on_stop ) - fade(false, true); - else - audio_pause(); - settings_save(); -#if !defined(HAVE_RTC_RAM) && !defined(HAVE_SW_POWEROFF) - call_storage_idle_notifys(true); /* make sure resume info is saved */ -#endif - } - break; - - case ACTION_WPS_VOLUP: - { - FOR_NB_SCREENS(i) - gui_wps[i].data->button_time_volume = current_tick; - global_settings.volume++; - bool res = false; - setvol(); - FOR_NB_SCREENS(i) - { - if(update_onvol_change(&gui_wps[i])) - res = true; - } - if (res) { - restore = true; - restoretimer = RESTORE_WPS_NEXT_SECOND; - } - } - break; - case ACTION_WPS_VOLDOWN: - { - FOR_NB_SCREENS(i) - gui_wps[i].data->button_time_volume = current_tick; - global_settings.volume--; - setvol(); - bool res = false; - FOR_NB_SCREENS(i) - { - if(update_onvol_change(&gui_wps[i])) - res = true; - } - if (res) { - restore = true; - restoretimer = RESTORE_WPS_NEXT_SECOND; - } - } - break; - /* fast forward - OR next dir if this is straight after ACTION_WPS_SKIPNEXT */ - case ACTION_WPS_SEEKFWD: - if (global_settings.party_mode) - break; - if (current_tick -last_right < HZ) - { - if (wps_state.id3->cuesheet) - { - audio_next(); - } - else - { - change_dir(1); - } - } - else - ffwd_rew(ACTION_WPS_SEEKFWD); - last_right = last_left = 0; - break; - /* fast rewind - OR prev dir if this is straight after ACTION_WPS_SKIPPREV,*/ - case ACTION_WPS_SEEKBACK: - if (global_settings.party_mode) - break; - if (current_tick -last_left < HZ) - { - if (wps_state.id3->cuesheet) - { - if (!wps_state.paused) -#if (CONFIG_CODEC == SWCODEC) - audio_pre_ff_rewind(); -#else - audio_pause(); -#endif - audio_ff_rewind(0); - } - else - { - change_dir(-1); - } - } - else - ffwd_rew(ACTION_WPS_SEEKBACK); - last_left = last_right = 0; - break; - - /* prev / restart */ - case ACTION_WPS_SKIPPREV: - if (global_settings.party_mode) - break; - last_left = current_tick; -#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() ) - { - if ( ab_after_A_marker(wps_state.id3->elapsed) ) - { - ab_jump_to_A_marker(); - break; -#if (AB_REPEAT_ENABLE == 2) - } else { - ab_reset_markers(); -#endif - } - } - else - /* ...otherwise, do it normally */ -#endif - play_hop(-1); - break; - - /* next - OR if skip length set, hop by predetermined amount. */ - case ACTION_WPS_SKIPNEXT: - if (global_settings.party_mode) - break; - last_right = current_tick; -#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() ) - { - if ( ab_before_A_marker(wps_state.id3->elapsed) ) - { - ab_jump_to_A_marker(); - break; -#if (AB_REPEAT_ENABLE == 2) - } else { - ab_reset_markers(); -#endif - } - } - else - /* ...otherwise, do it normally */ -#endif - play_hop(1); - break; - /* next / prev directories */ - /* and set A-B markers if in a-b mode */ - case ACTION_WPS_ABSETB_NEXTDIR: - if (global_settings.party_mode) - break; -#if defined(AB_REPEAT_ENABLE) - if (ab_repeat_mode_enabled()) - { - ab_set_B_marker(wps_state.id3->elapsed); - ab_jump_to_A_marker(); - } - else -#endif - { - change_dir(1); - } - break; - case ACTION_WPS_ABSETA_PREVDIR: - if (global_settings.party_mode) - break; -#if defined(AB_REPEAT_ENABLE) - if (ab_repeat_mode_enabled()) - ab_set_A_marker(wps_state.id3->elapsed); - else -#endif - { - change_dir(-1); - } - break; - /* menu key functions */ - case ACTION_WPS_MENU: - gwps_leave_wps(); - return GO_TO_ROOT; - break; - - -#ifdef HAVE_QUICKSCREEN - case ACTION_WPS_QUICKSCREEN: - { - gwps_leave_wps(); - if (quick_screen_quick(button)) - return SYS_USB_CONNECTED; - restore = true; - } - break; -#endif /* HAVE_QUICKSCREEN */ - - /* screen settings */ -#ifdef BUTTON_F3 - case ACTION_F3: - { - gwps_leave_wps(); - if (quick_screen_f3(BUTTON_F3)) - return SYS_USB_CONNECTED; - restore = true; - } - break; -#endif /* BUTTON_F3 */ - - /* pitch screen */ -#ifdef HAVE_PITCHSCREEN - case ACTION_WPS_PITCHSCREEN: - { - gwps_leave_wps(); - if (1 == gui_syncpitchscreen_run()) - return SYS_USB_CONNECTED; - restore = true; - } - break; -#endif /* HAVE_PITCHSCREEN */ - -#ifdef AB_REPEAT_ENABLE - /* reset A&B markers */ - case ACTION_WPS_ABRESET: - if (ab_repeat_mode_enabled()) - { - ab_reset_markers(); - update = true; - } - break; -#endif /* AB_REPEAT_ENABLE */ - - /* stop and exit wps */ - case ACTION_WPS_STOP: - if (global_settings.party_mode) - break; - bookmark = true; - exit = true; - break; - - case ACTION_WPS_ID3SCREEN: - { - gwps_leave_wps(); - browse_id3(); - restore = true; - } - break; -#ifdef HAVE_TOUCHSCREEN - case ACTION_TOUCH_SHUFFLE: /* toggle shuffle mode */ - { - global_settings.playlist_shuffle = - !global_settings.playlist_shuffle; -#if CONFIG_CODEC == SWCODEC - dsp_set_replaygain(); -#endif - if (global_settings.playlist_shuffle) - playlist_randomise(NULL, current_tick, true); - else - playlist_sort(NULL, true); - } - break; - case ACTION_TOUCH_REPMODE: /* cycle the repeat mode setting */ - { - const struct settings_list *rep_setting = - find_setting(&global_settings.repeat_mode, NULL); - option_select_next_val(rep_setting, false, true); - audio_flush_and_reload_tracks(); - } - break; -#endif /* HAVE_TOUCHSCREEN */ - /* this case is used by the softlock feature - * it requests a full update here */ - case ACTION_REDRAW: - wps_state.do_full_update = true; - break; - case ACTION_NONE: /* Timeout, do a partial update */ - update = true; - ffwd_rew(button); /* hopefully fix the ffw/rwd bug */ - break; -#ifdef HAVE_RECORDING - case ACTION_WPS_REC: - exit = true; - break; -#endif - case SYS_POWEROFF: - default_event_handler(SYS_POWEROFF); - break; - case ACTION_WPS_VIEW_PLAYLIST: - gwps_leave_wps(); - if (playlist_viewer()) /* true if USB connected */ - return SYS_USB_CONNECTED; - restore = true; - break; - default: - if(default_event_handler(button) == SYS_USB_CONNECTED) - return GO_TO_ROOT; - update = true; - break; - } - - if (wps_state.do_full_update || update) - { -#if defined(HAVE_BACKLIGHT) || defined(HAVE_REMOTE_LCD) - gwps_caption_backlight(&wps_state); -#endif - FOR_NB_SCREENS(i) - { -#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP) - if (lcd_active() -#ifdef HAVE_REMOTE_LCD - /* currently, all remotes are readable without backlight - * so still update those */ - || (i == SCREEN_REMOTE) -#endif - ) -#endif - { - gui_wps_update(&gui_wps[i]); - } - } - wps_state.do_full_update = false; - update = false; - } - - if (restore && wps_state.id3 && - ((restoretimer == RESTORE_WPS_INSTANTLY) || - TIME_AFTER(current_tick, restoretimer))) - { - restore = false; - restoretimer = RESTORE_WPS_INSTANTLY; - gwps_fix_statusbars(); -#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP) - lcd_activation_set_hook(wps_lcd_activation_hook); -#endif - FOR_NB_SCREENS(i) - { - screens[i].stop_scroll(); - gui_wps_display(&gui_wps[i]); - } - } - - if (exit) { -#ifdef HAVE_LCD_CHARCELLS - status_set_record(false); - status_set_audio(false); -#endif - if (global_settings.fade_on_stop) - fade(false, true); - - if (bookmark) - bookmark_autobookmark(); - audio_stop(); -#ifdef AB_REPEAT_ENABLE - ab_reset_markers(); -#endif - gwps_leave_wps(); -#ifdef HAVE_RECORDING - if (button == ACTION_WPS_REC) - return GO_TO_RECSCREEN; -#endif - if (global_settings.browse_current) - return GO_TO_PREVIOUS_BROWSER; - return GO_TO_PREVIOUS; - } - - if (button && !IS_SYSEVENT(button) ) - storage_spin(); - } - return GO_TO_ROOT; /* unreachable - just to reduce compiler warnings */ -} - -/* this is called from the playback thread so NO DRAWING! */ -static void track_changed_callback(void *param) -{ - wps_state.id3 = (struct mp3entry*)param; - wps_state.nid3 = audio_next_track(); - if (wps_state.id3->cuesheet) - { - cue_find_current_track(wps_state.id3->cuesheet, wps_state.id3->elapsed); - cue_spoof_id3(wps_state.id3->cuesheet, wps_state.id3); - } - wps_state.do_full_update = true; -} -static void nextid3available_callback(void* param) -{ - (void)param; - wps_state.nid3 = audio_next_track(); - wps_state.do_full_update = true; -} - - -static void wps_state_init(void) -{ - wps_state.ff_rewind = false; - wps_state.paused = false; - if(audio_status() & AUDIO_STATUS_PLAY) - { - wps_state.id3 = audio_current_track(); - wps_state.nid3 = audio_next_track(); - } - else - { - wps_state.id3 = NULL; - wps_state.nid3 = NULL; - } - /* We'll be updating due to restore initialized with true */ - wps_state.do_full_update = false; - /* add the WPS track event callbacks */ - add_event(PLAYBACK_EVENT_TRACK_CHANGE, false, track_changed_callback); - add_event(PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE, false, nextid3available_callback); -} - - -#ifdef HAVE_LCD_BITMAP -static void statusbar_toggle_handler(void *data) -{ - (void)data; - int i; - gwps_fix_statusbars(); - - FOR_NB_SCREENS(i) - { - struct viewport *vp = &gui_wps[i].data->viewports[0].vp; - bool draw = wpsbars & (VP_SB_ONSCREEN(i) | VP_SB_IGNORE_SETTING(i)); - if (!draw) - { - vp->y = 0; - vp->height = screens[i].lcdheight; - } - else - { - bool bar_at_top = statusbar_position(i) != STATUSBAR_BOTTOM; - vp->y = bar_at_top?STATUSBAR_HEIGHT:0; - vp->height = screens[i].lcdheight - STATUSBAR_HEIGHT; - } - } -} -#endif - -void gui_sync_wps_init(void) -{ - int i; - FOR_NB_SCREENS(i) - { - wps_data_init(&wps_datas[i]); -#ifdef HAVE_ALBUMART - wps_datas[i].wps_uses_albumart = 0; -#endif -#ifdef HAVE_REMOTE_LCD - wps_datas[i].remote_wps = (i != 0); -#endif - gui_wps[i].data = &wps_datas[i]; - gui_wps[i].display = &screens[i]; - /* Currently no seperate wps_state needed/possible - so use the only available ( "global" ) one */ - gui_wps[i].state = &wps_state; - } -#ifdef HAVE_LCD_BITMAP - add_event(GUI_EVENT_STATUSBAR_TOGGLE, false, statusbar_toggle_handler); -#endif -#if LCD_DEPTH > 1 - unload_wps_backdrop(); -#endif -#if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1 - unload_remote_wps_backdrop(); -#endif -} - -#ifdef HAVE_ALBUMART -/* Returns true if at least one of the gui_wps screens has an album art - tag in its wps structure */ -bool gui_sync_wps_uses_albumart(void) -{ - int i; - FOR_NB_SCREENS(i) { - struct gui_wps *gwps = &gui_wps[i]; - if (gwps->data && (gwps->data->wps_uses_albumart != WPS_ALBUMART_NONE)) - return true; - } - return false; -} -#endif diff --git a/apps/gui/gwps.h b/apps/gui/gwps.h deleted file mode 100644 index 2acde0dc39..0000000000 --- a/apps/gui/gwps.h +++ /dev/null @@ -1,546 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2007 Nicolas Pennequin - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ****************************************************************************/ -#ifndef _WPS_H -#define _WPS_H - -#include "screen_access.h" -#include "statusbar.h" -#include "metadata.h" - -/* constants used in line_type and as refresh_mode for wps_refresh */ -#define WPS_REFRESH_STATIC (1u<<0) /* line doesn't change over time */ -#define WPS_REFRESH_DYNAMIC (1u<<1) /* line may change (e.g. time flag) */ -#define WPS_REFRESH_SCROLL (1u<<2) /* line scrolls */ -#define WPS_REFRESH_PLAYER_PROGRESS (1u<<3) /* line contains a progress bar */ -#define WPS_REFRESH_PEAK_METER (1u<<4) /* line contains a peak meter */ -#define WPS_REFRESH_STATUSBAR (1u<<5) /* refresh statusbar */ -#define WPS_REFRESH_ALL (0xffffffffu) /* to refresh all line types */ - -/* to refresh only those lines that change over time */ -#define WPS_REFRESH_NON_STATIC (WPS_REFRESH_DYNAMIC| \ - WPS_REFRESH_PLAYER_PROGRESS| \ - WPS_REFRESH_PEAK_METER) -/* alignments */ -#define WPS_ALIGN_RIGHT 32 -#define WPS_ALIGN_CENTER 64 -#define WPS_ALIGN_LEFT 128 - -#ifdef HAVE_ALBUMART - -/* albumart definitions */ -#define WPS_ALBUMART_NONE 0 /* WPS does not contain AA tag */ -#define WPS_ALBUMART_CHECK 1 /* WPS contains AA conditional tag */ -#define WPS_ALBUMART_LOAD 2 /* WPS contains AA tag */ - -#define WPS_ALBUMART_ALIGN_RIGHT 1 /* x align: right */ -#define WPS_ALBUMART_ALIGN_CENTER 2 /* x/y align: center */ -#define WPS_ALBUMART_ALIGN_LEFT 4 /* x align: left */ -#define WPS_ALBUMART_ALIGN_TOP 1 /* y align: top */ -#define WPS_ALBUMART_ALIGN_BOTTOM 4 /* y align: bottom */ - -#endif /* HAVE_ALBUMART */ - -/* wps_data*/ - -#ifdef HAVE_LCD_BITMAP -struct gui_img { - struct bitmap bm; - struct viewport* vp; /* The viewport to display this image in */ - short int x; /* x-pos */ - short int y; /* y-pos */ - short int num_subimages; /* number of sub-images */ - short int subimage_height; /* height of each sub-image */ - short int display; /* -1 for no display, 0..n to display a subimage */ - bool loaded; /* load state */ - bool always_display; /* not using the preload/display mechanism */ -}; - -struct progressbar { - /* regular pb */ - short x; - /* >=0: explicitly set in the tag -> y-coord within the viewport - <0 : not set in the tag -> negated 1-based line number within - the viewport. y-coord will be computed based on the font height */ - short y; - short width; - short height; - /*progressbar image*/ - struct bitmap bm; - bool have_bitmap_pb; -}; -#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 MAX_PROGRESSBARS 3 - -/* The image buffer is big enough to store one full-screen native bitmap, - plus two full-screen mono bitmaps. */ - -#define IMG_BUFSIZE ((LCD_HEIGHT*LCD_WIDTH*LCD_DEPTH/8) \ - + (2*LCD_HEIGHT*LCD_WIDTH/8)) - -#define WPS_MAX_VIEWPORTS 24 -#define WPS_MAX_LINES ((LCD_HEIGHT/5+1) * 2) -#define WPS_MAX_SUBLINES (WPS_MAX_LINES*3) -#define WPS_MAX_TOKENS 1024 -#define WPS_MAX_STRINGS 128 -#define STRING_BUFFER_SIZE 1024 -#define WPS_MAX_COND_LEVEL 10 - -#else - -#define WPS_MAX_VIEWPORTS 2 -#define WPS_MAX_LINES 2 -#define WPS_MAX_SUBLINES 12 -#define WPS_MAX_TOKENS 64 -#define WPS_MAX_STRINGS 32 -#define STRING_BUFFER_SIZE 64 -#define WPS_MAX_COND_LEVEL 5 - -#endif - -#define SUBLINE_RESET -1 - -enum wps_parse_error { - PARSE_OK, - PARSE_FAIL_UNCLOSED_COND, - PARSE_FAIL_INVALID_CHAR, - PARSE_FAIL_COND_SYNTAX_ERROR, - PARSE_FAIL_COND_INVALID_PARAM, - PARSE_FAIL_LIMITS_EXCEEDED, -}; - -enum wps_token_type { - WPS_NO_TOKEN, /* for WPS tags we don't want to save as tokens */ - WPS_TOKEN_UNKNOWN, - - /* Markers */ - WPS_TOKEN_CHARACTER, - WPS_TOKEN_STRING, - - /* Alignment */ - WPS_TOKEN_ALIGN_LEFT, - WPS_TOKEN_ALIGN_CENTER, - WPS_TOKEN_ALIGN_RIGHT, - - /* Sublines */ - WPS_TOKEN_SUBLINE_TIMEOUT, - - /* Battery */ - WPS_TOKEN_BATTERY_PERCENT, - WPS_TOKEN_BATTERY_VOLTS, - WPS_TOKEN_BATTERY_TIME, - WPS_TOKEN_BATTERY_CHARGER_CONNECTED, - WPS_TOKEN_BATTERY_CHARGING, - WPS_TOKEN_BATTERY_SLEEPTIME, - - /* Sound */ -#if (CONFIG_CODEC != MAS3507D) - WPS_TOKEN_SOUND_PITCH, -#endif -#if (CONFIG_CODEC == SWCODEC) - WPS_TOKEN_REPLAYGAIN, - WPS_TOKEN_CROSSFADE, -#endif - - /* Time */ - - WPS_TOKEN_RTC_PRESENT, - - /* The begin/end values allow us to know if a token is an RTC one. - New RTC tokens should be added between the markers. */ - - WPS_TOKENS_RTC_BEGIN, /* just the start marker, not an actual token */ - - WPS_TOKEN_RTC_DAY_OF_MONTH, - WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED, - WPS_TOKEN_RTC_12HOUR_CFG, - WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, - WPS_TOKEN_RTC_HOUR_24, - WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, - WPS_TOKEN_RTC_HOUR_12, - WPS_TOKEN_RTC_MONTH, - WPS_TOKEN_RTC_MINUTE, - WPS_TOKEN_RTC_SECOND, - WPS_TOKEN_RTC_YEAR_2_DIGITS, - WPS_TOKEN_RTC_YEAR_4_DIGITS, - WPS_TOKEN_RTC_AM_PM_UPPER, - WPS_TOKEN_RTC_AM_PM_LOWER, - WPS_TOKEN_RTC_WEEKDAY_NAME, - WPS_TOKEN_RTC_MONTH_NAME, - WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, - WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, - - WPS_TOKENS_RTC_END, /* just the end marker, not an actual token */ - - /* Conditional */ - WPS_TOKEN_CONDITIONAL, - WPS_TOKEN_CONDITIONAL_START, - WPS_TOKEN_CONDITIONAL_OPTION, - WPS_TOKEN_CONDITIONAL_END, - - /* Database */ -#ifdef HAVE_TAGCACHE - WPS_TOKEN_DATABASE_PLAYCOUNT, - WPS_TOKEN_DATABASE_RATING, - WPS_TOKEN_DATABASE_AUTOSCORE, -#endif - - /* File */ - WPS_TOKEN_FILE_BITRATE, - WPS_TOKEN_FILE_CODEC, - WPS_TOKEN_FILE_FREQUENCY, - WPS_TOKEN_FILE_FREQUENCY_KHZ, - WPS_TOKEN_FILE_NAME, - WPS_TOKEN_FILE_NAME_WITH_EXTENSION, - WPS_TOKEN_FILE_PATH, - WPS_TOKEN_FILE_SIZE, - WPS_TOKEN_FILE_VBR, - WPS_TOKEN_FILE_DIRECTORY, - -#ifdef HAVE_LCD_BITMAP - /* Image */ - WPS_TOKEN_IMAGE_BACKDROP, - WPS_TOKEN_IMAGE_PROGRESS_BAR, - WPS_TOKEN_IMAGE_PRELOAD, - WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, - WPS_TOKEN_IMAGE_DISPLAY, -#endif - -#ifdef HAVE_ALBUMART - /* Albumart */ - WPS_TOKEN_ALBUMART_DISPLAY, - WPS_TOKEN_ALBUMART_FOUND, -#endif - - /* Metadata */ - WPS_TOKEN_METADATA_ARTIST, - WPS_TOKEN_METADATA_COMPOSER, - WPS_TOKEN_METADATA_ALBUM_ARTIST, - WPS_TOKEN_METADATA_GROUPING, - WPS_TOKEN_METADATA_ALBUM, - WPS_TOKEN_METADATA_GENRE, - WPS_TOKEN_METADATA_DISC_NUMBER, - WPS_TOKEN_METADATA_TRACK_NUMBER, - WPS_TOKEN_METADATA_TRACK_TITLE, - WPS_TOKEN_METADATA_VERSION, - WPS_TOKEN_METADATA_YEAR, - WPS_TOKEN_METADATA_COMMENT, - - /* Mode */ - WPS_TOKEN_REPEAT_MODE, - WPS_TOKEN_PLAYBACK_STATUS, - - WPS_TOKEN_MAIN_HOLD, - -#ifdef HAS_REMOTE_BUTTON_HOLD - WPS_TOKEN_REMOTE_HOLD, -#endif - - /* Progressbar */ - WPS_TOKEN_PROGRESSBAR, -#ifdef HAVE_LCD_CHARCELLS - WPS_TOKEN_PLAYER_PROGRESSBAR, -#endif - -#ifdef HAVE_LCD_BITMAP - /* Peakmeter */ - WPS_TOKEN_PEAKMETER, -#endif - - /* Volume level */ - WPS_TOKEN_VOLUME, - - /* Current track */ - WPS_TOKEN_TRACK_ELAPSED_PERCENT, - WPS_TOKEN_TRACK_TIME_ELAPSED, - WPS_TOKEN_TRACK_TIME_REMAINING, - WPS_TOKEN_TRACK_LENGTH, - - /* Playlist */ - WPS_TOKEN_PLAYLIST_ENTRIES, - WPS_TOKEN_PLAYLIST_NAME, - WPS_TOKEN_PLAYLIST_POSITION, - WPS_TOKEN_PLAYLIST_SHUFFLE, - -#if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD) - /* Virtual LED */ - WPS_TOKEN_VLED_HDD, -#endif - - /* Viewport display */ - WPS_VIEWPORT_ENABLE, - - /* buttons */ - WPS_TOKEN_BUTTON_VOLUME, - WPS_TOKEN_LASTTOUCH, - - /* Setting option */ - WPS_TOKEN_SETTING, -}; - -struct wps_token { - unsigned char type; /* enough to store the token type */ - - /* Whether the tag (e.g. track name or the album) refers the - current or the next song (false=current, true=next) */ - bool next; - - union { - char c; - unsigned short i; - } value; -}; - -/* Description of a subline on the WPS */ -struct wps_subline { - - /* Index of the first token for this subline in the token array. - Tokens of this subline end where tokens for the next subline - begin. */ - unsigned short first_token_idx; - - /* Bit or'ed WPS_REFRESH_xxx */ - unsigned char line_type; - - /* How long the subline should be displayed, in 10ths of sec */ - unsigned char time_mult; -}; - -/* Description of a line on the WPS. A line is a set of sublines. - A subline is displayed for a certain amount of time. After that, - the next subline of the line is displayed. And so on. */ -struct wps_line { - - /* Number of sublines in this line */ - signed char num_sublines; - - /* Number (0-based) of the subline within this line currently being displayed */ - signed char curr_subline; - - /* Index of the first subline of this line in the subline array. - Sublines for this line end where sublines for the next line begin. */ - unsigned short first_subline_idx; - - /* When the next subline of this line should be displayed - (absolute time value in ticks) */ - long subline_expire_time; -}; - -#define VP_DRAW_HIDEABLE 0x1 -#define VP_DRAW_HIDDEN 0x2 -#define VP_DRAW_WASHIDDEN 0x4 -struct wps_viewport { - struct viewport vp; /* The LCD viewport struct */ - struct progressbar *pb; - /* Indexes of the first and last lines belonging to this viewport in the - lines[] array */ - int first_line, last_line; - char hidden_flags; - char label; -}; - -#ifdef HAVE_TOUCHSCREEN -struct touchregion { - struct wps_viewport* wvp;/* The viewport this region is in */ - short int x; /* x-pos */ - short int y; /* y-pos */ - short int width; /* width */ - short int height; /* height */ - bool repeat; /* requires the area be held for the action */ - int action; /* action this button will return */ -}; -#define MAX_TOUCHREGIONS 15 -#endif -/* wps_data - this struct holds 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]; - unsigned char* img_buf_ptr; - int img_buf_free; - bool wps_sb_tag; - bool show_sb_on_wps; - - struct progressbar progressbar[MAX_PROGRESSBARS]; - short progressbar_count; - - bool peak_meter_enabled; - -#ifdef HAVE_ALBUMART - /* Album art support */ - unsigned char wps_uses_albumart; /* WPS_ALBUMART_NONE, _CHECK, _LOAD */ - short albumart_x; - short albumart_y; - unsigned char albumart_xalign; /* WPS_ALBUMART_ALIGN_LEFT, _CENTER, _RIGHT */ - unsigned char albumart_yalign; /* WPS_ALBUMART_ALIGN_TOP, _CENTER, _BOTTOM */ - short albumart_max_width; - short albumart_max_height; - - int albumart_cond_index; -#endif - -#else /*HAVE_LCD_CHARCELLS */ - unsigned short wps_progress_pat[8]; - bool full_line_progressbar; -#endif - -#ifdef HAVE_TOUCHSCREEN - struct touchregion touchregion[MAX_TOUCHREGIONS]; - short touchregion_count; -#endif - -#ifdef HAVE_REMOTE_LCD - bool remote_wps; -#endif - - /* Number of lines in the WPS. During WPS parsing, this is - the index of the line being parsed. */ - int num_lines; - - /* Number of viewports in the WPS */ - int num_viewports; - struct wps_viewport viewports[WPS_MAX_VIEWPORTS]; - - struct wps_line lines[WPS_MAX_LINES]; - - /* Total number of sublines in the WPS. During WPS parsing, this is - the index of the subline where the parsed tokens are added to. */ - int num_sublines; - struct wps_subline sublines[WPS_MAX_SUBLINES]; - - /* Total number of tokens in the WPS. During WPS parsing, this is - the index of the token being parsed. */ - int num_tokens; - struct wps_token tokens[WPS_MAX_TOKENS]; - - char string_buffer[STRING_BUFFER_SIZE]; - char *strings[WPS_MAX_STRINGS]; - int num_strings; - - bool wps_loaded; - - /* tick the volume button was last pressed */ - unsigned int button_time_volume; -}; - -/* 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, - struct screen *display, - const char *buf, - bool isfile); - -/* Redraw statusbars if necessary */ -void gwps_draw_statusbars(void); - -/* Returns the index of the subline in the subline array - line - 0-based line number - subline - 0-based subline number within the line - */ -int wps_subline_index(struct wps_data *wps_data, int line, int subline); - -/* Returns the index of the first subline's token in the token array - line - 0-based line number - subline - 0-based subline number within the line - */ -int wps_first_token_index(struct wps_data *data, int line, int subline); - -/* Returns the index of the last subline's token in the token array. - line - 0-based line number - subline - 0-based subline number within the line - */ -int wps_last_token_index(struct wps_data *data, int line, int subline); - -/* 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; - bool do_full_update; -}; - - -/* 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); Currently unused */ - -/* 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); Currently unused */ -/* wps_state end*/ - -/* gui_wps - defines a wps with its 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; -}; - -/* gui_wps end */ - -long gui_wps_show(void); - -/* currently only on wps_state is needed */ -extern struct wps_state wps_state; -extern struct gui_wps gui_wps[NB_SCREENS]; - -void gui_sync_wps_init(void); - -#ifdef HAVE_TOUCHSCREEN -int wps_get_touchaction(struct wps_data *data); -#endif - -#ifdef HAVE_ALBUMART -/* gives back if WPS contains an albumart tag */ -bool gui_sync_wps_uses_albumart(void); -#endif - -#endif diff --git a/apps/gui/music_screen.c b/apps/gui/music_screen.c new file mode 100644 index 0000000000..39d5e0e85b --- /dev/null +++ b/apps/gui/music_screen.c @@ -0,0 +1,1213 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002 Jerome Kuptz + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include +#include +#include +#include "config.h" + +#include "system.h" +#include "file.h" +#include "lcd.h" +#include "font.h" +#include "backlight.h" +#include "action.h" +#include "kernel.h" +#include "filetypes.h" +#include "debug.h" +#include "sprintf.h" +#include "settings.h" +#include "wps_engine/wps_engine.h" +#include "mp3_playback.h" +#include "audio.h" +#include "usb.h" +#include "status.h" +#include "storage.h" +#include "screens.h" +#include "playlist.h" +#ifdef HAVE_LCD_BITMAP +#include "icons.h" +#include "peakmeter.h" +#endif +#include "lang.h" +#include "bookmark.h" +#include "misc.h" +#include "sound.h" +#include "onplay.h" +#include "abrepeat.h" +#include "playback.h" +#include "splash.h" +#include "cuesheet.h" +#include "ata_idle_notify.h" +#include "root_menu.h" +#include "backdrop.h" +#include "quickscreen.h" +#include "pitchscreen.h" +#include "appevents.h" +#include "viewport.h" +#include "pcmbuf.h" +#include "option_select.h" +#include "dsp.h" +#include "playlist_viewer.h" +#include "music_screen.h" + +#define RESTORE_WPS_INSTANTLY 0l +#define RESTORE_WPS_NEXT_SECOND ((long)(HZ+current_tick)) +/* in milliseconds */ +#define DEFAULT_SKIP_TRESH 3000ul + +static int wpsbars; +/* currently only one wps_state is needed */ +struct wps_state wps_state; +struct gui_wps gui_wps[NB_SCREENS]; +static struct wps_data wps_datas[NB_SCREENS]; + +/* initial setup of wps_data */ +static void wps_state_init(void); +static void track_changed_callback(void *param); +static void nextid3available_callback(void* param); + + + +#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 + +bool wps_fading_out = false; +void fade(bool fade_in, bool updatewps) +{ + int fp_global_vol = global_settings.volume << 8; + int fp_min_vol = sound_min(SOUND_VOLUME) << 8; + int fp_step = (fp_global_vol - fp_min_vol) / 30; + int i; + wps_fading_out = !fade_in; + if (fade_in) { + /* fade in */ + int fp_volume = fp_min_vol; + + /* zero out the sound */ + sound_set_volume(fp_min_vol >> 8); + + sleep(HZ/10); /* let audio thread run */ + audio_resume(); + + while (fp_volume < fp_global_vol - fp_step) { + fp_volume += fp_step; + sound_set_volume(fp_volume >> 8); + if (updatewps) + { + FOR_NB_SCREENS(i) + gui_wps_redraw(&gui_wps[i], 0, WPS_REFRESH_NON_STATIC); + } + sleep(1); + } + sound_set_volume(global_settings.volume); + } + else { + /* fade out */ + int fp_volume = fp_global_vol; + + while (fp_volume > fp_min_vol + fp_step) { + fp_volume -= fp_step; + sound_set_volume(fp_volume >> 8); + if (updatewps) + { + FOR_NB_SCREENS(i) + gui_wps_redraw(&gui_wps[i], 0, WPS_REFRESH_NON_STATIC); + } + sleep(1); + } + audio_pause(); + wps_fading_out = false; +#if CONFIG_CODEC != SWCODEC +#ifndef SIMULATOR + /* let audio thread run and wait for the mas to run out of data */ + while (!mp3_pause_done()) +#endif + sleep(HZ/10); +#endif + + /* reset volume to what it was before the fade */ + sound_set_volume(global_settings.volume); + } +} +bool is_wps_fading(void) +{ + return wps_fading_out; +} + +bool update_onvol_change(struct gui_wps * gwps) +{ + gui_wps_redraw(gwps, 0, WPS_REFRESH_NON_STATIC); + +#ifdef HAVE_LCD_CHARCELLS + splashf(0, "Vol: %3d dB", + sound_val2phys(SOUND_VOLUME, global_settings.volume)); + return true; +#endif + return false; +} + +bool ffwd_rew(int button) +{ + 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 */ + bool exit = false; + bool usb = false; + int i = 0; + const long ff_rw_accel = (global_settings.ff_rewind_accel + 3); + + if (button == ACTION_NONE) + { + status_set_ffmode(0); + return usb; + } + while (!exit) + { + switch ( button ) + { + case ACTION_WPS_SEEKFWD: + direction = 1; + case ACTION_WPS_SEEKBACK: + 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; + + /* smooth seeking by multiplying step by: 1 + (2 ^ -accel) */ + step += step >> ff_rw_accel; + } + else + { + if ( (audio_status() & AUDIO_STATUS_PLAY) && + wps_state.id3 && wps_state.id3->length ) + { + if (!wps_state.paused) +#if (CONFIG_CODEC == SWCODEC) + audio_pre_ff_rewind(); +#else + audio_pause(); +#endif +#if CONFIG_KEYPAD == PLAYER_PAD + FOR_NB_SCREENS(i) + 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 = 1000 * global_settings.ff_rewind_min_step; + } + 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; + } + + FOR_NB_SCREENS(i) + gui_wps_redraw(&gui_wps[i], + (wps_state.wps_time_countup == false)? + ff_rewind_count:-ff_rewind_count, + WPS_REFRESH_PLAYER_PROGRESS | + WPS_REFRESH_DYNAMIC); + + break; + + case ACTION_WPS_STOPSEEK: + wps_state.id3->elapsed = wps_state.id3->elapsed+ff_rewind_count; + audio_ff_rewind(wps_state.id3->elapsed); + ff_rewind_count = 0; + wps_state.ff_rewind = false; + status_set_ffmode(0); +#if (CONFIG_CODEC != SWCODEC) + if (!wps_state.paused) + audio_resume(); +#endif +#ifdef HAVE_LCD_CHARCELLS + FOR_NB_SCREENS(i) + gui_wps_redraw(&gui_wps[i],0, WPS_REFRESH_ALL); +#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 = get_action(CONTEXT_WPS|ALLOW_SOFTLOCK,TIMEOUT_BLOCK); +#ifdef HAVE_TOUCHSCREEN + if (button == ACTION_TOUCHSCREEN) + button = wps_get_touchaction(gui_wps[SCREEN_MAIN].data); +#endif + } + } + return usb; +} + + +void display_keylock_text(bool locked) +{ + int i; + FOR_NB_SCREENS(i) + gui_wps[i].display->stop_scroll(); + + splash(HZ, locked ? ID2P(LANG_KEYLOCK_ON) : ID2P(LANG_KEYLOCK_OFF)); +} + + + + +#if defined(HAVE_BACKLIGHT) || defined(HAVE_REMOTE_LCD) +static void gwps_caption_backlight(struct wps_state *state) +{ + if (state && state->id3) + { +#ifdef HAVE_BACKLIGHT + if (global_settings.caption_backlight) + { + /* 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 = 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)) && + (state->paused == false)) + backlight_on(); + } +#endif +#ifdef HAVE_REMOTE_LCD + if (global_settings.remote_caption_backlight) + { + /* turn on remote backlight n seconds before track ends, and turn it + off n seconds into the new track. n == remote_backlight_timeout, + or 5s */ + int n = global_settings.remote_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)) && + (state->paused == false)) + remote_backlight_on(); + } +#endif + } +} +#endif + + +static void change_dir(int direction) +{ + if (global_settings.prevent_skip) + return; + + if (direction < 0) + audio_prev_dir(); + else if (direction > 0) + audio_next_dir(); + /* prevent the next dir to immediatly start being ffw'd */ + action_wait_for_release(); +} + +static void prev_track(unsigned long skip_thresh) +{ + if (wps_state.id3->elapsed < skip_thresh) + { + audio_prev(); + return; + } + else + { + if (wps_state.id3->cuesheet) + { + curr_cuesheet_skip(wps_state.id3->cuesheet, -1, wps_state.id3->elapsed); + return; + } + + if (!wps_state.paused) +#if (CONFIG_CODEC == SWCODEC) + audio_pre_ff_rewind(); +#else + audio_pause(); +#endif + + audio_ff_rewind(0); + +#if (CONFIG_CODEC != SWCODEC) + if (!wps_state.paused) + audio_resume(); +#endif + } +} + +static void next_track(void) +{ + /* take care of if we're playing a cuesheet */ + if (wps_state.id3->cuesheet) + { + if (curr_cuesheet_skip(wps_state.id3->cuesheet, 1, wps_state.id3->elapsed)) + { + /* if the result was false, then we really want + to skip to the next track */ + return; + } + } + + audio_next(); +} + +static void play_hop(int direction) +{ + unsigned long step = ((unsigned long)global_settings.skip_length)*1000; + unsigned long elapsed = wps_state.id3->elapsed; + unsigned long remaining = wps_state.id3->length - elapsed; + + if (!global_settings.prevent_skip && + (!step || + (direction > 0 && step >= remaining) || + (direction < 0 && elapsed < DEFAULT_SKIP_TRESH))) + { /* Do normal track skipping */ + if (direction > 0) + next_track(); + else if (direction < 0) + prev_track(DEFAULT_SKIP_TRESH); + return; + } + + if (direction == 1 && step >= remaining) + { +#if CONFIG_CODEC == SWCODEC + if(global_settings.beep) + pcmbuf_beep(1000, 150, 1500*global_settings.beep); +#endif + return; + } + else if ((direction == -1 && elapsed < step)) + { + elapsed = 0; + } + else + { + elapsed += step * direction; + } + if((audio_status() & AUDIO_STATUS_PLAY) && !wps_state.paused) + { +#if (CONFIG_CODEC == SWCODEC) + audio_pre_ff_rewind(); +#else + audio_pause(); +#endif + } + audio_ff_rewind(wps_state.id3->elapsed = elapsed); +#if (CONFIG_CODEC != SWCODEC) + if (!wps_state.paused) + audio_resume(); +#endif +} + +static void gwps_fix_statusbars(void) +{ +#ifdef HAVE_LCD_BITMAP + int i; + wpsbars = VP_SB_HIDE_ALL; + FOR_NB_SCREENS(i) + { + bool draw = false; + if (gui_wps[i].data->wps_sb_tag) + draw = gui_wps[i].data->show_sb_on_wps; + else if (statusbar_position(i) != STATUSBAR_OFF) + draw = true; + if (draw) + wpsbars |= (VP_SB_ONSCREEN(i) | VP_SB_IGNORE_SETTING(i)); + } +#else + wpsbars = VP_SB_ALLSCREENS; +#endif +} + +#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP) +/* + * If the user is unable to see the wps, because the display is deactivated, + * we suppress updates until the wps is activated again (the lcd driver will + * call this hook to issue an instant update) + * */ +static void wps_lcd_activation_hook(void) +{ + wps_state.do_full_update = true; + /* force timeout in wps main loop, so that the update is instantly */ + queue_post(&button_queue, BUTTON_NONE, 0); +} +#endif + +static void gwps_leave_wps(void) +{ + int i, oldbars = VP_SB_HIDE_ALL; + + FOR_NB_SCREENS(i) + gui_wps[i].display->stop_scroll(); + if (global_settings.statusbar) + oldbars = VP_SB_ALLSCREENS; + +#if LCD_DEPTH > 1 + show_main_backdrop(); +#endif +#if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1 + show_remote_main_backdrop(); +#endif + viewportmanager_set_statusbar(oldbars); +#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP) + /* Play safe and unregister the hook */ + lcd_activation_set_hook(NULL); +#endif +} + +void gwps_draw_statusbars(void) +{ + viewportmanager_set_statusbar(wpsbars); +} +#ifdef HAVE_TOUCHSCREEN +int wps_get_touchaction(struct wps_data *data) +{ + short x,y; + short vx, vy; + int type = action_get_touchscreen_press(&x, &y); + int i; + static int last_action = ACTION_NONE; + struct touchregion *r; + bool repeated = (type == BUTTON_REPEAT); + bool released = (type == BUTTON_REL); + for (i=0; itouchregion_count; i++) + { + r = &data->touchregion[i]; + /* make sure this region's viewport is visible */ + if (r->wvp->hidden_flags&VP_DRAW_HIDDEN) + continue; + /* reposition the touch inside the viewport */ + vx = x - r->wvp->vp.x; + vy = y - r->wvp->vp.y; + /* check if it's inside this viewport */ + if (vx >= 0 && vx < r->wvp->vp.x + r->wvp->vp.width && + vy >= 0 && vy < r->wvp->vp.y + r->wvp->vp.height) + { + /* now see if the point is inside this region */ + if (vx >= r->x && vx < r->x+r->width && + vy >= r->y && vy < r->y+r->height) + { + if ((repeated && r->repeat) || + (released && !r->repeat)) + { + last_action = r->action; + return r->action; + } + } + } + } + if ((last_action == ACTION_WPS_SEEKBACK || last_action == ACTION_WPS_SEEKFWD)) + return ACTION_WPS_STOPSEEK; + last_action = ACTION_TOUCHSCREEN; + return ACTION_TOUCHSCREEN; +} +#endif +/* The WPS can be left in two ways: + * a) call a function, which draws over the wps. In this case, the wps + * will be still active (i.e. the below function didn't return) + * b) return with a value evaluated by root_menu.c, in this case the wps + * is really left, and root_menu will handle the next screen + * + * In either way, call gwps_leave_wps(), in order to restore the correct + * "main screen" backdrops and statusbars + */ +long gui_wps_show(void) +{ + long button = 0; + bool restore = true; + long restoretimer = RESTORE_WPS_INSTANTLY; /* timer to delay screen redraw temporarily */ + bool exit = false; + bool bookmark = false; + bool update = false; + int i; + long last_left = 0, last_right = 0; + +#ifdef HAVE_LCD_CHARCELLS + status_set_audio(true); + status_set_param(false); +#endif + +#ifdef AB_REPEAT_ENABLE + ab_repeat_init(); + ab_reset_markers(); +#endif + wps_state_init(); + + 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_RAM) && !defined(HAVE_SW_POWEROFF) + call_storage_idle_notifys(true); +#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_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 = get_action(CONTEXT_WPS|ALLOW_SOFTLOCK,TIMEOUT_NOBLOCK); + if (button != ACTION_NONE) { + break; + } + peak_meter_peek(); + sleep(0); /* Sleep until end of current tick. */ + + if (TIME_AFTER(current_tick, next_refresh)) { + FOR_NB_SCREENS(i) + { + if(gui_wps[i].data->peak_meter_enabled) + gui_wps_redraw(&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 +#endif + { + button = get_action(CONTEXT_WPS|ALLOW_SOFTLOCK, + restore ? HZ/100 : HZ/5); + } + + /* Exit if audio has stopped playing. This happens e.g. at end of + playlist or if using the sleep timer. */ + if (!(audio_status() & AUDIO_STATUS_PLAY)) + exit = true; +#ifdef HAVE_TOUCHSCREEN + if (button == ACTION_TOUCHSCREEN) + button = wps_get_touchaction(gui_wps[SCREEN_MAIN].data); +#endif +/* The iPods/X5/M5 use a single button for the A-B mode markers, + defined as ACTION_WPSAB_SINGLE in their config files. */ +#ifdef ACTION_WPSAB_SINGLE + if (!global_settings.party_mode && ab_repeat_mode_enabled()) + { + static int wps_ab_state = 0; + if (button == ACTION_WPSAB_SINGLE) + { + switch (wps_ab_state) + { + case 0: /* set the A spot */ + button = ACTION_WPS_ABSETA_PREVDIR; + break; + case 1: /* set the B spot */ + button = ACTION_WPS_ABSETB_NEXTDIR; + break; + case 2: + button = ACTION_WPS_ABRESET; + break; + } + wps_ab_state = (wps_ab_state+1) % 3; + } + } +#endif + switch(button) + { + case ACTION_WPS_CONTEXT: + { + gwps_leave_wps(); + /* if music is stopped in the context menu we want to exit the wps */ + if (onplay(wps_state.id3->path, + FILE_ATTR_AUDIO, CONTEXT_WPS) == ONPLAY_MAINMENU + || !audio_status()) + return GO_TO_ROOT; + restore = true; + } + break; + + case ACTION_WPS_BROWSE: +#ifdef HAVE_LCD_CHARCELLS + status_set_record(false); + status_set_audio(false); +#endif + gwps_leave_wps(); + return GO_TO_PREVIOUS_BROWSER; + break; + + /* play/pause */ + case ACTION_WPS_PLAY: + if (global_settings.party_mode) + break; + if ( wps_state.paused ) + { + wps_state.paused = false; + if ( global_settings.fade_on_stop ) + fade(true, true); + else + audio_resume(); + } + else + { + wps_state.paused = true; + if ( global_settings.fade_on_stop ) + fade(false, true); + else + audio_pause(); + settings_save(); +#if !defined(HAVE_RTC_RAM) && !defined(HAVE_SW_POWEROFF) + call_storage_idle_notifys(true); /* make sure resume info is saved */ +#endif + } + break; + + case ACTION_WPS_VOLUP: + { + FOR_NB_SCREENS(i) + gui_wps[i].data->button_time_volume = current_tick; + global_settings.volume++; + bool res = false; + setvol(); + FOR_NB_SCREENS(i) + { + if(update_onvol_change(&gui_wps[i])) + res = true; + } + if (res) { + restore = true; + restoretimer = RESTORE_WPS_NEXT_SECOND; + } + } + break; + case ACTION_WPS_VOLDOWN: + { + FOR_NB_SCREENS(i) + gui_wps[i].data->button_time_volume = current_tick; + global_settings.volume--; + setvol(); + bool res = false; + FOR_NB_SCREENS(i) + { + if(update_onvol_change(&gui_wps[i])) + res = true; + } + if (res) { + restore = true; + restoretimer = RESTORE_WPS_NEXT_SECOND; + } + } + break; + /* fast forward + OR next dir if this is straight after ACTION_WPS_SKIPNEXT */ + case ACTION_WPS_SEEKFWD: + if (global_settings.party_mode) + break; + if (current_tick -last_right < HZ) + { + if (wps_state.id3->cuesheet) + { + audio_next(); + } + else + { + change_dir(1); + } + } + else + ffwd_rew(ACTION_WPS_SEEKFWD); + last_right = last_left = 0; + break; + /* fast rewind + OR prev dir if this is straight after ACTION_WPS_SKIPPREV,*/ + case ACTION_WPS_SEEKBACK: + if (global_settings.party_mode) + break; + if (current_tick -last_left < HZ) + { + if (wps_state.id3->cuesheet) + { + if (!wps_state.paused) +#if (CONFIG_CODEC == SWCODEC) + audio_pre_ff_rewind(); +#else + audio_pause(); +#endif + audio_ff_rewind(0); + } + else + { + change_dir(-1); + } + } + else + ffwd_rew(ACTION_WPS_SEEKBACK); + last_left = last_right = 0; + break; + + /* prev / restart */ + case ACTION_WPS_SKIPPREV: + if (global_settings.party_mode) + break; + last_left = current_tick; +#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() ) + { + if ( ab_after_A_marker(wps_state.id3->elapsed) ) + { + ab_jump_to_A_marker(); + break; +#if (AB_REPEAT_ENABLE == 2) + } else { + ab_reset_markers(); +#endif + } + } + else + /* ...otherwise, do it normally */ +#endif + play_hop(-1); + break; + + /* next + OR if skip length set, hop by predetermined amount. */ + case ACTION_WPS_SKIPNEXT: + if (global_settings.party_mode) + break; + last_right = current_tick; +#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() ) + { + if ( ab_before_A_marker(wps_state.id3->elapsed) ) + { + ab_jump_to_A_marker(); + break; +#if (AB_REPEAT_ENABLE == 2) + } else { + ab_reset_markers(); +#endif + } + } + else + /* ...otherwise, do it normally */ +#endif + play_hop(1); + break; + /* next / prev directories */ + /* and set A-B markers if in a-b mode */ + case ACTION_WPS_ABSETB_NEXTDIR: + if (global_settings.party_mode) + break; +#if defined(AB_REPEAT_ENABLE) + if (ab_repeat_mode_enabled()) + { + ab_set_B_marker(wps_state.id3->elapsed); + ab_jump_to_A_marker(); + } + else +#endif + { + change_dir(1); + } + break; + case ACTION_WPS_ABSETA_PREVDIR: + if (global_settings.party_mode) + break; +#if defined(AB_REPEAT_ENABLE) + if (ab_repeat_mode_enabled()) + ab_set_A_marker(wps_state.id3->elapsed); + else +#endif + { + change_dir(-1); + } + break; + /* menu key functions */ + case ACTION_WPS_MENU: + gwps_leave_wps(); + return GO_TO_ROOT; + break; + + +#ifdef HAVE_QUICKSCREEN + case ACTION_WPS_QUICKSCREEN: + { + gwps_leave_wps(); + if (quick_screen_quick(button)) + return SYS_USB_CONNECTED; + restore = true; + } + break; +#endif /* HAVE_QUICKSCREEN */ + + /* screen settings */ +#ifdef BUTTON_F3 + case ACTION_F3: + { + gwps_leave_wps(); + if (quick_screen_f3(BUTTON_F3)) + return SYS_USB_CONNECTED; + restore = true; + } + break; +#endif /* BUTTON_F3 */ + + /* pitch screen */ +#ifdef HAVE_PITCHSCREEN + case ACTION_WPS_PITCHSCREEN: + { + gwps_leave_wps(); + if (1 == gui_syncpitchscreen_run()) + return SYS_USB_CONNECTED; + restore = true; + } + break; +#endif /* HAVE_PITCHSCREEN */ + +#ifdef AB_REPEAT_ENABLE + /* reset A&B markers */ + case ACTION_WPS_ABRESET: + if (ab_repeat_mode_enabled()) + { + ab_reset_markers(); + update = true; + } + break; +#endif /* AB_REPEAT_ENABLE */ + + /* stop and exit wps */ + case ACTION_WPS_STOP: + if (global_settings.party_mode) + break; + bookmark = true; + exit = true; + break; + + case ACTION_WPS_ID3SCREEN: + { + gwps_leave_wps(); + browse_id3(); + restore = true; + } + break; +#ifdef HAVE_TOUCHSCREEN + case ACTION_TOUCH_SHUFFLE: /* toggle shuffle mode */ + { + global_settings.playlist_shuffle = + !global_settings.playlist_shuffle; +#if CONFIG_CODEC == SWCODEC + dsp_set_replaygain(); +#endif + if (global_settings.playlist_shuffle) + playlist_randomise(NULL, current_tick, true); + else + playlist_sort(NULL, true); + } + break; + case ACTION_TOUCH_REPMODE: /* cycle the repeat mode setting */ + { + const struct settings_list *rep_setting = + find_setting(&global_settings.repeat_mode, NULL); + option_select_next_val(rep_setting, false, true); + audio_flush_and_reload_tracks(); + } + break; +#endif /* HAVE_TOUCHSCREEN */ + /* this case is used by the softlock feature + * it requests a full update here */ + case ACTION_REDRAW: + wps_state.do_full_update = true; + break; + case ACTION_NONE: /* Timeout, do a partial update */ + update = true; + ffwd_rew(button); /* hopefully fix the ffw/rwd bug */ + break; +#ifdef HAVE_RECORDING + case ACTION_WPS_REC: + exit = true; + break; +#endif + case SYS_POWEROFF: + default_event_handler(SYS_POWEROFF); + break; + case ACTION_WPS_VIEW_PLAYLIST: + gwps_leave_wps(); + if (playlist_viewer()) /* true if USB connected */ + return SYS_USB_CONNECTED; + restore = true; + break; + default: + if(default_event_handler(button) == SYS_USB_CONNECTED) + return GO_TO_ROOT; + update = true; + break; + } + + if (wps_state.do_full_update || update) + { +#if defined(HAVE_BACKLIGHT) || defined(HAVE_REMOTE_LCD) + gwps_caption_backlight(&wps_state); +#endif + FOR_NB_SCREENS(i) + { +#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP) + if (lcd_active() +#ifdef HAVE_REMOTE_LCD + /* currently, all remotes are readable without backlight + * so still update those */ + || (i == SCREEN_REMOTE) +#endif + ) +#endif + { + gui_wps_update(&gui_wps[i]); + } + } + wps_state.do_full_update = false; + update = false; + } + + if (restore && wps_state.id3 && + ((restoretimer == RESTORE_WPS_INSTANTLY) || + TIME_AFTER(current_tick, restoretimer))) + { + restore = false; + restoretimer = RESTORE_WPS_INSTANTLY; + gwps_fix_statusbars(); +#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP) + lcd_activation_set_hook(wps_lcd_activation_hook); +#endif + FOR_NB_SCREENS(i) + { + screens[i].stop_scroll(); + gui_wps_display(&gui_wps[i]); + } + } + + if (exit) { +#ifdef HAVE_LCD_CHARCELLS + status_set_record(false); + status_set_audio(false); +#endif + if (global_settings.fade_on_stop) + fade(false, true); + + if (bookmark) + bookmark_autobookmark(); + audio_stop(); +#ifdef AB_REPEAT_ENABLE + ab_reset_markers(); +#endif + gwps_leave_wps(); +#ifdef HAVE_RECORDING + if (button == ACTION_WPS_REC) + return GO_TO_RECSCREEN; +#endif + if (global_settings.browse_current) + return GO_TO_PREVIOUS_BROWSER; + return GO_TO_PREVIOUS; + } + + if (button && !IS_SYSEVENT(button) ) + storage_spin(); + } + return GO_TO_ROOT; /* unreachable - just to reduce compiler warnings */ +} + +/* this is called from the playback thread so NO DRAWING! */ +static void track_changed_callback(void *param) +{ + wps_state.id3 = (struct mp3entry*)param; + wps_state.nid3 = audio_next_track(); + if (wps_state.id3->cuesheet) + { + cue_find_current_track(wps_state.id3->cuesheet, wps_state.id3->elapsed); + cue_spoof_id3(wps_state.id3->cuesheet, wps_state.id3); + } + wps_state.do_full_update = true; +} +static void nextid3available_callback(void* param) +{ + (void)param; + wps_state.nid3 = audio_next_track(); + wps_state.do_full_update = true; +} + + +static void wps_state_init(void) +{ + wps_state.ff_rewind = false; + wps_state.paused = false; + if(audio_status() & AUDIO_STATUS_PLAY) + { + wps_state.id3 = audio_current_track(); + wps_state.nid3 = audio_next_track(); + } + else + { + wps_state.id3 = NULL; + wps_state.nid3 = NULL; + } + /* We'll be updating due to restore initialized with true */ + wps_state.do_full_update = false; + /* add the WPS track event callbacks */ + add_event(PLAYBACK_EVENT_TRACK_CHANGE, false, track_changed_callback); + add_event(PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE, false, nextid3available_callback); +} + + +#ifdef HAVE_LCD_BITMAP +static void statusbar_toggle_handler(void *data) +{ + (void)data; + int i; + gwps_fix_statusbars(); + + FOR_NB_SCREENS(i) + { + struct viewport *vp = &gui_wps[i].data->viewports[0].vp; + bool draw = wpsbars & (VP_SB_ONSCREEN(i) | VP_SB_IGNORE_SETTING(i)); + if (!draw) + { + vp->y = 0; + vp->height = screens[i].lcdheight; + } + else + { + bool bar_at_top = statusbar_position(i) != STATUSBAR_BOTTOM; + vp->y = bar_at_top?STATUSBAR_HEIGHT:0; + vp->height = screens[i].lcdheight - STATUSBAR_HEIGHT; + } + } +} +#endif + +void gui_sync_wps_init(void) +{ + int i; + FOR_NB_SCREENS(i) + { + wps_data_init(&wps_datas[i]); +#ifdef HAVE_ALBUMART + wps_datas[i].wps_uses_albumart = 0; +#endif +#ifdef HAVE_REMOTE_LCD + wps_datas[i].remote_wps = (i != 0); +#endif + gui_wps[i].data = &wps_datas[i]; + gui_wps[i].display = &screens[i]; + /* Currently no seperate wps_state needed/possible + so use the only available ( "global" ) one */ + gui_wps[i].state = &wps_state; + } +#ifdef HAVE_LCD_BITMAP + add_event(GUI_EVENT_STATUSBAR_TOGGLE, false, statusbar_toggle_handler); +#endif +#if LCD_DEPTH > 1 + unload_wps_backdrop(); +#endif +#if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1 + unload_remote_wps_backdrop(); +#endif +} + +#ifdef HAVE_ALBUMART +/* Returns true if at least one of the gui_wps screens has an album art + tag in its wps structure */ +bool gui_sync_wps_uses_albumart(void) +{ + int i; + FOR_NB_SCREENS(i) { + struct gui_wps *gwps = &gui_wps[i]; + if (gwps->data && (gwps->data->wps_uses_albumart != WPS_ALBUMART_NONE)) + return true; + } + return false; +} +#endif diff --git a/apps/gui/music_screen.h b/apps/gui/music_screen.h new file mode 100644 index 0000000000..90614cb65b --- /dev/null +++ b/apps/gui/music_screen.h @@ -0,0 +1,37 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: gwps-common.h 20492 2009-03-23 17:19:48Z alle $ + * + * Copyright (C) 2002 Björn Stenberg + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef _MUSICSCREEN_H_ +#define _MUSICSCREEN_H_ +#include + +long gui_wps_show(void); + + +void gui_sync_wps_init(void); + +/* fades the volume, e.g. on pause or stop */ +void fade(bool fade_in, bool updatewps); + +bool ffwd_rew(int button); +void display_keylock_text(bool locked); + +bool is_wps_fading(void); +#endif diff --git a/apps/gui/wps_debug.c b/apps/gui/wps_debug.c deleted file mode 100644 index 727d9078c6..0000000000 --- a/apps/gui/wps_debug.c +++ /dev/null @@ -1,641 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2007 Nicolas Pennequin, Dan Everton, Matthias Mohr - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ****************************************************************************/ - -#if defined(DEBUG) || defined(SIMULATOR) - -#include -#include -#include "gwps.h" -#ifdef __PCTOOL__ -#ifdef WPSEDITOR -#include "proxy.h" -#else -#define DEBUGF printf -#endif -#else -#include "debug.h" -#endif - -#if defined(SIMULATOR) || defined(__PCTOOL__) -extern bool debug_wps; -extern int wps_verbose_level; -#endif - -static char *next_str(bool next) { - return next ? "next " : ""; -} - -static char *get_token_desc(struct wps_token *token, struct wps_data *data, - char *buf, int bufsize) -{ - bool next = token->next; - - switch(token->type) - { - case WPS_NO_TOKEN: - snprintf(buf, bufsize, "No token"); - break; - - case WPS_TOKEN_UNKNOWN: - snprintf(buf, bufsize, "Unknown token"); - break; - - case WPS_TOKEN_CHARACTER: - snprintf(buf, bufsize, "Character '%c'", - token->value.c); - break; - - case WPS_TOKEN_STRING: - snprintf(buf, bufsize, "String '%s'", - data->strings[token->value.i]); - break; - -#ifdef HAVE_LCD_BITMAP - case WPS_TOKEN_ALIGN_LEFT: - snprintf(buf, bufsize, "align left"); - break; - - case WPS_TOKEN_ALIGN_CENTER: - snprintf(buf, bufsize, "align center"); - break; - - case WPS_TOKEN_ALIGN_RIGHT: - snprintf(buf, bufsize, "align right"); - break; -#endif - - case WPS_TOKEN_SUBLINE_TIMEOUT: - snprintf(buf, bufsize, "subline timeout value: %d", - token->value.i); - break; - - case WPS_TOKEN_CONDITIONAL: - snprintf(buf, bufsize, "conditional, %d options", - token->value.i); - break; - - case WPS_TOKEN_CONDITIONAL_START: - snprintf(buf, bufsize, "conditional start, next cond: %d", - token->value.i); - break; - - case WPS_TOKEN_CONDITIONAL_OPTION: - snprintf(buf, bufsize, "conditional option, next cond: %d", - token->value.i); - break; - - case WPS_TOKEN_CONDITIONAL_END: - snprintf(buf, bufsize, "conditional end"); - break; - -#ifdef HAVE_LCD_BITMAP - case WPS_TOKEN_IMAGE_PRELOAD: - snprintf(buf, bufsize, "preload image"); - break; - - case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY: - snprintf(buf, bufsize, "display preloaded image %d", - token->value.i); - break; - - case WPS_TOKEN_IMAGE_DISPLAY: - snprintf(buf, bufsize, "display image"); - break; -#endif - -#ifdef HAS_BUTTON_HOLD - case WPS_TOKEN_MAIN_HOLD: - snprintf(buf, bufsize, "mode hold"); - break; -#endif - -#ifdef HAS_REMOTE_BUTTON_HOLD - case WPS_TOKEN_REMOTE_HOLD: - snprintf(buf, bufsize, "mode remote hold"); - break; -#endif - - case WPS_TOKEN_REPEAT_MODE: - snprintf(buf, bufsize, "mode repeat"); - break; - - case WPS_TOKEN_PLAYBACK_STATUS: - snprintf(buf, bufsize, "mode playback"); - break; - - case WPS_TOKEN_RTC_DAY_OF_MONTH: - snprintf(buf, bufsize, "rtc: day of month (01..31)"); - break; - case WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED: - snprintf(buf, bufsize, - "rtc: day of month, blank padded ( 1..31)"); - break; - case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED: - snprintf(buf, bufsize, "rtc: hour (00..23)"); - break; - case WPS_TOKEN_RTC_HOUR_24: - snprintf(buf, bufsize, "rtc: hour ( 0..23)"); - break; - case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED: - snprintf(buf, bufsize, "rtc: hour (01..12)"); - break; - case WPS_TOKEN_RTC_HOUR_12: - snprintf(buf, bufsize, "rtc: hour ( 1..12)"); - break; - case WPS_TOKEN_RTC_MONTH: - snprintf(buf, bufsize, "rtc: month (01..12)"); - break; - case WPS_TOKEN_RTC_MINUTE: - snprintf(buf, bufsize, "rtc: minute (00..59)"); - break; - case WPS_TOKEN_RTC_SECOND: - snprintf(buf, bufsize, "rtc: second (00..59)"); - break; - case WPS_TOKEN_RTC_YEAR_2_DIGITS: - snprintf(buf, bufsize, - "rtc: last two digits of year (00..99)"); - break; - case WPS_TOKEN_RTC_YEAR_4_DIGITS: - snprintf(buf, bufsize, "rtc: year (1970...)"); - break; - case WPS_TOKEN_RTC_AM_PM_UPPER: - snprintf(buf, bufsize, - "rtc: upper case AM or PM indicator"); - break; - case WPS_TOKEN_RTC_AM_PM_LOWER: - snprintf(buf, bufsize, - "rtc: lower case am or pm indicator"); - break; - case WPS_TOKEN_RTC_WEEKDAY_NAME: - snprintf(buf, bufsize, - "rtc: abbreviated weekday name (Sun..Sat)"); - break; - case WPS_TOKEN_RTC_MONTH_NAME: - snprintf(buf, bufsize, - "rtc: abbreviated month name (Jan..Dec)"); - break; - case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON: - snprintf(buf, bufsize, - "rtc: day of week (1..7); 1 is Monday"); - break; - case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN: - snprintf(buf, bufsize, - "rtc: day of week (0..6); 0 is Sunday"); - break; - -#if (CONFIG_CODEC == SWCODEC) - case WPS_TOKEN_CROSSFADE: - snprintf(buf, bufsize, "crossfade"); - break; - - case WPS_TOKEN_REPLAYGAIN: - snprintf(buf, bufsize, "replaygain"); - break; -#endif - -#ifdef HAVE_ALBUMART - case WPS_TOKEN_ALBUMART_DISPLAY: - snprintf(buf, bufsize, "album art display"); - break; - - case WPS_TOKEN_ALBUMART_FOUND: - snprintf(buf, bufsize, "%strack album art conditional", - next_str(next)); - break; -#endif - -#ifdef HAVE_LCD_BITMAP - case WPS_TOKEN_IMAGE_BACKDROP: - snprintf(buf, bufsize, "backdrop image"); - break; - - case WPS_TOKEN_IMAGE_PROGRESS_BAR: - snprintf(buf, bufsize, "progressbar bitmap"); - break; - - case WPS_TOKEN_PEAKMETER: - snprintf(buf, bufsize, "peakmeter"); - break; -#endif - - case WPS_TOKEN_PROGRESSBAR: - snprintf(buf, bufsize, "progressbar"); - break; - -#ifdef HAVE_LCD_CHARCELLS - case WPS_TOKEN_PLAYER_PROGRESSBAR: - snprintf(buf, bufsize, "full line progressbar"); - break; -#endif - - case WPS_TOKEN_TRACK_TIME_ELAPSED: - snprintf(buf, bufsize, "time elapsed in track"); - break; - - case WPS_TOKEN_TRACK_ELAPSED_PERCENT: - snprintf(buf, bufsize, "played percentage of track"); - break; - - case WPS_TOKEN_PLAYLIST_ENTRIES: - snprintf(buf, bufsize, "number of entries in playlist"); - break; - - case WPS_TOKEN_PLAYLIST_NAME: - snprintf(buf, bufsize, "playlist name"); - break; - - case WPS_TOKEN_PLAYLIST_POSITION: - snprintf(buf, bufsize, "position in playlist"); - break; - - case WPS_TOKEN_TRACK_TIME_REMAINING: - snprintf(buf, bufsize, "time remaining in track"); - break; - - case WPS_TOKEN_PLAYLIST_SHUFFLE: - snprintf(buf, bufsize, "playlist shuffle mode"); - break; - - case WPS_TOKEN_TRACK_LENGTH: - snprintf(buf, bufsize, "track length"); - break; - - case WPS_TOKEN_VOLUME: - snprintf(buf, bufsize, "volume"); - break; - - case WPS_TOKEN_METADATA_ARTIST: - snprintf(buf, bufsize, "%strack artist", - next_str(next)); - break; - - case WPS_TOKEN_METADATA_COMPOSER: - snprintf(buf, bufsize, "%strack composer", - next_str(next)); - break; - - case WPS_TOKEN_METADATA_ALBUM: - snprintf(buf, bufsize, "%strack album", - next_str(next)); - break; - - case WPS_TOKEN_METADATA_GROUPING: - snprintf(buf, bufsize, "%strack grouping", - next_str(next)); - break; - - case WPS_TOKEN_METADATA_GENRE: - snprintf(buf, bufsize, "%strack genre", - next_str(next)); - break; - - case WPS_TOKEN_METADATA_DISC_NUMBER: - snprintf(buf, bufsize, "%strack disc", next_str(next)); - break; - - case WPS_TOKEN_METADATA_TRACK_NUMBER: - snprintf(buf, bufsize, "%strack number", - next_str(next)); - break; - - case WPS_TOKEN_METADATA_TRACK_TITLE: - snprintf(buf, bufsize, "%strack title", - next_str(next)); - break; - - case WPS_TOKEN_METADATA_VERSION: - snprintf(buf, bufsize, "%strack ID3 version", - next_str(next)); - break; - - case WPS_TOKEN_METADATA_ALBUM_ARTIST: - snprintf(buf, bufsize, "%strack album artist", - next_str(next)); - break; - - case WPS_TOKEN_METADATA_COMMENT: - snprintf(buf, bufsize, "%strack comment", - next_str(next)); - break; - - case WPS_TOKEN_METADATA_YEAR: - snprintf(buf, bufsize, "%strack year", next_str(next)); - break; - -#ifdef HAVE_TAGCACHE - case WPS_TOKEN_DATABASE_PLAYCOUNT: - snprintf(buf, bufsize, "track playcount (database)"); - break; - - case WPS_TOKEN_DATABASE_RATING: - snprintf(buf, bufsize, "track rating (database)"); - break; - - case WPS_TOKEN_DATABASE_AUTOSCORE: - snprintf(buf, bufsize, "track autoscore (database)"); - break; -#endif - - case WPS_TOKEN_BATTERY_PERCENT: - snprintf(buf, bufsize, "battery percentage"); - break; - - case WPS_TOKEN_BATTERY_VOLTS: - snprintf(buf, bufsize, "battery voltage"); - break; - - case WPS_TOKEN_BATTERY_TIME: - snprintf(buf, bufsize, "battery time left"); - break; - - case WPS_TOKEN_BATTERY_CHARGER_CONNECTED: - snprintf(buf, bufsize, "battery charger connected"); - break; - - case WPS_TOKEN_BATTERY_CHARGING: - snprintf(buf, bufsize, "battery charging"); - break; - - case WPS_TOKEN_BATTERY_SLEEPTIME: - snprintf(buf, bufsize, "sleep timer"); - break; - - case WPS_TOKEN_FILE_BITRATE: - snprintf(buf, bufsize, "%sfile bitrate", next_str(next)); - break; - - case WPS_TOKEN_FILE_CODEC: - snprintf(buf, bufsize, "%sfile codec", next_str(next)); - break; - - case WPS_TOKEN_FILE_FREQUENCY: - snprintf(buf, bufsize, "%sfile audio frequency in Hz", - next_str(next)); - break; - - case WPS_TOKEN_FILE_FREQUENCY_KHZ: - snprintf(buf, bufsize, "%sfile audio frequency in KHz", - next_str(next)); - break; - - case WPS_TOKEN_FILE_NAME: - snprintf(buf, bufsize, "%sfile name", next_str(next)); - break; - - case WPS_TOKEN_FILE_NAME_WITH_EXTENSION: - snprintf(buf, bufsize, "%sfile name with extension", - next_str(next)); - break; - - case WPS_TOKEN_FILE_PATH: - snprintf(buf, bufsize, "%sfile path", next_str(next)); - break; - - case WPS_TOKEN_FILE_SIZE: - snprintf(buf, bufsize, "%sfile size", next_str(next)); - break; - - case WPS_TOKEN_FILE_VBR: - snprintf(buf, bufsize, "%sfile is vbr", next_str(next)); - break; - - case WPS_TOKEN_FILE_DIRECTORY: - snprintf(buf, bufsize, "%sfile directory, level: %d", - next_str(next), token->value.i); - break; - -#if (CONFIG_CODEC != MAS3507D) - case WPS_TOKEN_SOUND_PITCH: - snprintf(buf, bufsize, "pitch value"); - break; -#endif - case WPS_VIEWPORT_ENABLE: - snprintf(buf, bufsize, "enable VP:%d", - token->value.i); - break; - case WPS_TOKEN_BUTTON_VOLUME: - snprintf(buf, bufsize, "Volume button timeout:%d", - token->value.i); - break; - default: - snprintf(buf, bufsize, "FIXME (code: %d)", - token->type); - break; - } - - return buf; -} - -#if defined(SIMULATOR) || defined(__PCTOOL__) -static void dump_wps_tokens(struct wps_data *data) -{ - struct wps_token *token; - int i, j; - int indent = 0; - char buf[64]; - int num_string_tokens = 0; - - /* Dump parsed WPS */ - for (i = 0, token = data->tokens; i < data->num_tokens; i++, token++) - { - get_token_desc(token, data, buf, sizeof(buf)); - - switch(token->type) - { - case WPS_TOKEN_STRING: - num_string_tokens++; - break; - - case WPS_TOKEN_CONDITIONAL_START: - indent++; - break; - - case WPS_TOKEN_CONDITIONAL_END: - indent--; - break; - - default: - break; - } - - if (wps_verbose_level > 2) - { - for(j = 0; j < indent; j++) { - DEBUGF("\t"); - } - - DEBUGF("[%3d] = (%2d) %s\n", i, token->type, buf); - } - } - - if (wps_verbose_level > 0) - { - DEBUGF("\n"); - DEBUGF("Number of string tokens: %d\n", num_string_tokens); - DEBUGF("\n"); - } -} - -static void print_line_info(struct wps_data *data) -{ - int i, j, v; - struct wps_line *line; - struct wps_subline *subline; - - if (wps_verbose_level > 0) - { - DEBUGF("Number of viewports : %d\n", data->num_viewports); - for (v = 0; v < data->num_viewports; v++) - { - DEBUGF("vp %d: First line: %d\n", v, data->viewports[v].first_line); - DEBUGF("vp %d: Last line: %d\n", v, data->viewports[v].last_line); - } - DEBUGF("Number of sublines : %d\n", data->num_sublines); - DEBUGF("Number of tokens : %d\n", data->num_tokens); - DEBUGF("\n"); - } - - if (wps_verbose_level > 1) - { - for (v = 0; v < data->num_viewports; v++) - { - DEBUGF("Viewport %d - +%d+%d (%dx%d)\n",v,data->viewports[v].vp.x, - data->viewports[v].vp.y, - data->viewports[v].vp.width, - data->viewports[v].vp.height); - for (i = data->viewports[v].first_line, line = &data->lines[data->viewports[v].first_line]; i <= data->viewports[v].last_line; i++,line++) - { - DEBUGF("Line %2d (num_sublines=%d, first_subline=%d)\n", - i, line->num_sublines, line->first_subline_idx); - - for (j = 0, subline = data->sublines + line->first_subline_idx; - j < line->num_sublines; j++, subline++) - { - DEBUGF(" Subline %d: first_token=%3d, last_token=%3d", - j, subline->first_token_idx, - wps_last_token_index(data, i, j)); - - if (subline->line_type & WPS_REFRESH_SCROLL) - DEBUGF(", scrolled"); - else if (subline->line_type & WPS_REFRESH_PLAYER_PROGRESS) - DEBUGF(", progressbar"); - else if (subline->line_type & WPS_REFRESH_PEAK_METER) - DEBUGF(", peakmeter"); - - DEBUGF("\n"); - } - } - } - - DEBUGF("\n"); - } -} - -static void print_wps_strings(struct wps_data *data) -{ - int i, len, total_len = 0, buf_used = 0; - - if (wps_verbose_level > 1) DEBUGF("Strings:\n"); - for (i = 0; i < data->num_strings; i++) - { - len = strlen(data->strings[i]); - total_len += len; - buf_used += len + 1; - if (wps_verbose_level > 1) - DEBUGF("%2d: (%2d) '%s'\n", i, len, data->strings[i]); - } - if (wps_verbose_level > 1) DEBUGF("\n"); - - if (wps_verbose_level > 0) - { - DEBUGF("Number of unique strings: %d (max: %d)\n", - data->num_strings, WPS_MAX_STRINGS); - DEBUGF("Total string length: %d\n", total_len); - DEBUGF("String buffer used: %d out of %d bytes\n", - buf_used, STRING_BUFFER_SIZE); - DEBUGF("\n"); - } -} -#endif - -void print_debug_info(struct wps_data *data, enum wps_parse_error fail, int line) -{ -#if defined(SIMULATOR) || defined(__PCTOOL__) - if (debug_wps && wps_verbose_level) - { - dump_wps_tokens(data); - print_wps_strings(data); - print_line_info(data); - } -#endif /* SIMULATOR */ - - if (data->num_tokens >= WPS_MAX_TOKENS - 1) { - DEBUGF("Warning: Max number of tokens was reached (%d)\n", - WPS_MAX_TOKENS - 1); - } - - if (fail != PARSE_OK) - { - char buf[64]; - - DEBUGF("ERR: Failed parsing on line %d : ", line); - switch (fail) - { - case PARSE_OK: - break; - - case PARSE_FAIL_UNCLOSED_COND: - DEBUGF("ERR: Unclosed conditional"); - break; - - case PARSE_FAIL_INVALID_CHAR: - DEBUGF("ERR: Unexpected conditional char after token %d: \"%s\"", - data->num_tokens-1, - get_token_desc(&data->tokens[data->num_tokens-1], data, - buf, sizeof(buf)) - ); - break; - - case PARSE_FAIL_COND_SYNTAX_ERROR: - DEBUGF("ERR: Conditional syntax error after token %d: \"%s\"", - data->num_tokens-1, - get_token_desc(&data->tokens[data->num_tokens-1], data, - buf, sizeof(buf)) - ); - break; - - case PARSE_FAIL_COND_INVALID_PARAM: - DEBUGF("ERR: Invalid parameter list for token %d: \"%s\"", - data->num_tokens, - get_token_desc(&data->tokens[data->num_tokens], data, - buf, sizeof(buf)) - ); - break; - - case PARSE_FAIL_LIMITS_EXCEEDED: - DEBUGF("ERR: Limits exceeded"); - break; - } - DEBUGF("\n"); - } -} - -#endif /* DEBUG || SIMULATOR */ 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 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2007 Nicolas Pennequin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef _WPS_H +#define _WPS_H + +#include "screen_access.h" +#include "statusbar.h" +#include "metadata.h" + +/* constants used in line_type and as refresh_mode for wps_refresh */ +#define WPS_REFRESH_STATIC (1u<<0) /* line doesn't change over time */ +#define WPS_REFRESH_DYNAMIC (1u<<1) /* line may change (e.g. time flag) */ +#define WPS_REFRESH_SCROLL (1u<<2) /* line scrolls */ +#define WPS_REFRESH_PLAYER_PROGRESS (1u<<3) /* line contains a progress bar */ +#define WPS_REFRESH_PEAK_METER (1u<<4) /* line contains a peak meter */ +#define WPS_REFRESH_STATUSBAR (1u<<5) /* refresh statusbar */ +#define WPS_REFRESH_ALL (0xffffffffu) /* to refresh all line types */ + +/* to refresh only those lines that change over time */ +#define WPS_REFRESH_NON_STATIC (WPS_REFRESH_DYNAMIC| \ + WPS_REFRESH_PLAYER_PROGRESS| \ + WPS_REFRESH_PEAK_METER) +/* alignments */ +#define WPS_ALIGN_RIGHT 32 +#define WPS_ALIGN_CENTER 64 +#define WPS_ALIGN_LEFT 128 + +#ifdef HAVE_ALBUMART + +/* albumart definitions */ +#define WPS_ALBUMART_NONE 0 /* WPS does not contain AA tag */ +#define WPS_ALBUMART_CHECK 1 /* WPS contains AA conditional tag */ +#define WPS_ALBUMART_LOAD 2 /* WPS contains AA tag */ + +#define WPS_ALBUMART_ALIGN_RIGHT 1 /* x align: right */ +#define WPS_ALBUMART_ALIGN_CENTER 2 /* x/y align: center */ +#define WPS_ALBUMART_ALIGN_LEFT 4 /* x align: left */ +#define WPS_ALBUMART_ALIGN_TOP 1 /* y align: top */ +#define WPS_ALBUMART_ALIGN_BOTTOM 4 /* y align: bottom */ + +#endif /* HAVE_ALBUMART */ + +/* wps_data*/ + +#ifdef HAVE_LCD_BITMAP +struct gui_img { + struct bitmap bm; + struct viewport* vp; /* The viewport to display this image in */ + short int x; /* x-pos */ + short int y; /* y-pos */ + short int num_subimages; /* number of sub-images */ + short int subimage_height; /* height of each sub-image */ + short int display; /* -1 for no display, 0..n to display a subimage */ + bool loaded; /* load state */ + bool always_display; /* not using the preload/display mechanism */ +}; + +struct progressbar { + /* regular pb */ + short x; + /* >=0: explicitly set in the tag -> y-coord within the viewport + <0 : not set in the tag -> negated 1-based line number within + the viewport. y-coord will be computed based on the font height */ + short y; + short width; + short height; + /*progressbar image*/ + struct bitmap bm; + bool have_bitmap_pb; +}; +#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 MAX_PROGRESSBARS 3 + +/* The image buffer is big enough to store one full-screen native bitmap, + plus two full-screen mono bitmaps. */ + +#define IMG_BUFSIZE ((LCD_HEIGHT*LCD_WIDTH*LCD_DEPTH/8) \ + + (2*LCD_HEIGHT*LCD_WIDTH/8)) + +#define WPS_MAX_VIEWPORTS 24 +#define WPS_MAX_LINES ((LCD_HEIGHT/5+1) * 2) +#define WPS_MAX_SUBLINES (WPS_MAX_LINES*3) +#define WPS_MAX_TOKENS 1024 +#define WPS_MAX_STRINGS 128 +#define STRING_BUFFER_SIZE 1024 +#define WPS_MAX_COND_LEVEL 10 + +#else + +#define WPS_MAX_VIEWPORTS 2 +#define WPS_MAX_LINES 2 +#define WPS_MAX_SUBLINES 12 +#define WPS_MAX_TOKENS 64 +#define WPS_MAX_STRINGS 32 +#define STRING_BUFFER_SIZE 64 +#define WPS_MAX_COND_LEVEL 5 + +#endif + +#define SUBLINE_RESET -1 + +enum wps_parse_error { + PARSE_OK, + PARSE_FAIL_UNCLOSED_COND, + PARSE_FAIL_INVALID_CHAR, + PARSE_FAIL_COND_SYNTAX_ERROR, + PARSE_FAIL_COND_INVALID_PARAM, + PARSE_FAIL_LIMITS_EXCEEDED, +}; + +enum wps_token_type { + WPS_NO_TOKEN, /* for WPS tags we don't want to save as tokens */ + WPS_TOKEN_UNKNOWN, + + /* Markers */ + WPS_TOKEN_CHARACTER, + WPS_TOKEN_STRING, + + /* Alignment */ + WPS_TOKEN_ALIGN_LEFT, + WPS_TOKEN_ALIGN_CENTER, + WPS_TOKEN_ALIGN_RIGHT, + + /* Sublines */ + WPS_TOKEN_SUBLINE_TIMEOUT, + + /* Battery */ + WPS_TOKEN_BATTERY_PERCENT, + WPS_TOKEN_BATTERY_VOLTS, + WPS_TOKEN_BATTERY_TIME, + WPS_TOKEN_BATTERY_CHARGER_CONNECTED, + WPS_TOKEN_BATTERY_CHARGING, + WPS_TOKEN_BATTERY_SLEEPTIME, + + /* Sound */ +#if (CONFIG_CODEC != MAS3507D) + WPS_TOKEN_SOUND_PITCH, +#endif +#if (CONFIG_CODEC == SWCODEC) + WPS_TOKEN_REPLAYGAIN, + WPS_TOKEN_CROSSFADE, +#endif + + /* Time */ + + WPS_TOKEN_RTC_PRESENT, + + /* The begin/end values allow us to know if a token is an RTC one. + New RTC tokens should be added between the markers. */ + + WPS_TOKENS_RTC_BEGIN, /* just the start marker, not an actual token */ + + WPS_TOKEN_RTC_DAY_OF_MONTH, + WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED, + WPS_TOKEN_RTC_12HOUR_CFG, + WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, + WPS_TOKEN_RTC_HOUR_24, + WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, + WPS_TOKEN_RTC_HOUR_12, + WPS_TOKEN_RTC_MONTH, + WPS_TOKEN_RTC_MINUTE, + WPS_TOKEN_RTC_SECOND, + WPS_TOKEN_RTC_YEAR_2_DIGITS, + WPS_TOKEN_RTC_YEAR_4_DIGITS, + WPS_TOKEN_RTC_AM_PM_UPPER, + WPS_TOKEN_RTC_AM_PM_LOWER, + WPS_TOKEN_RTC_WEEKDAY_NAME, + WPS_TOKEN_RTC_MONTH_NAME, + WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, + WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, + + WPS_TOKENS_RTC_END, /* just the end marker, not an actual token */ + + /* Conditional */ + WPS_TOKEN_CONDITIONAL, + WPS_TOKEN_CONDITIONAL_START, + WPS_TOKEN_CONDITIONAL_OPTION, + WPS_TOKEN_CONDITIONAL_END, + + /* Database */ +#ifdef HAVE_TAGCACHE + WPS_TOKEN_DATABASE_PLAYCOUNT, + WPS_TOKEN_DATABASE_RATING, + WPS_TOKEN_DATABASE_AUTOSCORE, +#endif + + /* File */ + WPS_TOKEN_FILE_BITRATE, + WPS_TOKEN_FILE_CODEC, + WPS_TOKEN_FILE_FREQUENCY, + WPS_TOKEN_FILE_FREQUENCY_KHZ, + WPS_TOKEN_FILE_NAME, + WPS_TOKEN_FILE_NAME_WITH_EXTENSION, + WPS_TOKEN_FILE_PATH, + WPS_TOKEN_FILE_SIZE, + WPS_TOKEN_FILE_VBR, + WPS_TOKEN_FILE_DIRECTORY, + +#ifdef HAVE_LCD_BITMAP + /* Image */ + WPS_TOKEN_IMAGE_BACKDROP, + WPS_TOKEN_IMAGE_PROGRESS_BAR, + WPS_TOKEN_IMAGE_PRELOAD, + WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, + WPS_TOKEN_IMAGE_DISPLAY, +#endif + +#ifdef HAVE_ALBUMART + /* Albumart */ + WPS_TOKEN_ALBUMART_DISPLAY, + WPS_TOKEN_ALBUMART_FOUND, +#endif + + /* Metadata */ + WPS_TOKEN_METADATA_ARTIST, + WPS_TOKEN_METADATA_COMPOSER, + WPS_TOKEN_METADATA_ALBUM_ARTIST, + WPS_TOKEN_METADATA_GROUPING, + WPS_TOKEN_METADATA_ALBUM, + WPS_TOKEN_METADATA_GENRE, + WPS_TOKEN_METADATA_DISC_NUMBER, + WPS_TOKEN_METADATA_TRACK_NUMBER, + WPS_TOKEN_METADATA_TRACK_TITLE, + WPS_TOKEN_METADATA_VERSION, + WPS_TOKEN_METADATA_YEAR, + WPS_TOKEN_METADATA_COMMENT, + + /* Mode */ + WPS_TOKEN_REPEAT_MODE, + WPS_TOKEN_PLAYBACK_STATUS, + + WPS_TOKEN_MAIN_HOLD, + +#ifdef HAS_REMOTE_BUTTON_HOLD + WPS_TOKEN_REMOTE_HOLD, +#endif + + /* Progressbar */ + WPS_TOKEN_PROGRESSBAR, +#ifdef HAVE_LCD_CHARCELLS + WPS_TOKEN_PLAYER_PROGRESSBAR, +#endif + +#ifdef HAVE_LCD_BITMAP + /* Peakmeter */ + WPS_TOKEN_PEAKMETER, +#endif + + /* Volume level */ + WPS_TOKEN_VOLUME, + + /* Current track */ + WPS_TOKEN_TRACK_ELAPSED_PERCENT, + WPS_TOKEN_TRACK_TIME_ELAPSED, + WPS_TOKEN_TRACK_TIME_REMAINING, + WPS_TOKEN_TRACK_LENGTH, + + /* Playlist */ + WPS_TOKEN_PLAYLIST_ENTRIES, + WPS_TOKEN_PLAYLIST_NAME, + WPS_TOKEN_PLAYLIST_POSITION, + WPS_TOKEN_PLAYLIST_SHUFFLE, + +#if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD) + /* Virtual LED */ + WPS_TOKEN_VLED_HDD, +#endif + + /* Viewport display */ + WPS_VIEWPORT_ENABLE, + + /* buttons */ + WPS_TOKEN_BUTTON_VOLUME, + WPS_TOKEN_LASTTOUCH, + + /* Setting option */ + WPS_TOKEN_SETTING, +}; + +struct wps_token { + unsigned char type; /* enough to store the token type */ + + /* Whether the tag (e.g. track name or the album) refers the + current or the next song (false=current, true=next) */ + bool next; + + union { + char c; + unsigned short i; + } value; +}; + +/* Description of a subline on the WPS */ +struct wps_subline { + + /* Index of the first token for this subline in the token array. + Tokens of this subline end where tokens for the next subline + begin. */ + unsigned short first_token_idx; + + /* Bit or'ed WPS_REFRESH_xxx */ + unsigned char line_type; + + /* How long the subline should be displayed, in 10ths of sec */ + unsigned char time_mult; +}; + +/* Description of a line on the WPS. A line is a set of sublines. + A subline is displayed for a certain amount of time. After that, + the next subline of the line is displayed. And so on. */ +struct wps_line { + + /* Number of sublines in this line */ + signed char num_sublines; + + /* Number (0-based) of the subline within this line currently being displayed */ + signed char curr_subline; + + /* Index of the first subline of this line in the subline array. + Sublines for this line end where sublines for the next line begin. */ + unsigned short first_subline_idx; + + /* When the next subline of this line should be displayed + (absolute time value in ticks) */ + long subline_expire_time; +}; + +#define VP_DRAW_HIDEABLE 0x1 +#define VP_DRAW_HIDDEN 0x2 +#define VP_DRAW_WASHIDDEN 0x4 +struct wps_viewport { + struct viewport vp; /* The LCD viewport struct */ + struct progressbar *pb; + /* Indexes of the first and last lines belonging to this viewport in the + lines[] array */ + int first_line, last_line; + char hidden_flags; + char label; +}; + +#ifdef HAVE_TOUCHSCREEN +struct touchregion { + struct wps_viewport* wvp;/* The viewport this region is in */ + short int x; /* x-pos */ + short int y; /* y-pos */ + short int width; /* width */ + short int height; /* height */ + bool repeat; /* requires the area be held for the action */ + int action; /* action this button will return */ +}; +#define MAX_TOUCHREGIONS 15 +#endif +/* wps_data + this struct holds 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]; + unsigned char* img_buf_ptr; + int img_buf_free; + bool wps_sb_tag; + bool show_sb_on_wps; + + struct progressbar progressbar[MAX_PROGRESSBARS]; + short progressbar_count; + + bool peak_meter_enabled; + +#ifdef HAVE_ALBUMART + /* Album art support */ + unsigned char wps_uses_albumart; /* WPS_ALBUMART_NONE, _CHECK, _LOAD */ + short albumart_x; + short albumart_y; + unsigned char albumart_xalign; /* WPS_ALBUMART_ALIGN_LEFT, _CENTER, _RIGHT */ + unsigned char albumart_yalign; /* WPS_ALBUMART_ALIGN_TOP, _CENTER, _BOTTOM */ + short albumart_max_width; + short albumart_max_height; + + int albumart_cond_index; +#endif + +#else /*HAVE_LCD_CHARCELLS */ + unsigned short wps_progress_pat[8]; + bool full_line_progressbar; +#endif + +#ifdef HAVE_TOUCHSCREEN + struct touchregion touchregion[MAX_TOUCHREGIONS]; + short touchregion_count; +#endif + +#ifdef HAVE_REMOTE_LCD + bool remote_wps; +#endif + + /* Number of lines in the WPS. During WPS parsing, this is + the index of the line being parsed. */ + int num_lines; + + /* Number of viewports in the WPS */ + int num_viewports; + struct wps_viewport viewports[WPS_MAX_VIEWPORTS]; + + struct wps_line lines[WPS_MAX_LINES]; + + /* Total number of sublines in the WPS. During WPS parsing, this is + the index of the subline where the parsed tokens are added to. */ + int num_sublines; + struct wps_subline sublines[WPS_MAX_SUBLINES]; + + /* Total number of tokens in the WPS. During WPS parsing, this is + the index of the token being parsed. */ + int num_tokens; + struct wps_token tokens[WPS_MAX_TOKENS]; + + char string_buffer[STRING_BUFFER_SIZE]; + char *strings[WPS_MAX_STRINGS]; + int num_strings; + + bool wps_loaded; + + /* tick the volume button was last pressed */ + unsigned int button_time_volume; +}; + +/* 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, + struct screen *display, + const char *buf, + bool isfile); + +/* Redraw statusbars if necessary */ +void gwps_draw_statusbars(void); + +/* Returns the index of the subline in the subline array + line - 0-based line number + subline - 0-based subline number within the line + */ +int wps_subline_index(struct wps_data *wps_data, int line, int subline); + +/* Returns the index of the first subline's token in the token array + line - 0-based line number + subline - 0-based subline number within the line + */ +int wps_first_token_index(struct wps_data *data, int line, int subline); + +/* Returns the index of the last subline's token in the token array. + line - 0-based line number + subline - 0-based subline number within the line + */ +int wps_last_token_index(struct wps_data *data, int line, int subline); + +/* 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; + bool do_full_update; +}; + + +/* 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); Currently unused */ + +/* 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); Currently unused */ +/* wps_state end*/ + +/* gui_wps + defines a wps with its 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; +}; + +/* gui_wps end */ + +long gui_wps_show(void); + +/* currently only on wps_state is needed */ +extern struct wps_state wps_state; +extern struct gui_wps gui_wps[NB_SCREENS]; + +void gui_sync_wps_init(void); + +#ifdef HAVE_TOUCHSCREEN +int wps_get_touchaction(struct wps_data *data); +#endif + +#ifdef HAVE_ALBUMART +/* gives back if WPS contains an albumart tag */ +bool gui_sync_wps_uses_albumart(void); +#endif + +#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 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2007 Nicolas Pennequin, Dan Everton, Matthias Mohr + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#if defined(DEBUG) || defined(SIMULATOR) + +#include +#include +#include "wps_internals.h" +#ifdef __PCTOOL__ +#ifdef WPSEDITOR +#include "proxy.h" +#else +#define DEBUGF printf +#endif +#else +#include "debug.h" +#endif + +#if defined(SIMULATOR) || defined(__PCTOOL__) +extern bool debug_wps; +extern int wps_verbose_level; +#endif + +static char *next_str(bool next) { + return next ? "next " : ""; +} + +static char *get_token_desc(struct wps_token *token, struct wps_data *data, + char *buf, int bufsize) +{ + bool next = token->next; + + switch(token->type) + { + case WPS_NO_TOKEN: + snprintf(buf, bufsize, "No token"); + break; + + case WPS_TOKEN_UNKNOWN: + snprintf(buf, bufsize, "Unknown token"); + break; + + case WPS_TOKEN_CHARACTER: + snprintf(buf, bufsize, "Character '%c'", + token->value.c); + break; + + case WPS_TOKEN_STRING: + snprintf(buf, bufsize, "String '%s'", + data->strings[token->value.i]); + break; + +#ifdef HAVE_LCD_BITMAP + case WPS_TOKEN_ALIGN_LEFT: + snprintf(buf, bufsize, "align left"); + break; + + case WPS_TOKEN_ALIGN_CENTER: + snprintf(buf, bufsize, "align center"); + break; + + case WPS_TOKEN_ALIGN_RIGHT: + snprintf(buf, bufsize, "align right"); + break; +#endif + + case WPS_TOKEN_SUBLINE_TIMEOUT: + snprintf(buf, bufsize, "subline timeout value: %d", + token->value.i); + break; + + case WPS_TOKEN_CONDITIONAL: + snprintf(buf, bufsize, "conditional, %d options", + token->value.i); + break; + + case WPS_TOKEN_CONDITIONAL_START: + snprintf(buf, bufsize, "conditional start, next cond: %d", + token->value.i); + break; + + case WPS_TOKEN_CONDITIONAL_OPTION: + snprintf(buf, bufsize, "conditional option, next cond: %d", + token->value.i); + break; + + case WPS_TOKEN_CONDITIONAL_END: + snprintf(buf, bufsize, "conditional end"); + break; + +#ifdef HAVE_LCD_BITMAP + case WPS_TOKEN_IMAGE_PRELOAD: + snprintf(buf, bufsize, "preload image"); + break; + + case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY: + snprintf(buf, bufsize, "display preloaded image %d", + token->value.i); + break; + + case WPS_TOKEN_IMAGE_DISPLAY: + snprintf(buf, bufsize, "display image"); + break; +#endif + +#ifdef HAS_BUTTON_HOLD + case WPS_TOKEN_MAIN_HOLD: + snprintf(buf, bufsize, "mode hold"); + break; +#endif + +#ifdef HAS_REMOTE_BUTTON_HOLD + case WPS_TOKEN_REMOTE_HOLD: + snprintf(buf, bufsize, "mode remote hold"); + break; +#endif + + case WPS_TOKEN_REPEAT_MODE: + snprintf(buf, bufsize, "mode repeat"); + break; + + case WPS_TOKEN_PLAYBACK_STATUS: + snprintf(buf, bufsize, "mode playback"); + break; + + case WPS_TOKEN_RTC_DAY_OF_MONTH: + snprintf(buf, bufsize, "rtc: day of month (01..31)"); + break; + case WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED: + snprintf(buf, bufsize, + "rtc: day of month, blank padded ( 1..31)"); + break; + case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED: + snprintf(buf, bufsize, "rtc: hour (00..23)"); + break; + case WPS_TOKEN_RTC_HOUR_24: + snprintf(buf, bufsize, "rtc: hour ( 0..23)"); + break; + case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED: + snprintf(buf, bufsize, "rtc: hour (01..12)"); + break; + case WPS_TOKEN_RTC_HOUR_12: + snprintf(buf, bufsize, "rtc: hour ( 1..12)"); + break; + case WPS_TOKEN_RTC_MONTH: + snprintf(buf, bufsize, "rtc: month (01..12)"); + break; + case WPS_TOKEN_RTC_MINUTE: + snprintf(buf, bufsize, "rtc: minute (00..59)"); + break; + case WPS_TOKEN_RTC_SECOND: + snprintf(buf, bufsize, "rtc: second (00..59)"); + break; + case WPS_TOKEN_RTC_YEAR_2_DIGITS: + snprintf(buf, bufsize, + "rtc: last two digits of year (00..99)"); + break; + case WPS_TOKEN_RTC_YEAR_4_DIGITS: + snprintf(buf, bufsize, "rtc: year (1970...)"); + break; + case WPS_TOKEN_RTC_AM_PM_UPPER: + snprintf(buf, bufsize, + "rtc: upper case AM or PM indicator"); + break; + case WPS_TOKEN_RTC_AM_PM_LOWER: + snprintf(buf, bufsize, + "rtc: lower case am or pm indicator"); + break; + case WPS_TOKEN_RTC_WEEKDAY_NAME: + snprintf(buf, bufsize, + "rtc: abbreviated weekday name (Sun..Sat)"); + break; + case WPS_TOKEN_RTC_MONTH_NAME: + snprintf(buf, bufsize, + "rtc: abbreviated month name (Jan..Dec)"); + break; + case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON: + snprintf(buf, bufsize, + "rtc: day of week (1..7); 1 is Monday"); + break; + case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN: + snprintf(buf, bufsize, + "rtc: day of week (0..6); 0 is Sunday"); + break; + +#if (CONFIG_CODEC == SWCODEC) + case WPS_TOKEN_CROSSFADE: + snprintf(buf, bufsize, "crossfade"); + break; + + case WPS_TOKEN_REPLAYGAIN: + snprintf(buf, bufsize, "replaygain"); + break; +#endif + +#ifdef HAVE_ALBUMART + case WPS_TOKEN_ALBUMART_DISPLAY: + snprintf(buf, bufsize, "album art display"); + break; + + case WPS_TOKEN_ALBUMART_FOUND: + snprintf(buf, bufsize, "%strack album art conditional", + next_str(next)); + break; +#endif + +#ifdef HAVE_LCD_BITMAP + case WPS_TOKEN_IMAGE_BACKDROP: + snprintf(buf, bufsize, "backdrop image"); + break; + + case WPS_TOKEN_IMAGE_PROGRESS_BAR: + snprintf(buf, bufsize, "progressbar bitmap"); + break; + + case WPS_TOKEN_PEAKMETER: + snprintf(buf, bufsize, "peakmeter"); + break; +#endif + + case WPS_TOKEN_PROGRESSBAR: + snprintf(buf, bufsize, "progressbar"); + break; + +#ifdef HAVE_LCD_CHARCELLS + case WPS_TOKEN_PLAYER_PROGRESSBAR: + snprintf(buf, bufsize, "full line progressbar"); + break; +#endif + + case WPS_TOKEN_TRACK_TIME_ELAPSED: + snprintf(buf, bufsize, "time elapsed in track"); + break; + + case WPS_TOKEN_TRACK_ELAPSED_PERCENT: + snprintf(buf, bufsize, "played percentage of track"); + break; + + case WPS_TOKEN_PLAYLIST_ENTRIES: + snprintf(buf, bufsize, "number of entries in playlist"); + break; + + case WPS_TOKEN_PLAYLIST_NAME: + snprintf(buf, bufsize, "playlist name"); + break; + + case WPS_TOKEN_PLAYLIST_POSITION: + snprintf(buf, bufsize, "position in playlist"); + break; + + case WPS_TOKEN_TRACK_TIME_REMAINING: + snprintf(buf, bufsize, "time remaining in track"); + break; + + case WPS_TOKEN_PLAYLIST_SHUFFLE: + snprintf(buf, bufsize, "playlist shuffle mode"); + break; + + case WPS_TOKEN_TRACK_LENGTH: + snprintf(buf, bufsize, "track length"); + break; + + case WPS_TOKEN_VOLUME: + snprintf(buf, bufsize, "volume"); + break; + + case WPS_TOKEN_METADATA_ARTIST: + snprintf(buf, bufsize, "%strack artist", + next_str(next)); + break; + + case WPS_TOKEN_METADATA_COMPOSER: + snprintf(buf, bufsize, "%strack composer", + next_str(next)); + break; + + case WPS_TOKEN_METADATA_ALBUM: + snprintf(buf, bufsize, "%strack album", + next_str(next)); + break; + + case WPS_TOKEN_METADATA_GROUPING: + snprintf(buf, bufsize, "%strack grouping", + next_str(next)); + break; + + case WPS_TOKEN_METADATA_GENRE: + snprintf(buf, bufsize, "%strack genre", + next_str(next)); + break; + + case WPS_TOKEN_METADATA_DISC_NUMBER: + snprintf(buf, bufsize, "%strack disc", next_str(next)); + break; + + case WPS_TOKEN_METADATA_TRACK_NUMBER: + snprintf(buf, bufsize, "%strack number", + next_str(next)); + break; + + case WPS_TOKEN_METADATA_TRACK_TITLE: + snprintf(buf, bufsize, "%strack title", + next_str(next)); + break; + + case WPS_TOKEN_METADATA_VERSION: + snprintf(buf, bufsize, "%strack ID3 version", + next_str(next)); + break; + + case WPS_TOKEN_METADATA_ALBUM_ARTIST: + snprintf(buf, bufsize, "%strack album artist", + next_str(next)); + break; + + case WPS_TOKEN_METADATA_COMMENT: + snprintf(buf, bufsize, "%strack comment", + next_str(next)); + break; + + case WPS_TOKEN_METADATA_YEAR: + snprintf(buf, bufsize, "%strack year", next_str(next)); + break; + +#ifdef HAVE_TAGCACHE + case WPS_TOKEN_DATABASE_PLAYCOUNT: + snprintf(buf, bufsize, "track playcount (database)"); + break; + + case WPS_TOKEN_DATABASE_RATING: + snprintf(buf, bufsize, "track rating (database)"); + break; + + case WPS_TOKEN_DATABASE_AUTOSCORE: + snprintf(buf, bufsize, "track autoscore (database)"); + break; +#endif + + case WPS_TOKEN_BATTERY_PERCENT: + snprintf(buf, bufsize, "battery percentage"); + break; + + case WPS_TOKEN_BATTERY_VOLTS: + snprintf(buf, bufsize, "battery voltage"); + break; + + case WPS_TOKEN_BATTERY_TIME: + snprintf(buf, bufsize, "battery time left"); + break; + + case WPS_TOKEN_BATTERY_CHARGER_CONNECTED: + snprintf(buf, bufsize, "battery charger connected"); + break; + + case WPS_TOKEN_BATTERY_CHARGING: + snprintf(buf, bufsize, "battery charging"); + break; + + case WPS_TOKEN_BATTERY_SLEEPTIME: + snprintf(buf, bufsize, "sleep timer"); + break; + + case WPS_TOKEN_FILE_BITRATE: + snprintf(buf, bufsize, "%sfile bitrate", next_str(next)); + break; + + case WPS_TOKEN_FILE_CODEC: + snprintf(buf, bufsize, "%sfile codec", next_str(next)); + break; + + case WPS_TOKEN_FILE_FREQUENCY: + snprintf(buf, bufsize, "%sfile audio frequency in Hz", + next_str(next)); + break; + + case WPS_TOKEN_FILE_FREQUENCY_KHZ: + snprintf(buf, bufsize, "%sfile audio frequency in KHz", + next_str(next)); + break; + + case WPS_TOKEN_FILE_NAME: + snprintf(buf, bufsize, "%sfile name", next_str(next)); + break; + + case WPS_TOKEN_FILE_NAME_WITH_EXTENSION: + snprintf(buf, bufsize, "%sfile name with extension", + next_str(next)); + break; + + case WPS_TOKEN_FILE_PATH: + snprintf(buf, bufsize, "%sfile path", next_str(next)); + break; + + case WPS_TOKEN_FILE_SIZE: + snprintf(buf, bufsize, "%sfile size", next_str(next)); + break; + + case WPS_TOKEN_FILE_VBR: + snprintf(buf, bufsize, "%sfile is vbr", next_str(next)); + break; + + case WPS_TOKEN_FILE_DIRECTORY: + snprintf(buf, bufsize, "%sfile directory, level: %d", + next_str(next), token->value.i); + break; + +#if (CONFIG_CODEC != MAS3507D) + case WPS_TOKEN_SOUND_PITCH: + snprintf(buf, bufsize, "pitch value"); + break; +#endif + case WPS_VIEWPORT_ENABLE: + snprintf(buf, bufsize, "enable VP:%d", + token->value.i); + break; + case WPS_TOKEN_BUTTON_VOLUME: + snprintf(buf, bufsize, "Volume button timeout:%d", + token->value.i); + break; + default: + snprintf(buf, bufsize, "FIXME (code: %d)", + token->type); + break; + } + + return buf; +} + +#if defined(SIMULATOR) || defined(__PCTOOL__) +static void dump_wps_tokens(struct wps_data *data) +{ + struct wps_token *token; + int i, j; + int indent = 0; + char buf[64]; + int num_string_tokens = 0; + + /* Dump parsed WPS */ + for (i = 0, token = data->tokens; i < data->num_tokens; i++, token++) + { + get_token_desc(token, data, buf, sizeof(buf)); + + switch(token->type) + { + case WPS_TOKEN_STRING: + num_string_tokens++; + break; + + case WPS_TOKEN_CONDITIONAL_START: + indent++; + break; + + case WPS_TOKEN_CONDITIONAL_END: + indent--; + break; + + default: + break; + } + + if (wps_verbose_level > 2) + { + for(j = 0; j < indent; j++) { + DEBUGF("\t"); + } + + DEBUGF("[%3d] = (%2d) %s\n", i, token->type, buf); + } + } + + if (wps_verbose_level > 0) + { + DEBUGF("\n"); + DEBUGF("Number of string tokens: %d\n", num_string_tokens); + DEBUGF("\n"); + } +} + +static void print_line_info(struct wps_data *data) +{ + int i, j, v; + struct wps_line *line; + struct wps_subline *subline; + + if (wps_verbose_level > 0) + { + DEBUGF("Number of viewports : %d\n", data->num_viewports); + for (v = 0; v < data->num_viewports; v++) + { + DEBUGF("vp %d: First line: %d\n", v, data->viewports[v].first_line); + DEBUGF("vp %d: Last line: %d\n", v, data->viewports[v].last_line); + } + DEBUGF("Number of sublines : %d\n", data->num_sublines); + DEBUGF("Number of tokens : %d\n", data->num_tokens); + DEBUGF("\n"); + } + + if (wps_verbose_level > 1) + { + for (v = 0; v < data->num_viewports; v++) + { + DEBUGF("Viewport %d - +%d+%d (%dx%d)\n",v,data->viewports[v].vp.x, + data->viewports[v].vp.y, + data->viewports[v].vp.width, + data->viewports[v].vp.height); + for (i = data->viewports[v].first_line, line = &data->lines[data->viewports[v].first_line]; i <= data->viewports[v].last_line; i++,line++) + { + DEBUGF("Line %2d (num_sublines=%d, first_subline=%d)\n", + i, line->num_sublines, line->first_subline_idx); + + for (j = 0, subline = data->sublines + line->first_subline_idx; + j < line->num_sublines; j++, subline++) + { + DEBUGF(" Subline %d: first_token=%3d, last_token=%3d", + j, subline->first_token_idx, + wps_last_token_index(data, i, j)); + + if (subline->line_type & WPS_REFRESH_SCROLL) + DEBUGF(", scrolled"); + else if (subline->line_type & WPS_REFRESH_PLAYER_PROGRESS) + DEBUGF(", progressbar"); + else if (subline->line_type & WPS_REFRESH_PEAK_METER) + DEBUGF(", peakmeter"); + + DEBUGF("\n"); + } + } + } + + DEBUGF("\n"); + } +} + +static void print_wps_strings(struct wps_data *data) +{ + int i, len, total_len = 0, buf_used = 0; + + if (wps_verbose_level > 1) DEBUGF("Strings:\n"); + for (i = 0; i < data->num_strings; i++) + { + len = strlen(data->strings[i]); + total_len += len; + buf_used += len + 1; + if (wps_verbose_level > 1) + DEBUGF("%2d: (%2d) '%s'\n", i, len, data->strings[i]); + } + if (wps_verbose_level > 1) DEBUGF("\n"); + + if (wps_verbose_level > 0) + { + DEBUGF("Number of unique strings: %d (max: %d)\n", + data->num_strings, WPS_MAX_STRINGS); + DEBUGF("Total string length: %d\n", total_len); + DEBUGF("String buffer used: %d out of %d bytes\n", + buf_used, STRING_BUFFER_SIZE); + DEBUGF("\n"); + } +} +#endif + +void print_debug_info(struct wps_data *data, enum wps_parse_error fail, int line) +{ +#if defined(SIMULATOR) || defined(__PCTOOL__) + if (debug_wps && wps_verbose_level) + { + dump_wps_tokens(data); + print_wps_strings(data); + print_line_info(data); + } +#endif /* SIMULATOR */ + + if (data->num_tokens >= WPS_MAX_TOKENS - 1) { + DEBUGF("Warning: Max number of tokens was reached (%d)\n", + WPS_MAX_TOKENS - 1); + } + + if (fail != PARSE_OK) + { + char buf[64]; + + DEBUGF("ERR: Failed parsing on line %d : ", line); + switch (fail) + { + case PARSE_OK: + break; + + case PARSE_FAIL_UNCLOSED_COND: + DEBUGF("ERR: Unclosed conditional"); + break; + + case PARSE_FAIL_INVALID_CHAR: + DEBUGF("ERR: Unexpected conditional char after token %d: \"%s\"", + data->num_tokens-1, + get_token_desc(&data->tokens[data->num_tokens-1], data, + buf, sizeof(buf)) + ); + break; + + case PARSE_FAIL_COND_SYNTAX_ERROR: + DEBUGF("ERR: Conditional syntax error after token %d: \"%s\"", + data->num_tokens-1, + get_token_desc(&data->tokens[data->num_tokens-1], data, + buf, sizeof(buf)) + ); + break; + + case PARSE_FAIL_COND_INVALID_PARAM: + DEBUGF("ERR: Invalid parameter list for token %d: \"%s\"", + data->num_tokens, + get_token_desc(&data->tokens[data->num_tokens], data, + buf, sizeof(buf)) + ); + break; + + case PARSE_FAIL_LIMITS_EXCEEDED: + DEBUGF("ERR: Limits exceeded"); + break; + } + DEBUGF("\n"); + } +} + +#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 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002-2007 Björn Stenberg + * Copyright (C) 2007-2008 Nicolas Pennequin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "font.h" +#include +#include +#include +#include "system.h" +#include "settings.h" +#include "settings_list.h" +#include "rbunicode.h" +#include "rtc.h" +#include "audio.h" +#include "status.h" +#include "power.h" +#include "powermgmt.h" +#include "sound.h" +#include "debug.h" +#ifdef HAVE_LCD_CHARCELLS +#include "hwcompat.h" +#endif +#include "abrepeat.h" +#include "mp3_playback.h" +#include "lang.h" +#include "misc.h" +#include "splash.h" +#include "scrollbar.h" +#include "led.h" +#include "lcd.h" +#ifdef HAVE_LCD_BITMAP +#include "peakmeter.h" +/* Image stuff */ +#include "bmp.h" +#include "albumart.h" +#endif +#include "dsp.h" +#include "action.h" +#include "cuesheet.h" +#include "playlist.h" +#if CONFIG_CODEC == SWCODEC +#include "playback.h" +#endif +#include "backdrop.h" +#include "viewport.h" + + +#include "wps_internals.h" +#include "wps_engine.h" + +bool gui_wps_display(struct gui_wps *gwps) +{ + struct screen *display = gwps->display; + struct wps_data *data = gwps->data; + int screen = display->screen_type; + + /* Update the values in the first (default) viewport - in case the user + has modified the statusbar or colour settings */ +#if LCD_DEPTH > 1 + if (display->depth > 1) + { + data->viewports[0].vp.fg_pattern = display->get_foreground(); + data->viewports[0].vp.bg_pattern = display->get_background(); + } +#endif + display->clear_display(); + if (!data->wps_loaded) { + if ( !data->num_tokens ) { + /* set the default wps for the main-screen */ + if(screen == SCREEN_MAIN) + { +#if LCD_DEPTH > 1 + unload_wps_backdrop(); +#endif + wps_data_load(data, + display, +#ifdef HAVE_LCD_BITMAP + "%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 %?iv<(id3v%iv)|(no id3)>\n" + "%pb\n" + "%pm\n", false); +#else + "%s%pp/%pe: %?it<%it|%fn> - %?ia<%ia|%d2> - %?id<%id|%d1>\n" + "%pc%?ps<*|/>%pt\n", false); +#endif + } +#ifdef HAVE_REMOTE_LCD + /* set the default wps for the remote-screen */ + else if(screen == SCREEN_REMOTE) + { +#if LCD_REMOTE_DEPTH > 1 + unload_remote_wps_backdrop(); +#endif + wps_data_load(data, + display, + "%s%?ia<%ia|%?d2<%d2|(root)>>\n" + "%s%?it<%?in<%in. |>%it|%fn>\n" + "%al%pc/%pt%ar[%pp:%pe]\n" + "%fbkBit %?fv %?iv<(id3v%iv)|(no id3)>\n" + "%pb\n", false); + } +#endif + } + } + else + { +#if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1 + if (screen == SCREEN_REMOTE) + show_remote_wps_backdrop(); + else if (screen == SCREEN_MAIN) +#endif +#if LCD_DEPTH > 1 + show_wps_backdrop(); +#endif + } + return gui_wps_redraw(gwps, 0, WPS_REFRESH_ALL); +} + +bool gui_wps_update(struct gui_wps *gwps) +{ + struct mp3entry *id3 = gwps->state->id3; + bool retval; + bool cuesheet_update = (id3 != NULL ? cuesheet_subtrack_changed(id3) : false); + gwps->state->do_full_update = cuesheet_update || gwps->state->do_full_update; + retval = gui_wps_redraw(gwps, 0, + gwps->state->do_full_update ? + WPS_REFRESH_ALL : WPS_REFRESH_NON_STATIC); + return retval; +} + + +#ifdef HAVE_LCD_BITMAP + +static void draw_progressbar(struct gui_wps *gwps, + struct wps_viewport *wps_vp) + { + struct screen *display = gwps->display; + struct wps_state *state = gwps->state; + struct progressbar *pb = wps_vp->pb; + int y = pb->y; + + if (y < 0) + { + int line_height = font_get(wps_vp->vp.font)->height; + /* center the pb in the line, but only if the line is higher than the pb */ + int center = (line_height-pb->height)/2; + /* if Y was not set calculate by font height,Y is -line_number-1 */ + y = (-y -1)*line_height + (0 > center ? 0 : center); + } + + if (pb->have_bitmap_pb) + gui_bitmap_scrollbar_draw(display, pb->bm, + pb->x, y, pb->width, pb->bm.height, + state->id3->length ? state->id3->length : 1, 0, + state->id3->length ? state->id3->elapsed + + state->ff_rewind_count : 0, + HORIZONTAL); + else + gui_scrollbar_draw(display, pb->x, y, pb->width, pb->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() && state->id3->length != 0 ) + ab_draw_markers(display, state->id3->length, + pb->x, pb->x + pb->width, y, pb->height); +#endif + + if (state->id3->cuesheet) + cue_draw_markers(display, state->id3->cuesheet, state->id3->length, + pb->x, pb->x + pb->width, y+1, pb->height-2); +} + +/* 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].bm.width, data->img[n].subimage_height); + gwps->display->set_drawmode(DRMODE_SOLID); +} + +static void wps_draw_image(struct gui_wps *gwps, int n, int subimage) +{ + struct screen *display = gwps->display; + struct wps_data *data = gwps->data; + if(data->img[n].always_display) + display->set_drawmode(DRMODE_FG); + else + display->set_drawmode(DRMODE_SOLID); + +#if LCD_DEPTH > 1 + if(data->img[n].bm.format == FORMAT_MONO) { +#endif + display->mono_bitmap_part(data->img[n].bm.data, + 0, data->img[n].subimage_height * subimage, + data->img[n].bm.width, data->img[n].x, + data->img[n].y, data->img[n].bm.width, + data->img[n].subimage_height); +#if LCD_DEPTH > 1 + } else { + display->transparent_bitmap_part((fb_data *)data->img[n].bm.data, + 0, data->img[n].subimage_height * subimage, + data->img[n].bm.width, data->img[n].x, + data->img[n].y, data->img[n].bm.width, + data->img[n].subimage_height); + } +#endif +} + +static void wps_display_images(struct gui_wps *gwps, struct viewport* vp) +{ + if(!gwps || !gwps->data || !gwps->display) + return; + + int n; + struct wps_data *data = gwps->data; + struct screen *display = gwps->display; + + for (n = 0; n < MAX_IMAGES; n++) + { + if (data->img[n].loaded) + { + if (data->img[n].display >= 0) + { + wps_draw_image(gwps, n, data->img[n].display); + } else if (data->img[n].always_display && data->img[n].vp == vp) + { + wps_draw_image(gwps, n, 0); + } + } + } + display->set_drawmode(DRMODE_SOLID); +} + +#else /* HAVE_LCD_CHARCELL */ + +static bool draw_player_progress(struct gui_wps *gwps) +{ + struct wps_state *state = gwps->state; + struct screen *display = gwps->display; + unsigned char progress_pattern[7]; + int pos = 0; + int i; + + if (!state->id3) + return false; + + if (state->id3->length) + pos = 36 * (state->id3->elapsed + state->ff_rewind_count) + / state->id3->length; + + for (i = 0; i < 7; i++, pos -= 5) + { + if (pos <= 0) + progress_pattern[i] = 0x1fu; + else if (pos >= 5) + progress_pattern[i] = 0x00u; + else + progress_pattern[i] = 0x1fu >> pos; + } + + display->define_pattern(gwps->data->wps_progress_pat[0], progress_pattern); + return true; +} + +static void draw_player_fullbar(struct gui_wps *gwps, char* buf, int buf_size) +{ + static const unsigned char numbers[10][4] = { + {0x0e, 0x0a, 0x0a, 0x0e}, /* 0 */ + {0x04, 0x0c, 0x04, 0x04}, /* 1 */ + {0x0e, 0x02, 0x04, 0x0e}, /* 2 */ + {0x0e, 0x02, 0x06, 0x0e}, /* 3 */ + {0x08, 0x0c, 0x0e, 0x04}, /* 4 */ + {0x0e, 0x0c, 0x02, 0x0c}, /* 5 */ + {0x0e, 0x08, 0x0e, 0x0e}, /* 6 */ + {0x0e, 0x02, 0x04, 0x08}, /* 7 */ + {0x0e, 0x0e, 0x0a, 0x0e}, /* 8 */ + {0x0e, 0x0e, 0x02, 0x0e}, /* 9 */ + }; + + struct wps_state *state = gwps->state; + struct screen *display = gwps->display; + struct wps_data *data = gwps->data; + unsigned char progress_pattern[7]; + char timestr[10]; + int time; + int time_idx = 0; + int pos = 0; + int pat_idx = 1; + int digit, i, j; + bool softchar; + + if (!state->id3 || buf_size < 34) /* worst case: 11x UTF-8 char + \0 */ + return; + + time = state->id3->elapsed + state->ff_rewind_count; + if (state->id3->length) + pos = 55 * time / state->id3->length; + + memset(timestr, 0, sizeof(timestr)); + format_time(timestr, sizeof(timestr)-2, time); + timestr[strlen(timestr)] = ':'; /* always safe */ + + for (i = 0; i < 11; i++, pos -= 5) + { + softchar = false; + memset(progress_pattern, 0, sizeof(progress_pattern)); + + if ((digit = timestr[time_idx])) + { + softchar = true; + digit -= '0'; + + if (timestr[time_idx + 1] == ':') /* ones, left aligned */ + { + memcpy(progress_pattern, numbers[digit], 4); + time_idx += 2; + } + else /* tens, shifted right */ + { + for (j = 0; j < 4; j++) + progress_pattern[j] = numbers[digit][j] >> 1; + + if (time_idx > 0) /* not the first group, add colon in front */ + { + progress_pattern[1] |= 0x10u; + progress_pattern[3] |= 0x10u; + } + time_idx++; + } + + if (pos >= 5) + progress_pattern[5] = progress_pattern[6] = 0x1fu; + } + + if (pos > 0 && pos < 5) + { + softchar = true; + progress_pattern[5] = progress_pattern[6] = (~0x1fu >> pos) & 0x1fu; + } + + if (softchar && pat_idx < 8) + { + display->define_pattern(data->wps_progress_pat[pat_idx], + progress_pattern); + buf = utf8encode(data->wps_progress_pat[pat_idx], buf); + pat_idx++; + } + else if (pos <= 0) + buf = utf8encode(' ', buf); + else + buf = utf8encode(0xe115, buf); /* 2/7 _ */ + } + *buf = '\0'; +} + +#endif /* HAVE_LCD_CHARCELL */ + +/* Return the index to the end token for the conditional token at index. + The conditional token can be either a start token or a separator + (i.e. option) token. +*/ +static int find_conditional_end(struct wps_data *data, int index) +{ + int ret = index; + while (data->tokens[ret].type != WPS_TOKEN_CONDITIONAL_END) + ret = data->tokens[ret].value.i; + + /* ret now is the index to the end token for the conditional. */ + return ret; +} + +/* Evaluate the conditional that is at *token_index and return whether a skip + has ocurred. *token_index is updated with the new position. +*/ +static bool evaluate_conditional(struct gui_wps *gwps, int *token_index) +{ + if (!gwps) + return false; + + struct wps_data *data = gwps->data; + + int i, cond_end; + int cond_index = *token_index; + char result[128]; + const char *value; + unsigned char num_options = data->tokens[cond_index].value.i & 0xFF; + unsigned char prev_val = (data->tokens[cond_index].value.i & 0xFF00) >> 8; + + /* treat ?xx constructs as if they had 2 options. */ + if (num_options < 2) + num_options = 2; + + int intval = num_options; + /* get_token_value needs to know the number of options in the enum */ + value = get_token_value(gwps, &data->tokens[cond_index + 1], + result, sizeof(result), &intval); + + /* intval is now the number of the enum option we want to read, + starting from 1. If intval is -1, we check if value is empty. */ + if (intval == -1) + intval = (value && *value) ? 1 : num_options; + else if (intval > num_options || intval < 1) + intval = num_options; + + data->tokens[cond_index].value.i = (intval << 8) + num_options; + + /* skip to the appropriate enum case */ + int next = cond_index + 2; + for (i = 1; i < intval; i++) + { + next = data->tokens[next].value.i; + } + *token_index = next; + + if (prev_val == intval) + { + /* Same conditional case as previously. Return without clearing the + pictures */ + return false; + } + + cond_end = find_conditional_end(data, cond_index + 2); + for (i = cond_index + 3; i < cond_end; i++) + { +#ifdef HAVE_LCD_BITMAP + /* clear all pictures in the conditional and nested ones */ + if (data->tokens[i].type == WPS_TOKEN_IMAGE_PRELOAD_DISPLAY) + clear_image_pos(gwps, data->tokens[i].value.i & 0xFF); +#endif +#ifdef HAVE_ALBUMART + if (data->tokens[i].type == WPS_TOKEN_ALBUMART_DISPLAY) + draw_album_art(gwps, audio_current_aa_hid(), true); +#endif + } + + return true; +} + +/* Read a (sub)line to the given alignment format buffer. + linebuf is the buffer where the data is actually stored. + align is the alignment format that'll be used to display the text. + The return value indicates whether the line needs to be updated. +*/ +static bool get_line(struct gui_wps *gwps, + int line, int subline, + struct align_pos *align, + char *linebuf, + int linebuf_size) +{ + struct wps_data *data = gwps->data; + + char temp_buf[128]; + char *buf = linebuf; /* will always point to the writing position */ + char *linebuf_end = linebuf + linebuf_size - 1; + int i, last_token_idx; + bool update = false; + + /* alignment-related variables */ + int cur_align; + char* cur_align_start; + cur_align_start = buf; + cur_align = WPS_ALIGN_LEFT; + align->left = NULL; + align->center = NULL; + align->right = NULL; + + /* Process all tokens of the desired subline */ + last_token_idx = wps_last_token_index(data, line, subline); + for (i = wps_first_token_index(data, line, subline); + i <= last_token_idx; i++) + { + switch(data->tokens[i].type) + { + case WPS_TOKEN_CONDITIONAL: + /* place ourselves in the right conditional case */ + update |= evaluate_conditional(gwps, &i); + break; + + case WPS_TOKEN_CONDITIONAL_OPTION: + /* we've finished in the curent conditional case, + skip to the end of the conditional structure */ + i = find_conditional_end(data, i); + break; + +#ifdef HAVE_LCD_BITMAP + case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY: + { + struct gui_img *img = data->img; + int n = data->tokens[i].value.i & 0xFF; + int subimage = data->tokens[i].value.i >> 8; + + if (n >= 0 && n < MAX_IMAGES && img[n].loaded) + img[n].display = subimage; + break; + } +#endif + + case WPS_TOKEN_ALIGN_LEFT: + case WPS_TOKEN_ALIGN_CENTER: + case WPS_TOKEN_ALIGN_RIGHT: + /* 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 (data->tokens[i].type) + { + case WPS_TOKEN_ALIGN_LEFT: + cur_align = WPS_ALIGN_LEFT; + break; + case WPS_TOKEN_ALIGN_CENTER: + cur_align = WPS_ALIGN_CENTER; + break; + case WPS_TOKEN_ALIGN_RIGHT: + cur_align = WPS_ALIGN_RIGHT; + break; + default: + break; + } + *buf++ = 0; + cur_align_start = buf; + break; + case WPS_VIEWPORT_ENABLE: + { + char label = data->tokens[i].value.i; + int j; + char temp = VP_DRAW_HIDEABLE; + for(j=0;jnum_viewports;j++) + { + temp = VP_DRAW_HIDEABLE; + if ((data->viewports[j].hidden_flags&VP_DRAW_HIDEABLE) && + (data->viewports[j].label == label)) + { + if (data->viewports[j].hidden_flags&VP_DRAW_WASHIDDEN) + temp |= VP_DRAW_WASHIDDEN; + data->viewports[j].hidden_flags = temp; + } + } + } + break; + default: + { + /* get the value of the tag and copy it to the buffer */ + const char *value = get_token_value(gwps, &data->tokens[i], + temp_buf, sizeof(temp_buf), NULL); + if (value) + { + update = true; + while (*value && (buf < linebuf_end)) + *buf++ = *value++; + } + break; + } + } + } + + /* close the current alignment */ + 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; + } + + return update; +} + +static void get_subline_timeout(struct gui_wps *gwps, int line, int subline) +{ + struct wps_data *data = gwps->data; + int i; + int subline_idx = wps_subline_index(data, line, subline); + int last_token_idx = wps_last_token_index(data, line, subline); + + data->sublines[subline_idx].time_mult = DEFAULT_SUBLINE_TIME_MULTIPLIER; + + for (i = wps_first_token_index(data, line, subline); + i <= last_token_idx; i++) + { + switch(data->tokens[i].type) + { + case WPS_TOKEN_CONDITIONAL: + /* place ourselves in the right conditional case */ + evaluate_conditional(gwps, &i); + break; + + case WPS_TOKEN_CONDITIONAL_OPTION: + /* we've finished in the curent conditional case, + skip to the end of the conditional structure */ + i = find_conditional_end(data, i); + break; + + case WPS_TOKEN_SUBLINE_TIMEOUT: + data->sublines[subline_idx].time_mult = data->tokens[i].value.i; + break; + + default: + break; + } + } +} + +/* Calculates which subline should be displayed for the specified line + Returns true iff the subline must be refreshed */ +static bool update_curr_subline(struct gui_wps *gwps, int line) +{ + struct wps_data *data = gwps->data; + + int search, search_start, num_sublines; + bool reset_subline; + bool new_subline_refresh; + bool only_one_subline; + + num_sublines = data->lines[line].num_sublines; + reset_subline = (data->lines[line].curr_subline == SUBLINE_RESET); + new_subline_refresh = false; + only_one_subline = false; + + /* if time to advance to next sub-line */ + if (TIME_AFTER(current_tick, data->lines[line].subline_expire_time - 1) || + reset_subline) + { + /* search all sublines until the next subline with time > 0 + is found or we get back to the subline we started with */ + if (reset_subline) + search_start = 0; + else + search_start = data->lines[line].curr_subline; + + for (search = 0; search < num_sublines; search++) + { + data->lines[line].curr_subline++; + + /* wrap around if beyond last defined subline or WPS_MAX_SUBLINES */ + if (data->lines[line].curr_subline == num_sublines) + { + if (data->lines[line].curr_subline == 1) + only_one_subline = true; + data->lines[line].curr_subline = 0; + } + + /* if back where we started after search or + only one subline is defined on the line */ + if (((search > 0) && + (data->lines[line].curr_subline == search_start)) || + only_one_subline) + { + /* no other subline with a time > 0 exists */ + data->lines[line].subline_expire_time = (reset_subline ? + current_tick : + data->lines[line].subline_expire_time) + 100 * HZ; + break; + } + else + { + /* get initial time multiplier for this subline */ + get_subline_timeout(gwps, line, data->lines[line].curr_subline); + + int subline_idx = wps_subline_index(data, line, + data->lines[line].curr_subline); + + /* only use this subline if subline time > 0 */ + if (data->sublines[subline_idx].time_mult > 0) + { + new_subline_refresh = true; + data->lines[line].subline_expire_time = (reset_subline ? + current_tick : data->lines[line].subline_expire_time) + + TIMEOUT_UNIT*data->sublines[subline_idx].time_mult; + break; + } + } + } + } + + return new_subline_refresh; +} + +/* Display a line appropriately according to its alignment format. + format_align contains the text, separated between left, center and right. + line is the index of the line on the screen. + scroll indicates whether the line is a scrolling one or not. +*/ +static void write_line(struct screen *display, + struct align_pos *format_align, + int line, + bool scroll) +{ + int left_width = 0, left_xpos; + int center_width = 0, center_xpos; + int right_width = 0, right_xpos; + int ypos; + int space_width; + int string_height; + int scroll_width; + + /* calculate different string sizes and positions */ + display->getstringsize((unsigned char *)" ", &space_width, &string_height); + if (format_align->left != 0) { + display->getstringsize((unsigned char *)format_align->left, + &left_width, &string_height); + } + + if (format_align->right != 0) { + display->getstringsize((unsigned char *)format_align->right, + &right_width, &string_height); + } + + if (format_align->center != 0) { + display->getstringsize((unsigned char *)format_align->center, + ¢er_width, &string_height); + } + + left_xpos = 0; + right_xpos = (display->getwidth() - right_width); + center_xpos = (display->getwidth() + left_xpos - center_width) / 2; + + scroll_width = display->getwidth() - left_xpos; + + /* 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 */ + *(--format_align->center) = ' '; + /* calculate the new width and position of the merged string */ + left_width = left_width + space_width + center_width; + /* 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 */ + format_align->left = format_align->center; + /* calculate the new width and position of the string */ + left_width = center_width; + /* 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 */ + *(--format_align->right) = ' '; + /* move the center string to the right after merge */ + format_align->right = format_align->center; + /* calculate the new width and position of the merged string */ + right_width = center_width + space_width + right_width; + right_xpos = (display->getwidth() - 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 */ + format_align->right = format_align->center; + /* calculate the new width and position of the string */ + right_width = center_width; + right_xpos = (display->getwidth() - 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 */ + *(--format_align->right) = ' '; + /* calculate the new width and position of the string */ + left_width = left_width + space_width + right_width; + /* 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_width > right_xpos)) { + /* move the right string to the left string */ + format_align->left = format_align->right; + /* calculate the new width and position of the string */ + left_width = right_width; + /* there is no right string anymore */ + right_width = 0; + } + + ypos = (line * string_height); + + + if (scroll && ((left_width > scroll_width) || + (center_width > scroll_width) || + (right_width > scroll_width))) + { + display->puts_scroll(0, line, + (unsigned char *)format_align->left); + } + else + { +#ifdef HAVE_LCD_BITMAP + /* clear the line first */ + display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID); + display->fillrect(left_xpos, ypos, display->getwidth(), string_height); + display->set_drawmode(DRMODE_SOLID); +#endif + + /* Nasty hack: we output an empty scrolling string, + which will reset the scroller for that line */ + display->puts_scroll(0, line, (unsigned char *)""); + + /* print aligned strings */ + if (left_width != 0) + { + display->putsxy(left_xpos, ypos, + (unsigned char *)format_align->left); + } + if (center_width != 0) + { + display->putsxy(center_xpos, ypos, + (unsigned char *)format_align->center); + } + if (right_width != 0) + { + display->putsxy(right_xpos, ypos, + (unsigned char *)format_align->right); + } + } +} + +bool gui_wps_redraw(struct gui_wps *gwps, + int ffwd_offset, + unsigned refresh_mode) +{ + struct wps_data *data = gwps->data; + struct screen *display = gwps->display; + struct wps_state *state = gwps->state; + + if (!data || !state || !display) + return false; + + struct mp3entry *id3 = state->id3; + + if (!id3) + return false; + + int v, line, i, subline_idx; + unsigned flags; + char linebuf[MAX_PATH]; + + struct align_pos align; + align.left = NULL; + align.center = NULL; + align.right = NULL; + + bool update_line, new_subline_refresh; + +#ifdef HAVE_LCD_BITMAP + + /* 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; + +#endif + + /* reset to first subline if refresh all flag is set */ + if (refresh_mode == WPS_REFRESH_ALL) + { + display->set_viewport(&data->viewports[0].vp); + display->clear_viewport(); + + for (i = 0; i <= data->num_lines; i++) + { + data->lines[i].curr_subline = 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 + + state->ff_rewind_count = ffwd_offset; + + /* disable any viewports which are conditionally displayed */ + for (v = 0; v < data->num_viewports; v++) + { + if (data->viewports[v].hidden_flags&VP_DRAW_HIDEABLE) + { + if (data->viewports[v].hidden_flags&VP_DRAW_HIDDEN) + data->viewports[v].hidden_flags |= VP_DRAW_WASHIDDEN; + else + data->viewports[v].hidden_flags |= VP_DRAW_HIDDEN; + } + } + for (v = 0; v < data->num_viewports; v++) + { + struct wps_viewport *wps_vp = &(data->viewports[v]); + unsigned vp_refresh_mode = refresh_mode; + display->set_viewport(&wps_vp->vp); + +#ifdef HAVE_LCD_BITMAP + /* Set images to not to be displayed */ + for (i = 0; i < MAX_IMAGES; i++) + { + data->img[i].display = -1; + } +#endif + /* dont redraw the viewport if its disabled */ + if ((wps_vp->hidden_flags&VP_DRAW_HIDDEN)) + { + if (!(wps_vp->hidden_flags&VP_DRAW_WASHIDDEN)) + display->scroll_stop(&wps_vp->vp); + wps_vp->hidden_flags |= VP_DRAW_WASHIDDEN; + continue; + } + else if (((wps_vp->hidden_flags& + (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE)) + == (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE))) + { + vp_refresh_mode = WPS_REFRESH_ALL; + wps_vp->hidden_flags = VP_DRAW_HIDEABLE; + } + if (vp_refresh_mode == WPS_REFRESH_ALL) + { + display->clear_viewport(); + } + + for (line = wps_vp->first_line; + line <= wps_vp->last_line; line++) + { + memset(linebuf, 0, sizeof(linebuf)); + update_line = false; + + /* get current subline for the line */ + new_subline_refresh = update_curr_subline(gwps, line); + + subline_idx = wps_subline_index(data, line, + data->lines[line].curr_subline); + flags = data->sublines[subline_idx].line_type; + + if (vp_refresh_mode == WPS_REFRESH_ALL || (flags & vp_refresh_mode) + || new_subline_refresh) + { + /* get_line tells us if we need to update the line */ + update_line = get_line(gwps, line, data->lines[line].curr_subline, + &align, linebuf, sizeof(linebuf)); + } +#ifdef HAVE_LCD_BITMAP + /* peakmeter */ + if (flags & vp_refresh_mode & WPS_REFRESH_PEAK_METER) + { + /* the peakmeter should be alone on its line */ + update_line = false; + + int h = font_get(wps_vp->vp.font)->height; + int peak_meter_y = (line - wps_vp->first_line)* h; + + /* 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 <= display->getheight()) { + /* found a line with a peak meter -> remember that we must + enable it later */ + enable_pm = true; + peak_meter_enabled = true; + peak_meter_screen(gwps->display, 0, peak_meter_y, + MIN(h, display->getheight() - peak_meter_y)); + } + else + { + peak_meter_enabled = false; + } + } + +#else /* HAVE_LCD_CHARCELL */ + + /* progressbar */ + if (flags & vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS) + { + if (data->full_line_progressbar) + draw_player_fullbar(gwps, linebuf, sizeof(linebuf)); + else + draw_player_progress(gwps); + } +#endif + + if (update_line && + /* conditionals clear the line which means if the %Vd is put into the default + viewport there will be a blank line. + To get around this we dont allow any actual drawing to happen in the + deault vp if other vp's are defined */ + ((data->num_viewports>1 && v!=0) || data->num_viewports == 1)) + { + if (flags & WPS_REFRESH_SCROLL) + { + /* if the line is a scrolling one we don't want to update + too often, so that it has the time to scroll */ + if ((vp_refresh_mode & WPS_REFRESH_SCROLL) || new_subline_refresh) + write_line(display, &align, line - wps_vp->first_line, true); + } + else + write_line(display, &align, line - wps_vp->first_line, false); + } + } + +#ifdef HAVE_LCD_BITMAP + /* progressbar */ + if (vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS) + { + if (wps_vp->pb) + { + draw_progressbar(gwps, wps_vp); + } + } + /* Now display any images in this viewport */ + wps_display_images(gwps, &wps_vp->vp); +#endif + } + +#ifdef HAVE_LCD_BITMAP + data->peak_meter_enabled = enable_pm; +#endif + + if (refresh_mode & WPS_REFRESH_STATUSBAR) + { + gwps_draw_statusbars(); + } + /* Restore the default viewport */ + display->set_viewport(NULL); + + display->update(); + + return true; +} 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 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: gwps.h 22003 2009-07-22 22:10:25Z kugel $ + * + * Copyright (C) 2007 Nicolas Pennequin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + + /** Use this for stuff which external code needs to include **/ + +#ifndef _WPS_ENGINE_H +#define _WPS_ENGINE_H +#include +#include "wps_internals.h" /* TODO: remove this line.. shoudlnt be needed */ + + +#ifdef HAVE_TOUCHSCREEN +int wps_get_touchaction(struct wps_data *data); +#endif + +#ifdef HAVE_ALBUMART +/* gives back if WPS contains an albumart tag */ +bool gui_sync_wps_uses_albumart(void); +#endif + +/* setup and display a WPS for the first time */ +bool gui_wps_display(struct gui_wps *gwps); +/* do a requested redraw */ +bool gui_wps_redraw(struct gui_wps *gwps, + int ffwd_offset, + unsigned refresh_mode); +/* do a partial redraw, or full if required, also do any housekeeping + * which might be needed */ +bool gui_wps_update(struct gui_wps *gwps); + +#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 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2007 Nicolas Pennequin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + + /* This stuff is for the wps engine only.. anyone caught using this outside + * of apps/gui/wps_engine will be shot on site! */ + +#ifndef _WPS_ENGINE_INTERNALS_ +#define _WPS_ENGINE_INTERNALS_ +/* Timeout unit expressed in HZ. In WPS, all timeouts are given in seconds + (possibly with a decimal fraction) but stored as integer values. + E.g. 2.5 is stored as 25. This means 25 tenth of a second, i.e. 25 units. +*/ +#define TIMEOUT_UNIT (HZ/10) /* I.e. 0.1 sec */ +#define DEFAULT_SUBLINE_TIME_MULTIPLIER 20 /* In TIMEOUT_UNIT's */ + + + + +/* TODO: sort this mess out */ + +#include "screen_access.h" +#include "statusbar.h" +#include "metadata.h" + +/* constants used in line_type and as refresh_mode for wps_refresh */ +#define WPS_REFRESH_STATIC (1u<<0) /* line doesn't change over time */ +#define WPS_REFRESH_DYNAMIC (1u<<1) /* line may change (e.g. time flag) */ +#define WPS_REFRESH_SCROLL (1u<<2) /* line scrolls */ +#define WPS_REFRESH_PLAYER_PROGRESS (1u<<3) /* line contains a progress bar */ +#define WPS_REFRESH_PEAK_METER (1u<<4) /* line contains a peak meter */ +#define WPS_REFRESH_STATUSBAR (1u<<5) /* refresh statusbar */ +#define WPS_REFRESH_ALL (0xffffffffu) /* to refresh all line types */ + +/* to refresh only those lines that change over time */ +#define WPS_REFRESH_NON_STATIC (WPS_REFRESH_DYNAMIC| \ + WPS_REFRESH_PLAYER_PROGRESS| \ + WPS_REFRESH_PEAK_METER) +/* alignments */ +#define WPS_ALIGN_RIGHT 32 +#define WPS_ALIGN_CENTER 64 +#define WPS_ALIGN_LEFT 128 + +#ifdef HAVE_ALBUMART + +/* albumart definitions */ +#define WPS_ALBUMART_NONE 0 /* WPS does not contain AA tag */ +#define WPS_ALBUMART_CHECK 1 /* WPS contains AA conditional tag */ +#define WPS_ALBUMART_LOAD 2 /* WPS contains AA tag */ + +#define WPS_ALBUMART_ALIGN_RIGHT 1 /* x align: right */ +#define WPS_ALBUMART_ALIGN_CENTER 2 /* x/y align: center */ +#define WPS_ALBUMART_ALIGN_LEFT 4 /* x align: left */ +#define WPS_ALBUMART_ALIGN_TOP 1 /* y align: top */ +#define WPS_ALBUMART_ALIGN_BOTTOM 4 /* y align: bottom */ + +#endif /* HAVE_ALBUMART */ + +/* wps_data*/ + +#ifdef HAVE_LCD_BITMAP +struct gui_img { + struct bitmap bm; + struct viewport* vp; /* The viewport to display this image in */ + short int x; /* x-pos */ + short int y; /* y-pos */ + short int num_subimages; /* number of sub-images */ + short int subimage_height; /* height of each sub-image */ + short int display; /* -1 for no display, 0..n to display a subimage */ + bool loaded; /* load state */ + bool always_display; /* not using the preload/display mechanism */ +}; + +struct progressbar { + /* regular pb */ + short x; + /* >=0: explicitly set in the tag -> y-coord within the viewport + <0 : not set in the tag -> negated 1-based line number within + the viewport. y-coord will be computed based on the font height */ + short y; + short width; + short height; + /*progressbar image*/ + struct bitmap bm; + bool have_bitmap_pb; +}; +#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 MAX_PROGRESSBARS 3 + +/* The image buffer is big enough to store one full-screen native bitmap, + plus two full-screen mono bitmaps. */ + +#define IMG_BUFSIZE ((LCD_HEIGHT*LCD_WIDTH*LCD_DEPTH/8) \ + + (2*LCD_HEIGHT*LCD_WIDTH/8)) + +#define WPS_MAX_VIEWPORTS 24 +#define WPS_MAX_LINES ((LCD_HEIGHT/5+1) * 2) +#define WPS_MAX_SUBLINES (WPS_MAX_LINES*3) +#define WPS_MAX_TOKENS 1024 +#define WPS_MAX_STRINGS 128 +#define STRING_BUFFER_SIZE 1024 +#define WPS_MAX_COND_LEVEL 10 + +#else + +#define WPS_MAX_VIEWPORTS 2 +#define WPS_MAX_LINES 2 +#define WPS_MAX_SUBLINES 12 +#define WPS_MAX_TOKENS 64 +#define WPS_MAX_STRINGS 32 +#define STRING_BUFFER_SIZE 64 +#define WPS_MAX_COND_LEVEL 5 + +#endif + +#define SUBLINE_RESET -1 + +enum wps_parse_error { + PARSE_OK, + PARSE_FAIL_UNCLOSED_COND, + PARSE_FAIL_INVALID_CHAR, + PARSE_FAIL_COND_SYNTAX_ERROR, + PARSE_FAIL_COND_INVALID_PARAM, + PARSE_FAIL_LIMITS_EXCEEDED, +}; + +enum wps_token_type { + WPS_NO_TOKEN, /* for WPS tags we don't want to save as tokens */ + WPS_TOKEN_UNKNOWN, + + /* Markers */ + WPS_TOKEN_CHARACTER, + WPS_TOKEN_STRING, + + /* Alignment */ + WPS_TOKEN_ALIGN_LEFT, + WPS_TOKEN_ALIGN_CENTER, + WPS_TOKEN_ALIGN_RIGHT, + + /* Sublines */ + WPS_TOKEN_SUBLINE_TIMEOUT, + + /* Battery */ + WPS_TOKEN_BATTERY_PERCENT, + WPS_TOKEN_BATTERY_VOLTS, + WPS_TOKEN_BATTERY_TIME, + WPS_TOKEN_BATTERY_CHARGER_CONNECTED, + WPS_TOKEN_BATTERY_CHARGING, + WPS_TOKEN_BATTERY_SLEEPTIME, + + /* Sound */ +#if (CONFIG_CODEC != MAS3507D) + WPS_TOKEN_SOUND_PITCH, +#endif +#if (CONFIG_CODEC == SWCODEC) + WPS_TOKEN_REPLAYGAIN, + WPS_TOKEN_CROSSFADE, +#endif + + /* Time */ + + WPS_TOKEN_RTC_PRESENT, + + /* The begin/end values allow us to know if a token is an RTC one. + New RTC tokens should be added between the markers. */ + + WPS_TOKENS_RTC_BEGIN, /* just the start marker, not an actual token */ + + WPS_TOKEN_RTC_DAY_OF_MONTH, + WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED, + WPS_TOKEN_RTC_12HOUR_CFG, + WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, + WPS_TOKEN_RTC_HOUR_24, + WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, + WPS_TOKEN_RTC_HOUR_12, + WPS_TOKEN_RTC_MONTH, + WPS_TOKEN_RTC_MINUTE, + WPS_TOKEN_RTC_SECOND, + WPS_TOKEN_RTC_YEAR_2_DIGITS, + WPS_TOKEN_RTC_YEAR_4_DIGITS, + WPS_TOKEN_RTC_AM_PM_UPPER, + WPS_TOKEN_RTC_AM_PM_LOWER, + WPS_TOKEN_RTC_WEEKDAY_NAME, + WPS_TOKEN_RTC_MONTH_NAME, + WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, + WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, + + WPS_TOKENS_RTC_END, /* just the end marker, not an actual token */ + + /* Conditional */ + WPS_TOKEN_CONDITIONAL, + WPS_TOKEN_CONDITIONAL_START, + WPS_TOKEN_CONDITIONAL_OPTION, + WPS_TOKEN_CONDITIONAL_END, + + /* Database */ +#ifdef HAVE_TAGCACHE + WPS_TOKEN_DATABASE_PLAYCOUNT, + WPS_TOKEN_DATABASE_RATING, + WPS_TOKEN_DATABASE_AUTOSCORE, +#endif + + /* File */ + WPS_TOKEN_FILE_BITRATE, + WPS_TOKEN_FILE_CODEC, + WPS_TOKEN_FILE_FREQUENCY, + WPS_TOKEN_FILE_FREQUENCY_KHZ, + WPS_TOKEN_FILE_NAME, + WPS_TOKEN_FILE_NAME_WITH_EXTENSION, + WPS_TOKEN_FILE_PATH, + WPS_TOKEN_FILE_SIZE, + WPS_TOKEN_FILE_VBR, + WPS_TOKEN_FILE_DIRECTORY, + +#ifdef HAVE_LCD_BITMAP + /* Image */ + WPS_TOKEN_IMAGE_BACKDROP, + WPS_TOKEN_IMAGE_PROGRESS_BAR, + WPS_TOKEN_IMAGE_PRELOAD, + WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, + WPS_TOKEN_IMAGE_DISPLAY, +#endif + +#ifdef HAVE_ALBUMART + /* Albumart */ + WPS_TOKEN_ALBUMART_DISPLAY, + WPS_TOKEN_ALBUMART_FOUND, +#endif + + /* Metadata */ + WPS_TOKEN_METADATA_ARTIST, + WPS_TOKEN_METADATA_COMPOSER, + WPS_TOKEN_METADATA_ALBUM_ARTIST, + WPS_TOKEN_METADATA_GROUPING, + WPS_TOKEN_METADATA_ALBUM, + WPS_TOKEN_METADATA_GENRE, + WPS_TOKEN_METADATA_DISC_NUMBER, + WPS_TOKEN_METADATA_TRACK_NUMBER, + WPS_TOKEN_METADATA_TRACK_TITLE, + WPS_TOKEN_METADATA_VERSION, + WPS_TOKEN_METADATA_YEAR, + WPS_TOKEN_METADATA_COMMENT, + + /* Mode */ + WPS_TOKEN_REPEAT_MODE, + WPS_TOKEN_PLAYBACK_STATUS, + + WPS_TOKEN_MAIN_HOLD, + +#ifdef HAS_REMOTE_BUTTON_HOLD + WPS_TOKEN_REMOTE_HOLD, +#endif + + /* Progressbar */ + WPS_TOKEN_PROGRESSBAR, +#ifdef HAVE_LCD_CHARCELLS + WPS_TOKEN_PLAYER_PROGRESSBAR, +#endif + +#ifdef HAVE_LCD_BITMAP + /* Peakmeter */ + WPS_TOKEN_PEAKMETER, +#endif + + /* Volume level */ + WPS_TOKEN_VOLUME, + + /* Current track */ + WPS_TOKEN_TRACK_ELAPSED_PERCENT, + WPS_TOKEN_TRACK_TIME_ELAPSED, + WPS_TOKEN_TRACK_TIME_REMAINING, + WPS_TOKEN_TRACK_LENGTH, + + /* Playlist */ + WPS_TOKEN_PLAYLIST_ENTRIES, + WPS_TOKEN_PLAYLIST_NAME, + WPS_TOKEN_PLAYLIST_POSITION, + WPS_TOKEN_PLAYLIST_SHUFFLE, + +#if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD) + /* Virtual LED */ + WPS_TOKEN_VLED_HDD, +#endif + + /* Viewport display */ + WPS_VIEWPORT_ENABLE, + + /* buttons */ + WPS_TOKEN_BUTTON_VOLUME, + WPS_TOKEN_LASTTOUCH, + + /* Setting option */ + WPS_TOKEN_SETTING, +}; + +struct wps_token { + unsigned char type; /* enough to store the token type */ + + /* Whether the tag (e.g. track name or the album) refers the + current or the next song (false=current, true=next) */ + bool next; + + union { + char c; + unsigned short i; + } value; +}; + +/* Description of a subline on the WPS */ +struct wps_subline { + + /* Index of the first token for this subline in the token array. + Tokens of this subline end where tokens for the next subline + begin. */ + unsigned short first_token_idx; + + /* Bit or'ed WPS_REFRESH_xxx */ + unsigned char line_type; + + /* How long the subline should be displayed, in 10ths of sec */ + unsigned char time_mult; +}; + +/* Description of a line on the WPS. A line is a set of sublines. + A subline is displayed for a certain amount of time. After that, + the next subline of the line is displayed. And so on. */ +struct wps_line { + + /* Number of sublines in this line */ + signed char num_sublines; + + /* Number (0-based) of the subline within this line currently being displayed */ + signed char curr_subline; + + /* Index of the first subline of this line in the subline array. + Sublines for this line end where sublines for the next line begin. */ + unsigned short first_subline_idx; + + /* When the next subline of this line should be displayed + (absolute time value in ticks) */ + long subline_expire_time; +}; + +#define VP_DRAW_HIDEABLE 0x1 +#define VP_DRAW_HIDDEN 0x2 +#define VP_DRAW_WASHIDDEN 0x4 +struct wps_viewport { + struct viewport vp; /* The LCD viewport struct */ + struct progressbar *pb; + /* Indexes of the first and last lines belonging to this viewport in the + lines[] array */ + int first_line, last_line; + char hidden_flags; + char label; +}; + +#ifdef HAVE_TOUCHSCREEN +struct touchregion { + struct wps_viewport* wvp;/* The viewport this region is in */ + short int x; /* x-pos */ + short int y; /* y-pos */ + short int width; /* width */ + short int height; /* height */ + bool repeat; /* requires the area be held for the action */ + int action; /* action this button will return */ +}; +#define MAX_TOUCHREGIONS 15 +#endif +/* wps_data + this struct holds 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]; + unsigned char* img_buf_ptr; + int img_buf_free; + bool wps_sb_tag; + bool show_sb_on_wps; + + struct progressbar progressbar[MAX_PROGRESSBARS]; + short progressbar_count; + + bool peak_meter_enabled; + +#ifdef HAVE_ALBUMART + /* Album art support */ + unsigned char wps_uses_albumart; /* WPS_ALBUMART_NONE, _CHECK, _LOAD */ + short albumart_x; + short albumart_y; + unsigned char albumart_xalign; /* WPS_ALBUMART_ALIGN_LEFT, _CENTER, _RIGHT */ + unsigned char albumart_yalign; /* WPS_ALBUMART_ALIGN_TOP, _CENTER, _BOTTOM */ + short albumart_max_width; + short albumart_max_height; + + int albumart_cond_index; +#endif + +#else /*HAVE_LCD_CHARCELLS */ + unsigned short wps_progress_pat[8]; + bool full_line_progressbar; +#endif + +#ifdef HAVE_TOUCHSCREEN + struct touchregion touchregion[MAX_TOUCHREGIONS]; + short touchregion_count; +#endif + +#ifdef HAVE_REMOTE_LCD + bool remote_wps; +#endif + + /* Number of lines in the WPS. During WPS parsing, this is + the index of the line being parsed. */ + int num_lines; + + /* Number of viewports in the WPS */ + int num_viewports; + struct wps_viewport viewports[WPS_MAX_VIEWPORTS]; + + struct wps_line lines[WPS_MAX_LINES]; + + /* Total number of sublines in the WPS. During WPS parsing, this is + the index of the subline where the parsed tokens are added to. */ + int num_sublines; + struct wps_subline sublines[WPS_MAX_SUBLINES]; + + /* Total number of tokens in the WPS. During WPS parsing, this is + the index of the token being parsed. */ + int num_tokens; + struct wps_token tokens[WPS_MAX_TOKENS]; + + char string_buffer[STRING_BUFFER_SIZE]; + char *strings[WPS_MAX_STRINGS]; + int num_strings; + + bool wps_loaded; + + /* tick the volume button was last pressed */ + unsigned int button_time_volume; +}; + +/* 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, + struct screen *display, + const char *buf, + bool isfile); + +/* Redraw statusbars if necessary */ +void gwps_draw_statusbars(void); + +/* Returns the index of the subline in the subline array + line - 0-based line number + subline - 0-based subline number within the line + */ +int wps_subline_index(struct wps_data *wps_data, int line, int subline); + +/* Returns the index of the first subline's token in the token array + line - 0-based line number + subline - 0-based subline number within the line + */ +int wps_first_token_index(struct wps_data *data, int line, int subline); + +/* Returns the index of the last subline's token in the token array. + line - 0-based line number + subline - 0-based subline number within the line + */ +int wps_last_token_index(struct wps_data *data, int line, int subline); + +/* 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; + bool do_full_update; +}; + + +/* 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); Currently unused */ + +/* 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); Currently unused */ +/* wps_state end*/ + +/* gui_wps + defines a wps with its 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; +}; + +/* gui_wps end */ + + +/* currently only on wps_state is needed */ +extern struct wps_state wps_state; +extern struct gui_wps gui_wps[NB_SCREENS]; + +/***** wps_tokens.c ******/ + +const char *get_token_value(struct gui_wps *gwps, + struct wps_token *token, + char *buf, int buf_size, + int *intval); + +#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 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2007 Nicolas Pennequin, Dan Everton, Matthias Mohr + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include +#include +#include +#include "file.h" +#include "misc.h" +#include "plugin.h" + +#ifdef __PCTOOL__ +#ifdef WPSEDITOR +#include "proxy.h" +#include "sysfont.h" +#else +#include "checkwps.h" +#include "audio.h" +#define DEBUGF printf +#endif /*WPSEDITOR*/ +#else +#include "debug.h" +#endif /*__PCTOOL__*/ + +#include +#include +#include "font.h" + +#include "wps_internals.h" +#include "settings.h" +#include "settings_list.h" + +#ifdef HAVE_LCD_BITMAP +#include "bmp.h" +#endif + +#include "backdrop.h" + +#define WPS_DEFAULTCFG WPS_DIR "/rockbox_default.wps" +#define RWPS_DEFAULTCFG WPS_DIR "/rockbox_default.rwps" + +#define WPS_ERROR_INVALID_PARAM -1 + +/* level of current conditional. + -1 means we're not in a conditional. */ +static int level = -1; + +/* index of the last WPS_TOKEN_CONDITIONAL_OPTION + or WPS_TOKEN_CONDITIONAL_START in current level */ +static int lastcond[WPS_MAX_COND_LEVEL]; + +/* index of the WPS_TOKEN_CONDITIONAL in current level */ +static int condindex[WPS_MAX_COND_LEVEL]; + +/* number of condtional options in current level */ +static int numoptions[WPS_MAX_COND_LEVEL]; + +/* the current line in the file */ +static int line; + +#ifdef HAVE_LCD_BITMAP + +#if LCD_DEPTH > 1 +#define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS+1) /* WPS images + pbar bitmap + backdrop */ +#else +#define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS) /* WPS images + pbar bitmap */ +#endif + +#define PROGRESSBAR_BMP MAX_IMAGES +#define BACKDROP_BMP (MAX_BITMAPS-1) + +/* pointers to the bitmap filenames in the WPS source */ +static const char *bmp_names[MAX_BITMAPS]; + +#endif /* HAVE_LCD_BITMAP */ + +#if defined(DEBUG) || defined(SIMULATOR) +/* debugging function */ +extern void print_debug_info(struct wps_data *data, int fail, int line); +#endif + +static void wps_reset(struct wps_data *data); + +/* Function for parsing of details for a token. At the moment the + function is called, the token type has already been set. The + function must fill in the details and possibly add more tokens + to the token array. It should return the number of chars that + has been consumed. + + wps_bufptr points to the char following the tag (i.e. where + details begin). + token is the pointer to the 'main' token being parsed + */ +typedef int (*wps_tag_parse_func)(const char *wps_bufptr, + struct wps_token *token, struct wps_data *wps_data); + +struct wps_tag { + enum wps_token_type type; + const char name[3]; + unsigned char refresh_type; + const wps_tag_parse_func parse_func; +}; +static int skip_end_of_line(const char *wps_bufptr); +/* prototypes of all special parse functions : */ +static int parse_timeout(const char *wps_bufptr, + struct wps_token *token, struct wps_data *wps_data); +static int parse_progressbar(const char *wps_bufptr, + struct wps_token *token, struct wps_data *wps_data); +static int parse_dir_level(const char *wps_bufptr, + struct wps_token *token, struct wps_data *wps_data); +static int parse_setting(const char *wps_bufptr, + struct wps_token *token, struct wps_data *wps_data); + +#ifdef HAVE_LCD_BITMAP +static int parse_viewport_display(const char *wps_bufptr, + struct wps_token *token, struct wps_data *wps_data); +static int parse_viewport(const char *wps_bufptr, + struct wps_token *token, struct wps_data *wps_data); +static int parse_statusbar_enable(const char *wps_bufptr, + struct wps_token *token, struct wps_data *wps_data); +static int parse_statusbar_disable(const char *wps_bufptr, + struct wps_token *token, struct wps_data *wps_data); +static int parse_image_display(const char *wps_bufptr, + struct wps_token *token, struct wps_data *wps_data); +static int parse_image_load(const char *wps_bufptr, + struct wps_token *token, struct wps_data *wps_data); +#endif /*HAVE_LCD_BITMAP */ +#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1)) +static int parse_image_special(const char *wps_bufptr, + struct wps_token *token, struct wps_data *wps_data); +#endif +#ifdef HAVE_ALBUMART +static int parse_albumart_load(const char *wps_bufptr, + struct wps_token *token, struct wps_data *wps_data); +static int parse_albumart_conditional(const char *wps_bufptr, + struct wps_token *token, struct wps_data *wps_data); +#endif /* HAVE_ALBUMART */ +#ifdef HAVE_TOUCHSCREEN +static int parse_touchregion(const char *wps_bufptr, + struct wps_token *token, struct wps_data *wps_data); +#else +static int fulline_tag_not_supported(const char *wps_bufptr, + struct wps_token *token, struct wps_data *wps_data) +{ + (void)token; (void)wps_data; + return skip_end_of_line(wps_bufptr); +} +#define parse_touchregion fulline_tag_not_supported +#endif +#ifdef CONFIG_RTC +#define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC +#else +#define WPS_RTC_REFRESH WPS_REFRESH_STATIC +#endif + +/* array of available tags - those with more characters have to go first + (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */ +static const struct wps_tag all_tags[] = { + + { WPS_TOKEN_ALIGN_CENTER, "ac", 0, NULL }, + { WPS_TOKEN_ALIGN_LEFT, "al", 0, NULL }, + { WPS_TOKEN_ALIGN_RIGHT, "ar", 0, NULL }, + + { WPS_TOKEN_BATTERY_PERCENT, "bl", WPS_REFRESH_DYNAMIC, NULL }, + { WPS_TOKEN_BATTERY_VOLTS, "bv", WPS_REFRESH_DYNAMIC, NULL }, + { WPS_TOKEN_BATTERY_TIME, "bt", WPS_REFRESH_DYNAMIC, NULL }, + { WPS_TOKEN_BATTERY_SLEEPTIME, "bs", WPS_REFRESH_DYNAMIC, NULL }, +#if CONFIG_CHARGING >= CHARGING_MONITOR + { WPS_TOKEN_BATTERY_CHARGING, "bc", WPS_REFRESH_DYNAMIC, NULL }, +#endif +#if CONFIG_CHARGING + { WPS_TOKEN_BATTERY_CHARGER_CONNECTED,"bp", WPS_REFRESH_DYNAMIC, NULL }, +#endif + + { WPS_TOKEN_RTC_PRESENT , "cc", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_RTC_DAY_OF_MONTH, "cd", WPS_RTC_REFRESH, NULL }, + { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,"ce", WPS_RTC_REFRESH, NULL }, + { WPS_TOKEN_RTC_12HOUR_CFG, "cf", WPS_RTC_REFRESH, NULL }, + { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", WPS_RTC_REFRESH, NULL }, + { WPS_TOKEN_RTC_HOUR_24, "ck", WPS_RTC_REFRESH, NULL }, + { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", WPS_RTC_REFRESH, NULL }, + { WPS_TOKEN_RTC_HOUR_12, "cl", WPS_RTC_REFRESH, NULL }, + { WPS_TOKEN_RTC_MONTH, "cm", WPS_RTC_REFRESH, NULL }, + { WPS_TOKEN_RTC_MINUTE, "cM", WPS_RTC_REFRESH, NULL }, + { WPS_TOKEN_RTC_SECOND, "cS", WPS_RTC_REFRESH, NULL }, + { WPS_TOKEN_RTC_YEAR_2_DIGITS, "cy", WPS_RTC_REFRESH, NULL }, + { WPS_TOKEN_RTC_YEAR_4_DIGITS, "cY", WPS_RTC_REFRESH, NULL }, + { WPS_TOKEN_RTC_AM_PM_UPPER, "cP", WPS_RTC_REFRESH, NULL }, + { WPS_TOKEN_RTC_AM_PM_LOWER, "cp", WPS_RTC_REFRESH, NULL }, + { WPS_TOKEN_RTC_WEEKDAY_NAME, "ca", WPS_RTC_REFRESH, NULL }, + { WPS_TOKEN_RTC_MONTH_NAME, "cb", WPS_RTC_REFRESH, NULL }, + { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", WPS_RTC_REFRESH, NULL }, + { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", WPS_RTC_REFRESH, NULL }, + + /* current file */ + { WPS_TOKEN_FILE_BITRATE, "fb", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_FILE_CODEC, "fc", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_FILE_FREQUENCY, "ff", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_FILE_FREQUENCY_KHZ, "fk", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "fm", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_FILE_NAME, "fn", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_FILE_PATH, "fp", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_FILE_SIZE, "fs", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_FILE_VBR, "fv", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_FILE_DIRECTORY, "d", WPS_REFRESH_STATIC, + parse_dir_level }, + + /* next file */ + { WPS_TOKEN_FILE_BITRATE, "Fb", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_STATIC, + parse_dir_level }, + + /* current metadata */ + { WPS_TOKEN_METADATA_ARTIST, "ia", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_COMPOSER, "ic", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_ALBUM, "id", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_ALBUM_ARTIST, "iA", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_GROUPING, "iG", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_GENRE, "ig", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_DISC_NUMBER, "ik", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_TRACK_NUMBER, "in", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_TRACK_TITLE, "it", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_VERSION, "iv", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_YEAR, "iy", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_COMMENT, "iC", WPS_REFRESH_STATIC, NULL }, + + /* next metadata */ + { WPS_TOKEN_METADATA_ARTIST, "Ia", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_STATIC, NULL }, + +#if (CONFIG_CODEC != MAS3507D) + { WPS_TOKEN_SOUND_PITCH, "Sp", WPS_REFRESH_DYNAMIC, NULL }, +#endif + +#if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD) + { WPS_TOKEN_VLED_HDD, "lh", WPS_REFRESH_DYNAMIC, NULL }, +#endif + + { WPS_TOKEN_MAIN_HOLD, "mh", WPS_REFRESH_DYNAMIC, NULL }, + +#ifdef HAS_REMOTE_BUTTON_HOLD + { WPS_TOKEN_REMOTE_HOLD, "mr", WPS_REFRESH_DYNAMIC, NULL }, +#else + { WPS_TOKEN_UNKNOWN, "mr", 0, NULL }, +#endif + + { WPS_TOKEN_REPEAT_MODE, "mm", WPS_REFRESH_DYNAMIC, NULL }, + { WPS_TOKEN_PLAYBACK_STATUS, "mp", WPS_REFRESH_DYNAMIC, NULL }, + { WPS_TOKEN_BUTTON_VOLUME, "mv", WPS_REFRESH_DYNAMIC, + parse_timeout }, + +#ifdef HAVE_LCD_BITMAP + { WPS_TOKEN_PEAKMETER, "pm", WPS_REFRESH_PEAK_METER, NULL }, +#else + { WPS_TOKEN_PLAYER_PROGRESSBAR, "pf", + WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar }, +#endif + { WPS_TOKEN_PROGRESSBAR, "pb", WPS_REFRESH_PLAYER_PROGRESS, + parse_progressbar }, + + { WPS_TOKEN_VOLUME, "pv", WPS_REFRESH_DYNAMIC, NULL }, + + { WPS_TOKEN_TRACK_ELAPSED_PERCENT, "px", WPS_REFRESH_DYNAMIC, NULL }, + { WPS_TOKEN_TRACK_TIME_ELAPSED, "pc", WPS_REFRESH_DYNAMIC, NULL }, + { WPS_TOKEN_TRACK_TIME_REMAINING, "pr", WPS_REFRESH_DYNAMIC, NULL }, + { WPS_TOKEN_TRACK_LENGTH, "pt", WPS_REFRESH_STATIC, NULL }, + + { WPS_TOKEN_PLAYLIST_POSITION, "pp", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_PLAYLIST_ENTRIES, "pe", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_PLAYLIST_NAME, "pn", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_PLAYLIST_SHUFFLE, "ps", WPS_REFRESH_DYNAMIC, NULL }, + +#ifdef HAVE_TAGCACHE + { WPS_TOKEN_DATABASE_PLAYCOUNT, "rp", WPS_REFRESH_DYNAMIC, NULL }, + { WPS_TOKEN_DATABASE_RATING, "rr", WPS_REFRESH_DYNAMIC, NULL }, + { WPS_TOKEN_DATABASE_AUTOSCORE, "ra", WPS_REFRESH_DYNAMIC, NULL }, +#endif + +#if CONFIG_CODEC == SWCODEC + { WPS_TOKEN_REPLAYGAIN, "rg", WPS_REFRESH_STATIC, NULL }, + { WPS_TOKEN_CROSSFADE, "xf", WPS_REFRESH_DYNAMIC, NULL }, +#endif + + { WPS_NO_TOKEN, "s", WPS_REFRESH_SCROLL, NULL }, + { WPS_TOKEN_SUBLINE_TIMEOUT, "t", 0, parse_timeout }, + +#ifdef HAVE_LCD_BITMAP + { WPS_NO_TOKEN, "we", 0, parse_statusbar_enable }, + { WPS_NO_TOKEN, "wd", 0, parse_statusbar_disable }, + + { WPS_NO_TOKEN, "xl", 0, parse_image_load }, + + { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, "xd", WPS_REFRESH_STATIC, + parse_image_display }, + + { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load }, +#ifdef HAVE_ALBUMART + { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load }, + { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_STATIC, + parse_albumart_conditional }, +#endif + + { WPS_VIEWPORT_ENABLE, "Vd", WPS_REFRESH_DYNAMIC, + parse_viewport_display }, + { WPS_NO_TOKEN, "V", 0, parse_viewport }, + +#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1)) + { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special }, +#endif +#endif + + { WPS_TOKEN_SETTING, "St", WPS_REFRESH_DYNAMIC, parse_setting }, + + { WPS_TOKEN_LASTTOUCH, "Tl", WPS_REFRESH_DYNAMIC, parse_timeout }, + { WPS_NO_TOKEN, "T", 0, parse_touchregion }, + + { WPS_TOKEN_UNKNOWN, "", 0, NULL } + /* the array MUST end with an empty string (first char is \0) */ +}; + +/* Returns the number of chars that should be skipped to jump + immediately after the first eol, i.e. to the start of the next line */ +static int skip_end_of_line(const char *wps_bufptr) +{ + line++; + int skip = 0; + while(*(wps_bufptr + skip) != '\n') + skip++; + return ++skip; +} + +/* Starts a new subline in the current line during parsing */ +static void wps_start_new_subline(struct wps_data *data) +{ + data->num_sublines++; + data->sublines[data->num_sublines].first_token_idx = data->num_tokens; + data->lines[data->num_lines].num_sublines++; +} + +#ifdef HAVE_LCD_BITMAP + +static int parse_statusbar_enable(const char *wps_bufptr, + struct wps_token *token, + struct wps_data *wps_data) +{ + (void)token; /* Kill warnings */ + wps_data->wps_sb_tag = true; + wps_data->show_sb_on_wps = true; + if (wps_data->viewports[0].vp.y == 0) + { + wps_data->viewports[0].vp.y = STATUSBAR_HEIGHT; + wps_data->viewports[0].vp.height -= STATUSBAR_HEIGHT; + } + return skip_end_of_line(wps_bufptr); +} + +static int parse_statusbar_disable(const char *wps_bufptr, + struct wps_token *token, + struct wps_data *wps_data) +{ + (void)token; /* Kill warnings */ + wps_data->wps_sb_tag = true; + wps_data->show_sb_on_wps = false; + if (wps_data->viewports[0].vp.y == STATUSBAR_HEIGHT) + { + wps_data->viewports[0].vp.y = 0; + wps_data->viewports[0].vp.height += STATUSBAR_HEIGHT; + } + return skip_end_of_line(wps_bufptr); +} + +static bool load_bitmap(struct wps_data *wps_data, + char* filename, + struct bitmap *bm) +{ + int format; +#ifdef HAVE_REMOTE_LCD + if (wps_data->remote_wps) + format = FORMAT_ANY|FORMAT_REMOTE; + else +#endif + format = FORMAT_ANY|FORMAT_TRANSPARENT; + + int ret = read_bmp_file(filename, bm, + wps_data->img_buf_free, + format,NULL); + + if (ret > 0) + { +#if LCD_DEPTH == 16 + if (ret % 2) ret++; + /* Always consume an even number of bytes */ +#endif + wps_data->img_buf_ptr += ret; + wps_data->img_buf_free -= ret; + + return true; + } + else + return false; +} + +static int get_image_id(int c) +{ + if(c >= 'a' && c <= 'z') + return c - 'a'; + else if(c >= 'A' && c <= 'Z') + return c - 'A' + 26; + else + return -1; +} + +static char *get_image_filename(const char *start, const char* bmpdir, + char *buf, int buf_size) +{ + const char *end = strchr(start, '|'); + + if ( !end || (end - start) >= (buf_size - (int)ROCKBOX_DIR_LEN - 2) ) + { + buf = "\0"; + return NULL; + } + + int bmpdirlen = strlen(bmpdir); + + strcpy(buf, bmpdir); + buf[bmpdirlen] = '/'; + memcpy( &buf[bmpdirlen + 1], start, end - start); + buf[bmpdirlen + 1 + end - start] = 0; + + return buf; +} + +static int parse_image_display(const char *wps_bufptr, + struct wps_token *token, + struct wps_data *wps_data) +{ + (void)wps_data; + int n = get_image_id(wps_bufptr[0]); + int subimage; + + if (n == -1) + { + /* invalid picture display tag */ + return WPS_ERROR_INVALID_PARAM; + } + + if ((subimage = get_image_id(wps_bufptr[1])) != -1) + { + /* Sanity check */ + if (subimage >= wps_data->img[n].num_subimages) + return WPS_ERROR_INVALID_PARAM; + + /* Store sub-image number to display in high bits */ + token->value.i = n | (subimage << 8); + return 2; /* We have consumed 2 bytes */ + } else { + token->value.i = n; + return 1; /* We have consumed 1 byte */ + } +} + +static int parse_image_load(const char *wps_bufptr, + struct wps_token *token, + struct wps_data *wps_data) +{ + int n; + const char *ptr = wps_bufptr; + const char *pos; + const char* filename; + const char* id; + const char *newline; + int x,y; + + /* format: %x|n|filename.bmp|x|y| + or %xl|n|filename.bmp|x|y| + or %xl|n|filename.bmp|x|y|num_subimages| + */ + + if (*ptr != '|') + return WPS_ERROR_INVALID_PARAM; + + ptr++; + + if (!(ptr = parse_list("ssdd", NULL, '|', ptr, &id, &filename, &x, &y))) + return WPS_ERROR_INVALID_PARAM; + + /* Check there is a terminating | */ + if (*ptr != '|') + return WPS_ERROR_INVALID_PARAM; + + /* get the image ID */ + n = get_image_id(*id); + + /* check the image number and load state */ + if(n < 0 || n >= MAX_IMAGES || wps_data->img[n].loaded) + { + /* Invalid image ID */ + return WPS_ERROR_INVALID_PARAM; + } + + /* save a pointer to the filename */ + bmp_names[n] = filename; + + wps_data->img[n].x = x; + wps_data->img[n].y = y; + + /* save current viewport */ + wps_data->img[n].vp = &wps_data->viewports[wps_data->num_viewports].vp; + + if (token->type == WPS_TOKEN_IMAGE_DISPLAY) + { + wps_data->img[n].always_display = true; + } + else + { + /* Parse the (optional) number of sub-images */ + ptr++; + newline = strchr(ptr, '\n'); + pos = strchr(ptr, '|'); + if (pos && pos < newline) + wps_data->img[n].num_subimages = atoi(ptr); + + if (wps_data->img[n].num_subimages <= 0) + return WPS_ERROR_INVALID_PARAM; + } + + /* Skip the rest of the line */ + return skip_end_of_line(wps_bufptr); +} + +static int parse_viewport_display(const char *wps_bufptr, + struct wps_token *token, + struct wps_data *wps_data) +{ + (void)wps_data; + char letter = wps_bufptr[0]; + + if (letter < 'a' || letter > 'z') + { + /* invalid viewport tag */ + return WPS_ERROR_INVALID_PARAM; + } + token->value.i = letter; + return 1; +} + +static int parse_viewport(const char *wps_bufptr, + struct wps_token *token, + struct wps_data *wps_data) +{ + (void)token; /* Kill warnings */ + const char *ptr = wps_bufptr; + struct viewport* vp; + int depth; + uint32_t set = 0; + enum { + PL_X = 0, + PL_Y, + PL_WIDTH, + PL_HEIGHT, + PL_FONT, + PL_FG, + PL_BG, + }; + int lcd_width = LCD_WIDTH, lcd_height = LCD_HEIGHT; +#ifdef HAVE_REMOTE_LCD + if (wps_data->remote_wps) + { + lcd_width = LCD_REMOTE_WIDTH; + lcd_height = LCD_REMOTE_HEIGHT; + } +#endif + + if (wps_data->num_viewports >= WPS_MAX_VIEWPORTS) + return WPS_ERROR_INVALID_PARAM; + + wps_data->num_viewports++; + /* check for the optional letter to signify its a hideable viewport */ + /* %Vl|