| /*************************************************************************** |
| * __________ __ ___. |
| * Open \______ \ ____ ____ | | _\_ |__ _______ ___ |
| * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / |
| * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
| * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
| * \/ \/ \/ \/ \/ |
| * $Id$ |
| * |
| * Copyright (C) 2002 Björn Stenberg |
| * |
| * 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. |
| * |
| ****************************************************************************/ |
| #include "gwps-common.h" |
| #include "font.h" |
| #include <stdio.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include "system.h" |
| #include "settings.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 "backlight.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 "atoi.h" |
| #endif |
| #ifdef HAVE_LCD_COLOR |
| #include "backdrop.h" |
| #endif |
| #include "dsp.h" |
| #include "action.h" |
| |
| #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 |
| |
| /* Skip leading UTF-8 BOM, if present. */ |
| static char* skip_utf8_bom(char* buf) |
| { |
| unsigned char* s = (unsigned char*) buf; |
| |
| if(s[0] == 0xef && s[1] == 0xbb && s[2] == 0xbf) |
| { |
| buf += 3; |
| } |
| |
| return buf; |
| } |
| |
| /* |
| * returns the image_id between |
| * a..z and A..Z |
| */ |
| #ifdef HAVE_LCD_BITMAP |
| 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; |
| } |
| #endif |
| |
| /* |
| * parse the given buffer for following static tags: |
| * %x - load image for always display |
| * %X - load backdrop image |
| * %xl - preload image |
| * %we - enable statusbar on wps regardless of the global setting |
| * %wd - disable statusbar on wps regardless of the global setting |
| * and also for: |
| * # - a comment line |
| * |
| * it returns true if one of these tags is found and handled |
| * false otherwise |
| */ |
| bool wps_data_preload_tags(struct wps_data *data, char *buf, |
| const char *bmpdir, size_t bmpdirlen) |
| { |
| if(!data || !buf) return false; |
| |
| char c; |
| #ifndef HAVE_LCD_BITMAP |
| /* no bitmap-lcd == no bitmap loading */ |
| (void)bmpdir; |
| (void)bmpdirlen; |
| #endif |
| buf = skip_utf8_bom(buf); |
| |
| if(*buf == '#') |
| return true; |
| if('%' != *buf) |
| return false; |
| buf++; |
| |
| c = *buf; |
| switch (c) |
| { |
| #ifdef HAVE_LCD_BITMAP |
| case 'w': |
| /* |
| * if tag found then return because these two tags must be on |
| * must be on their own line |
| */ |
| if(*(buf+1) == 'd' || *(buf+1) == 'e') |
| { |
| data->wps_sb_tag = true; |
| if( *(buf+1) == 'e' ) |
| data->show_sb_on_wps = true; |
| return true; |
| } |
| break; |
| |
| #ifdef HAVE_LCD_COLOR |
| case 'X': |
| /* Backdrop image - must be the same size as the LCD */ |
| { |
| char *ptr = buf+2; |
| char *pos = NULL; |
| char imgname[MAX_PATH]; |
| |
| /* format: %X|filename.bmp| */ |
| |
| /* 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; |
| } |
| |
| /* load the image */ |
| return load_wps_backdrop(imgname); |
| } |
| |
| break; |
| #endif |
| |
| case 'P': |
| /* progress bar image */ |
| { |
| int ret = 0; |
| char *ptr = buf+2; |
| char *pos = NULL; |
| char imgname[MAX_PATH]; |
| |
| /* format: %P|filename.bmp| */ |
| { |
| /* 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; |
| |
| /* load the image */ |
| data->progressbar.bm.data=data->img_buf_ptr; |
| ret = read_bmp_file(imgname, &data->progressbar.bm, |
| data->img_buf_free, |
| FORMAT_ANY|FORMAT_TRANSPARENT); |
| |
| if (ret > 0) |
| { |
| #if LCD_DEPTH == 16 |
| if (ret % 2) ret++; |
| /* Always consume an even number of bytes */ |
| #endif |
| |
| data->img_buf_ptr += ret; |
| data->img_buf_free -= ret; |
| |
| if (data->progressbar.bm.width <= LCD_WIDTH) { |
| data->progressbar.have_bitmap_pb=true; |
| return true; |
| } else |
| return false; |
| } |
| |
| } |
| } |
| |
| break; |
| |
| case 'x': |
| /* 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 = get_image_id(*ptr); |
| |
| if(n < 0 || n >= MAX_IMAGES) |
| { |
| /* Skip the rest of the line */ |
| while(*buf != '\n') |
| buf++; |
| return false; |
| } |
| ptr = pos+1; |
| |
| /* check the image number and load state */ |
| if (data->img[n].loaded) |
| { |
| /* Skip the rest of the line */ |
| while(*buf != '\n') |
| buf++; |
| return false; |
| } |
| else |
| { |
| /* get filename */ |
| pos = strchr(ptr, '|'); |
| |
| if (pos == NULL) |
| return false; |
| |
| 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++; |
| return false; |
| } |
| |
| /* get y-position */ |
| ptr = pos+1; |
| pos = strchr(ptr, '|'); |
| if (pos) |
| data->img[n].y = atoi(ptr); |
| else |
| { |
| /* weird syntax, bail out */ |
| buf++; |
| return false; |
| } |
| |
| /* load the image */ |
| data->img[n].bm.data = data->img_buf_ptr; |
| ret = read_bmp_file(imgname, &data->img[n].bm, |
| data->img_buf_free, |
| FORMAT_ANY|FORMAT_TRANSPARENT); |
| |
| if (ret > 0) |
| { |
| #if LCD_DEPTH == 16 |
| if (ret % 2) ret++; |
| /* Always consume an even number of bytes */ |
| #endif |
| |
| data->img_buf_ptr += ret; |
| data->img_buf_free -= ret; |
| data->img[n].loaded = true; |
| if(qual == '|') |
| data->img[n].always_display = true; |
| } |
| return true; |
| } |
| } |
| } |
| } |
| |
| break; |
| #endif |
| } |
| /* no of these tags found */ |
| return false; |
| } |
| |
| |
| /* draws the statusbar on the given wps-screen */ |
| #ifdef HAVE_LCD_BITMAP |
| static void gui_wps_statusbar_draw(struct gui_wps *wps, bool force) |
| { |
| bool draw = global_settings.statusbar; |
| |
| if(wps->data->wps_sb_tag |
| && wps->data->show_sb_on_wps) |
| draw = true; |
| else if(wps->data->wps_sb_tag) |
| draw = false; |
| if(draw) |
| gui_statusbar_draw(wps->statusbar, force); |
| } |
| #else |
| #define gui_wps_statusbar_draw(wps, force) \ |
| gui_statusbar_draw((wps)->statusbar, (force)) |
| #endif |
| |
| /* 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 */ |
| int limit = *intval; |
| #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 |
| char *p=strchr(tag, '|'); |
| if (p) { |
| wps_data->progress_height=atoi(++p); |
| p=strchr(p, '|'); |
| if (p) { |
| wps_data->progress_start=atoi(++p); |
| p=strchr(p, '|'); |
| if (p) |
| wps_data->progress_end=atoi(++p); |
| else |
| wps_data->progress_end=0; |
| }else { |
| wps_data->progress_start=0; |
| wps_data->progress_end=0; |
| } |
| |
| if (wps_data->progress_height<3) |
| wps_data->progress_height=3; |
| if (wps_data->progress_end<wps_data->progress_start+3) |
| wps_data->progress_end=0; |
| }else { |
| wps_data->progress_height=6; |
| wps_data->progress_start=0; |
| wps_data->progress_end=0; |
| } |
| return "\x01"; |
| #endif |
| case 'f': /* full-line progress bar */ |
| #ifdef HAVE_LCD_CHARCELLS |
| if(is_new_player()) { |
| *flags |= WPS_REFRESH_PLAYER_PROGRESS; |
| *flags |= WPS_REFRESH_DYNAMIC; |
| wps_data->full_line_progressbar=1; |
| /* we need 11 characters (full line) for |
| progress-bar */ |
| snprintf(buf, buf_size, " "); |
| } |
| else |
| { |
| /* Tell the user if we have an OldPlayer */ |
| snprintf(buf, buf_size, " <Old LCD> "); |
| } |
| return buf; |
| #endif |
| case 'p': /* Playlist Position */ |
| *flags |= WPS_REFRESH_STATIC; |
| snprintf(buf, buf_size, "%d", |
| playlist_get_display_index()); |
| return buf; |
| |
| case 'n': /* Playlist Name (without path) */ |
| *flags |= WPS_REFRESH_STATIC; |
| return playlist_name(NULL, buf, buf_size); |
| |
| case 'e': /* Playlist Total Entries */ |
| *flags |= WPS_REFRESH_STATIC; |
| snprintf(buf, buf_size, "%d", playlist_amount()); |
| return buf; |
| |
| case 'c': /* Current Time in Song */ |
| *flags |= WPS_REFRESH_DYNAMIC; |
| gui_wps_format_time(buf, buf_size, |
| id3->elapsed + wps_state.ff_rewind_count); |
| return buf; |
| |
| case 'r': /* Remaining Time in Song */ |
| *flags |= WPS_REFRESH_DYNAMIC; |
| gui_wps_format_time(buf, buf_size, |
| id3->length - id3->elapsed - |
| wps_state.ff_rewind_count); |
| return buf; |
| |
| case 't': /* Total Time */ |
| *flags |= WPS_REFRESH_STATIC; |
| gui_wps_format_time(buf, buf_size, id3->length); |
| return buf; |
| |
| #ifdef HAVE_LCD_BITMAP |
| case 'm': /* Peak Meter */ |
| *flags |= WPS_REFRESH_PEAK_METER; |
| return "\x01"; |
| #endif |
| case 's': /* shuffle */ |
| *flags |= WPS_REFRESH_DYNAMIC; |
| if ( global_settings.playlist_shuffle ) |
| return "s"; |
| else |
| return NULL; |
| break; |
| |
| case 'v': /* volume */ |
| *flags |= WPS_REFRESH_DYNAMIC; |
| snprintf(buf, buf_size, "%d", global_settings.volume); |
| *intval = limit * (global_settings.volume |
| - sound_min(SOUND_VOLUME)) |
| / (sound_max(SOUND_VOLUME) |
| - sound_min(SOUND_VOLUME)) + 1; |
| return buf; |
| |
| } |
| break; |
| |
| #if (CONFIG_CODEC == SWCODEC) |
| case 'S': /* DSP/Equalizer/Sound settings */ |
| switch (tag[1]) |
| { |
| case 'p': /* pitch */ |
| *intval = sound_get_pitch(); |
| snprintf(buf, buf_size, "%d.%d", |
| *intval / 10, *intval % 10); |
| return buf; |
| } |
| break; |
| #endif |
| |
| 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; |
| |
| #ifdef HAS_BUTTON_HOLD |
| case 'h': /* hold */ |
| *flags |= WPS_REFRESH_DYNAMIC; |
| if (button_hold()) |
| return "h"; |
| else |
| return NULL; |
| #endif |
| #ifdef HAS_REMOTE_BUTTON_HOLD |
| 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(); |
| limit = MAX(limit, 2); |
| if (l > -1) |
| { |
| snprintf(buf, buf_size, "%d", l); |
| /* First enum is used for "unknown level". */ |
| *intval = (limit - 1) * l / 100 + 1 + 1; |
| } |
| else |
| { |
| *intval = 1; |
| return "?"; |
| } |
| return buf; |
| } |
| |
| case 'v': /* battery voltage */ |
| { |
| unsigned int v = battery_voltage(); |
| snprintf(buf, buf_size, "%d.%02d", v/100, v%100); |
| 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 's': /* sleep timer */ |
| { |
| if (get_sleep_timer() == 0) |
| { |
| return NULL; |
| } |
| else |
| { |
| gui_wps_format_time(buf, buf_size, \ |
| get_sleep_timer() * 1000); |
| return buf; |
| } |
| } |
| |
| #ifdef CONFIG_CHARGING |
| case 'p': /* External power plugged in? */ |
| { |
| if(charger_input_state==CHARGER) |
| return "p"; |
| else |
| return NULL; |
| } |
| #endif |
| #if CONFIG_CHARGING >= CHARGING_MONITOR |
| case 'c': /* Charging */ |
| { |
| if (charge_state == CHARGING || charge_state == TOPOFF) { |
| return "c"; |
| } else { |
| return NULL; |
| } |
| } |
| #endif |
| } |
| break; |
| |
| #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD) |
| case 'l': /* VIRTUAL_LED */ |
| { |
| switch(tag[1]) |
| { |
| case 'h': /* Only one we have so far HDD LED */ |
| *flags |= WPS_REFRESH_DYNAMIC; |
| if(led_read(HZ/2)) |
| return "h"; |
| else |
| return NULL; |
| } |
| } |
| break; |
| #endif |
| |
| 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 and Replaygain */ |
| 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; |
| #if CONFIG_CODEC == SWCODEC |
| case 'g': /* ReplayGain */ |
| *flags |= WPS_REFRESH_STATIC; |
| if(global_settings.replaygain) |
| *intval = global_settings.replaygain_type+2; |
| else |
| *intval = 1; |
| snprintf(buf, buf_size, "%d", *intval); |
| return buf; |
| #endif |
| } |
| break; |
| #ifdef CONFIG_RTC |
| case 'c': /* Real Time Clock display */ |
| *flags |= WPS_REFRESH_DYNAMIC; |
| { |
| int value; |
| char *format = 0; |
| char *bufptr = buf; |
| struct tm* tm = get_time(); |
| int i; |
| for (i=1;/*break*/;i++) { |
| switch(tag[i]) |
| { |
| case 'a': /* abbreviated weekday name (Sun..Sat) */ |
| value = tm->tm_wday; |
| if (value > 6 || value < 0) continue; |
| value = snprintf( |
| bufptr,buf_size,"%s",str(dayname[value])); |
| bufptr += value; |
| buf_size -= value; |
| continue; |
| case 'b': /* abbreviated month name (Jan..Dec) */ |
| value = tm->tm_mon; |
| if (value > 11 || value < 0) continue; |
| value = snprintf( |
| bufptr,buf_size,"%s",str(monthname[value])); |
| bufptr += value; |
| buf_size -= value; |
| continue; |
| case 'd': /* day of month (01..31) */ |
| value = tm->tm_mday; |
| if (value > 31 || value < 1) continue; |
| format = "%02d"; |
| break; |
| case 'e': /* day of month, blank padded ( 1..31) */ |
| value = tm->tm_mday; |
| if (value > 31 || value < 1) continue; |
| format = "%2d"; |
| break; |
| case 'H': /* hour (00..23) */ |
| value = tm->tm_hour; |
| if (value > 23) continue; |
| format = "%02d"; |
| break; |
| case 'k': /* hour ( 0..23) */ |
| value = tm->tm_hour; |
| if (value > 23) continue; |
| format = "%2d"; |
| break; |
| case 'I': /* hour (01..12) */ |
| value = tm->tm_hour; |
| if (value > 23) continue; |
| value %= 12; |
| if (value == 0) value = 12; |
| format = "%02d"; |
| break; |
| case 'l': /* hour ( 1..12) */ |
| value = tm->tm_hour; |
| if (value > 23 || value < 0) continue; |
| value %= 12; |
| if (value == 0) value = 12; |
| format = "%2d"; |
| break; |
| case 'm': /* month (01..12) */ |
| value = tm->tm_mon; |
| if (value > 11 || value < 0) continue; |
| value++; |
| format = "%02d"; |
| break; |
| case 'M': /* minute (00..59) */ |
| value = tm->tm_min; |
| if (value > 59 || value < 0) continue; |
| format = "%02d"; |
| break; |
| case 'S': /* second (00..59) */ |
| value = tm->tm_sec; |
| if (value > 59 || value < 0) continue; |
| format = "%02d"; |
| break; |
| case 'y': /* last two digits of year (00..99) */ |
| value = tm->tm_year; |
| value %= 100; |
| format = "%02d"; |
| break; |
| case 'Y': /* year (1970...) */ |
| value = tm->tm_year; |
| if (value > 199 || value < 100) continue; |
| value += 1900; |
| format = "%04d"; |
| break; |
| case 'p': /* upper case AM or PM indicator */ |
| if (tm->tm_hour/12 == 0) format = "AM"; |
| else format = "PM"; |
| snprintf(bufptr,buf_size,"%s",format); |
| bufptr += 2; |
| buf_size -= 2; |
| continue; |
| case 'P': /* lower case am or pm indicator */ |
| if (tm->tm_hour/12 == 0) format = "am"; |
| else format = "pm"; |
| snprintf(bufptr,buf_size,"%s",format); |
| bufptr += 2; |
| buf_size -= 2; |
| continue; |
| case 'u': /* day of week (1..7); 1 is Monday */ |
| value = tm->tm_wday; |
| if (value < 0 || value > 6) continue; |
| value++; |
| format = "%1d"; |
| break; |
| case 'w': /* day of week (0..6); 0 is Sunday */ |
| value = tm->tm_wday; |
| if (value < 0 || value > 6) continue; |
| format = "%1d"; |
| break; |
| default: |
| if (tag[i] == 'c') { |
| i++; |
| value = -1; |
| break; |
| } else if (tag[i] == '\n') { |
| value = -1; |
| break; |
| } |
| snprintf(bufptr,buf_size,"%c",tag[i]); |
| bufptr++; |
| buf_size--; |
| continue; |
| } /* switch */ |
| if (value < 0) break; |
| |
| value = snprintf(bufptr, buf_size, format, value); |
| bufptr += value; |
| buf_size -= value; |
| } /* while */ |
| *tag_len = i; |
| return buf; |
| } |
| #endif /* CONFIG_RTC */ |
| #if CONFIG_CODEC == SWCODEC |
| case 'x': |
| *flags |= WPS_REFRESH_DYNAMIC; |
| switch(tag[1]) |
| { |
| case 'd': /* crossfeed */ |
| if(global_settings.crossfeed) |
| return "d"; |
| else |
| return NULL; |
| case 'f': /* crossfade */ |
| *intval = global_settings.crossfade+1; |
| snprintf(buf, buf_size, "%d", global_settings.crossfade); |
| return buf; |
| } |
| break; |
| #endif |
| } |
| 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].bm.width, data->img[n].bm.height); |
| 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 ">"). |
| * enums - If not NULL, set to the number of |'s found in the current |
| * conditional (sub-conditionals are ignored). num should be 0 |
| * to find all |'s. |
| * |
| * Returns the new position in fmt. |
| */ |
| static const char* skip_conditional(struct gui_wps *gwps, const char* fmt, |
| int num, int *enums) |
| { |
| int level = 1; |
| int count = num; |
| const char *last_alternative = NULL; |
| #ifdef HAVE_LCD_BITMAP |
| struct wps_data *data = NULL; |
| int last_x=-1, last_y=-1, last_w=-1, last_h=-1; |
| if(gwps) |
| data = gwps->data; |
| if (enums) |
| *enums = 0; |
| #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].bm.width |
| || last_h != data->img[n].bm.height) |
| { |
| last_x = data->img[n].x; |
| last_y = data->img[n].y; |
| last_w = data->img[n].bm.width; |
| last_h = data->img[n].bm.height; |
| clear_image_pos(gwps,n); |
| } |
| } |
| #endif |
| break; |
| |
| case '|': |
| if(1 == level) { |
| if (enums) |
| (*enums)++; |
| 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, NULL); |
| 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 |
| 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++; |
| /* Get number of "|" chars in the current conditional; |
| * used by get_tag when calculating levels. |
| */ |
| skip_conditional(gwps, fmt, 0, &intval); |
| 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(NULL, fmt, 1000, NULL); |
| else |
| if(intval > 1) /* enum */ |
| fmt = skip_conditional(NULL, fmt, intval - 1, NULL); |
| |
| level++; |
| break; |
| |
| default: |
| intval = 1; |
| 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) |
| { |
| 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; |
| |
| 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); |
| 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); |
| sleep(1); |
| } |
| 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) |
| { |
| char* buf = data->format_buffer; |
| char* start_of_line = data->format_buffer; |
| int line = 0; |
| int subline; |
| char c; |
| if(!data) |
| return; |
| |
| for (line=0; line<WPS_MAX_LINES; line++) |
| { |
| for (subline=0; subline<WPS_MAX_SUBLINES; subline++) |
| { |
| data->format_lines[line][subline] = 0; |
| data->time_mult[line][subline] = 0; |
| } |
| data->subline_expire_time[line] = 0; |
| data->curr_subline[line] = SUBLINE_RESET; |
| } |
| |
| line = 0; |
| subline = 0; |
| buf = skip_utf8_bom(buf); |
| data->format_lines[line][subline] = buf; |
| |
| while ((*buf) && (line < WPS_MAX_LINES)) |
| { |
| c = *buf; |
| |
| switch (c) |
| { |
| /* |
| * skip % sequences so "%;" doesn't start a new subline |
| * don't skip %x lines (pre-load bitmaps) |
| */ |
| case '%': |
| 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; |
| } |
| buf++; |
| } |
| } |
| |
| #ifdef HAVE_LCD_BITMAP |
| /* Display images */ |
| static void wps_draw_image(struct gui_wps *gwps, int n) |
| { |
| 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(data->img[n].bm.data, data->img[n].x, |
| data->img[n].y, data->img[n].bm.width, |
| data->img[n].bm.height); |
| #if LCD_DEPTH > 1 |
| } else { |
| display->transparent_bitmap((fb_data *)data->img[n].bm.data, |
| data->img[n].x, |
| data->img[n].y, data->img[n].bm.width, |
| data->img[n].bm.height); |
| } |
| #endif |
| } |
| static void wps_display_images(struct gui_wps *gwps, bool always) |
| { |
| 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( (!always && data->img[n].display) |
| || (always && data->img[n].always_display) ) |
| wps_draw_image(gwps, n); |
| } |
| } |
| 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; |
| bool reset_subline; |
| int search; |
| int search_start; |
| struct align_pos format_align; |
| 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 = 0; |
| gui_wps_statusbar_draw(gwps, true); |
| if(data->wps_sb_tag && data->show_sb_on_wps) |
| offset = STATUSBAR_HEIGHT; |
| else if ( global_settings.statusbar && !data->wps_sb_tag) |
| offset = STATUSBAR_HEIGHT; |
| |
| /* to find out wether the peak meter is enabled we |
| assume it wasn't until we find a line that contains |
| the peak meter. We can't use peak_meter_enabled itself |
| because that would mean to turn off the meter thread |
| temporarily. (That shouldn't matter unless yield |
| or sleep is called but who knows...) |
| */ |
| bool enable_pm = false; |
| |
| /* Set images to not to be displayed */ |
| for (i = 0; i < MAX_IMAGES; i++) { |
| data->img[i].display = false; |
| } |
| #endif |
| /* reset to first subline if refresh all flag is set */ |
| if (refresh_mode == WPS_REFRESH_ALL) |
| { |
| for (i=0; i<WPS_MAX_LINES; i++) |
| { |
| data->curr_subline[i] = SUBLINE_RESET; |
| } |
| } |
| |
| #ifdef HAVE_LCD_CHARCELLS |
| for (i=0; i<8; i++) { |
| if (data->wps_progress_pat[i]==0) |
| data->wps_progress_pat[i]=display->get_locked_pattern(); |
| } |
| #endif |
| |
| if (!state->id3) |
| { |
| display->stop_scroll(); |
| return false; |
| } |
| |
| state->ff_rewind_count = ffwd_offset; |
| |
| for (i = 0; i < WPS_MAX_LINES; i++) |
| { |
| reset_subline = (data->curr_subline[i] == SUBLINE_RESET); |
| 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) || |
| 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->curr_subline[i]; |
| for (search=0; search<WPS_MAX_SUBLINES; search++) |
| { |
| data->curr_subline[i]++; |
| |
| /* wrap around if beyond last defined subline or WPS_MAX_SUBLINES */ |
| if ((!data->format_lines[i][data->curr_subline[i]]) || |
| (data->curr_subline[i] == WPS_MAX_SUBLINES)) |
| { |
| if (data->curr_subline[i] == 1) |
| only_one_subline = true; |
| data->curr_subline[i] = 0; |
| } |
| |
| /* if back where we started after search or |
| only one subline is defined on the line */ |
| if (((search > 0) && (data->curr_subline[i] == search_start)) || |
| only_one_subline) |
| { |
| /* no other subline with a time > 0 exists */ |
| data->subline_expire_time[i] = (reset_subline? |
| current_tick : data->subline_expire_time[i]) + 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]], |
| &format_align, |
| &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] = (reset_subline? |
| current_tick : data->subline_expire_time[i]) + |
| 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]], |
| &format_align, |
| &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) |
| { |
| int sb_y = i*h + offset + ((h > data->progress_height + 1) |
| ? (h - data->progress_height) / 2 : 1); |
| |
| if (!data->progress_end) |
| data->progress_end=display->width; |
| |
| if (gwps->data->progressbar.have_bitmap_pb) |
| gui_bitmap_scrollbar_draw(display, data->progressbar.bm, |
| data->progress_start, sb_y, |
| data->progress_end-data->progress_start, |
| data->progressbar.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, data->progress_start, sb_y, |
| data->progress_end-data->progress_start, |
| data->progress_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(display, state->id3->length, 0, sb_y, |
| data->progress_height); |
| #endif |
| update_line = true; |
| } |
| if (flags & refresh_mode & WPS_REFRESH_PEAK_METER) { |
| /* 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 <= display->height) { |
| /* found a line with a peak meter -> remember that we must |
| enable it later */ |
| enable_pm = true; |
| peak_meter_screen(gwps->display, 0, peak_meter_y, |
| MIN(h, display->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((unsigned char *)" ", &space_width, &string_height); |
| if (format_align.left != 0) { |
| display->getstringsize((unsigned char *)format_align.left, |
| &left_width, &string_height); |
| } |
| else { |
| left_width = 0; |
| } |
| left_xpos = 0; |
| |
| if (format_align.center != 0) { |
| display->getstringsize((unsigned char *)format_align.center, |
| ¢er_width, &string_height); |
| } |
| else { |
| center_width = 0; |
| } |
| center_xpos=(display->width - center_width) / 2; |
| |
| if (format_align.right != 0) { |
| display->getstringsize((unsigned char *)format_align.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 */ |
| *(--format_align.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 */ |
| format_align.left = format_align.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 */ |
| *(--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->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 */ |
| format_align.right = format_align.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 */ |
| *(--format_align.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 */ |
| format_align.left = format_align.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, |
| (unsigned char *)format_align.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, (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); |
| } |
| } |
| #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, (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); |
| } |
| #else |
| update_line = true; |
| display->puts(0, i, buf); |
| #endif |
| } |
| } |
| } |
| #ifdef HAVE_LCD_BITMAP |
| if (update_line) { |
| wps_display_images(gwps,false); |
| } |
| #endif |
| } |
| |
| #ifdef HAVE_LCD_BITMAP |
| /* Display all images */ |
| wps_display_images(gwps,true); |
| display->update(); |
| /* 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 |
| |
| #ifdef CONFIG_BACKLIGHT |
| 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)) && |
| (state->paused == false)) |
| backlight_on(); |
| } |
| #endif |
| #ifdef HAVE_REMOTE_LCD |
| if (global_settings.remote_caption_backlight && state->id3) { |
| /* 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 = backlight_timeout_value[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 |
| 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; |
| struct screen *display = gwps->display; |
| 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]; |
| } |
| } |
| display->define_pattern(gwps->data->wps_progress_pat[0], player_progressbar); |
| return true; |
| } |
| |
| static char map_fullbar_char(char ascii_val) |
| { |
| if (ascii_val >= '0' && ascii_val <= '9') { |
| return(ascii_val - '0'); |
| } |
| else if (ascii_val == ':') { |
| return(10); |
| } |
| else |
| return(11); /* anything besides a number or ':' is mapped to <blank> */ |
| } |
| |
| static void draw_player_fullbar(struct gui_wps *gwps, char* buf, int buf_size) |
| { |
| int i,j,lcd_char_pos; |
| |
| char player_progressbar[7]; |
| char binline[36]; |
| static const char numbers[12][4][3]={ |
| {{1,1,1},{1,0,1},{1,0,1},{1,1,1}},/*0*/ |
| {{0,1,0},{1,1,0},{0,1,0},{0,1,0}},/*1*/ |
| {{1,1,1},{0,0,1},{0,1,0},{1,1,1}},/*2*/ |
| {{1,1,1},{0,0,1},{0,1,1},{1,1,1}},/*3*/ |
| {{1,0,0},{1,1,0},{1,1,1},{0,1,0}},/*4*/ |
| {{1,1,1},{1,1,0},{0,0,1},{1,1,0}},/*5*/ |
| {{1,1,1},{1,0,0},{1,1,1},{1,1,1}},/*6*/ |
| {{1,1,1},{0,0,1},{0,1,0},{1,0,0}},/*7*/ |
| {{1,1,1},{1,1,1},{1,0,1},{1,1,1}},/*8*/ |
| {{1,1,1},{1,1,1},{0,0,1},{1,1,1}},/*9*/ |
| {{0,0,0},{0,1,0},{0,0,0},{0,1,0}},/*:*/ |
| {{0,0,0},{0,0,0},{0,0,0},{0,0,0}} /*<blank>*/ |
| }; |
| |
| int songpos = 0; |
| int digits[6]; |
| int time; |
| char timestr[7]; |
| |
| struct wps_state *state = gwps->state; |
| struct screen *display = gwps->display; |
| struct wps_data *data = gwps->data; |
| |
| 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]; |
| } |
| } |
| |
| display->define_pattern(data->wps_progress_pat[lcd_char_pos+1], |
| player_progressbar); |
| buf[lcd_char_pos]=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]; |
| } |
| } |
| |
| display->define_pattern(data->wps_progress_pat[7],player_progressbar); |
| |
| buf[songpos/5]=data->wps_progress_pat[7]; |
| } |
| } |
| #endif |
| |
| /* set volume */ |
| void 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); |
| settings_save(); |
| } |
| /* return true if screen restore is needed |
| return false otherwise |
| */ |
| bool update_onvol_change(struct gui_wps * gwps) |
| { |
| gui_wps_statusbar_draw(gwps, false); |
| gui_wps_refresh(gwps, 0, WPS_REFRESH_NON_STATIC); |
| |
| #ifdef HAVE_LCD_CHARCELLS |
| gui_splash(gwps->display,0, false, "Vol: %d dB ", |
| 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; |
| |
| 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; |
| |
| 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) |
| #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 = 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; |
| } |
| |
| FOR_NB_SCREENS(i) |
| gui_wps_refresh(&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 |
| 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 = get_action(CONTEXT_WPS,TIMEOUT_BLOCK); |
| } |
| action_signalscreenchange(); |
| 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_wps[i].display->clear_display(); |
| if (!gui_wps[i].data->wps_loaded) { |
| if ( !gui_wps[i].data->format_buffer[0] ) { |
| /* set the default wps for the main-screen */ |
| if(i == 0) |
| { |
| #ifdef HAVE_LCD_BITMAP |
| #ifdef HAVE_LCD_COLOR |
| unload_wps_backdrop(); |
| #endif |
| wps_data_load(gui_wps[i].data, |
| "%s%?it<%?in<%in. |>%it|%fn>\n" |
| "%s%?ia<%ia|%?d2<%d2|(root)>>\n" |
| "%s%?id<%id|%?d1<%d1|(root)>> %?iy<(%iy)|>\n" |
| "\n" |
| "%al%pc/%pt%ar[%pp:%pe]\n" |
| "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n" |
| "%pb\n" |
| "%pm\n", false); |
| #else |
| wps_data_load(gui_wps[i].data, |
| "%s%pp/%pe: %?it<%it|%fn> - %?ia<%ia|%d2> - %?id<%id|%d1>\n" |
| "%pc%?ps<*|/>%pt\n", false); |
| #endif |
| } |
| #if NB_SCREENS == 2 |
| /* set the default wps for the remote-screen */ |
| else if(i == 1) |
| { |
| wps_data_load(gui_wps[i].data, |
| "%s%?ia<%ia|%?d2<%d2|(root)>>\n" |
| "%s%?it<%?in<%in. |>%it|%fn>\n" |
| "%al%pc/%pt%ar[%pp:%pe]\n" |
| "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n" |
| "%pb", false); |
| } |
| #endif |
| } |
| } |
| } |
| } |
| yield(); |
| FOR_NB_SCREENS(i) |
| { |
| gui_wps_refresh(&gui_wps[i], 0, WPS_REFRESH_ALL); |
| } |
| 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_wps_statusbar_draw(gwps, false); |
| |
| return retcode; |
| } |
| |
| |
| void display_keylock_text(bool locked) |
| { |
| char* s; |
| int i; |
| FOR_NB_SCREENS(i) |
| 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); |
| } |
| |