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