remove numerical genre and use genre_string consistently:
[Rockbox.git] / apps / gui / gwps-common.c
blobd59ac130fc084aa8d86b4c681e7a4cb39688dcc3
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 Björn Stenberg
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
19 #include "gwps-common.h"
20 #include "font.h"
21 #include <stdio.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include "system.h"
25 #include "settings.h"
26 #ifdef CONFIG_RTC
27 #include "rtc.h"
28 #endif
29 #include "audio.h"
30 #include "status.h"
31 #include "power.h"
32 #include "powermgmt.h"
33 #include "sound.h"
34 #include "debug.h"
35 #ifdef HAVE_LCD_CHARCELLS
36 #include "hwcompat.h"
37 #endif
38 #include "abrepeat.h"
39 #include "mp3_playback.h"
40 #include "backlight.h"
41 #include "lang.h"
42 #include "misc.h"
43 #include "splash.h"
44 #include "scrollbar.h"
45 #include "led.h"
46 #include "lcd.h"
47 #ifdef HAVE_LCD_BITMAP
48 #include "peakmeter.h"
49 /* Image stuff */
50 #include "bmp.h"
51 #include "atoi.h"
52 #endif
53 #if LCD_DEPTH > 1
54 #include "backdrop.h"
55 #endif
56 #include "dsp.h"
57 #include "action.h"
58 #include "cuesheet.h"
60 #ifdef HAVE_LCD_CHARCELLS
61 static bool draw_player_progress(struct gui_wps *gwps);
62 static void draw_player_fullbar(struct gui_wps *gwps,
63 char* buf, int buf_size);
64 #endif
66 #define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */
67 /* 3% of 30min file == 54s step size */
68 #define MIN_FF_REWIND_STEP 500
70 /* Skip leading UTF-8 BOM, if present. */
71 static char* skip_utf8_bom(char* buf)
73 unsigned char* s = (unsigned char*) buf;
75 if(s[0] == 0xef && s[1] == 0xbb && s[2] == 0xbf)
77 buf += 3;
80 return buf;
84 * returns the image_id between
85 * a..z and A..Z
87 #ifdef HAVE_LCD_BITMAP
88 static int get_image_id(int c)
90 if(c >= 'a' && c <= 'z')
91 return c - 'a';
92 else if(c >= 'A' && c <= 'Z')
93 return c - 'A' + 26;
94 else
95 return -1;
97 #endif
100 * parse the given buffer for following static tags:
101 * %x - load image for always display
102 * %X - load backdrop image
103 * %xl - preload image
104 * %we - enable statusbar on wps regardless of the global setting
105 * %wd - disable statusbar on wps regardless of the global setting
106 * and also for:
107 * # - a comment line
109 * it returns true if one of these tags is found and handled
110 * false otherwise
112 bool wps_data_preload_tags(struct wps_data *data, char *buf,
113 const char *bmpdir, size_t bmpdirlen)
115 if(!data || !buf) return false;
117 char c;
118 #ifndef HAVE_LCD_BITMAP
119 /* no bitmap-lcd == no bitmap loading */
120 (void)bmpdir;
121 (void)bmpdirlen;
122 #endif
123 buf = skip_utf8_bom(buf);
125 if(*buf == '#')
126 return true;
127 if('%' != *buf)
128 return false;
129 buf++;
131 c = *buf;
132 switch (c)
134 #ifdef HAVE_LCD_BITMAP
135 case 'w':
137 * if tag found then return because these two tags must be on
138 * must be on their own line
140 if(*(buf+1) == 'd' || *(buf+1) == 'e')
142 data->wps_sb_tag = true;
143 if( *(buf+1) == 'e' )
144 data->show_sb_on_wps = true;
145 return true;
147 break;
149 #if LCD_DEPTH > 1
150 case 'X':
151 /* Backdrop image - must be the same size as the LCD */
153 char *ptr = buf+2;
154 char *pos = NULL;
155 char imgname[MAX_PATH];
157 /* format: %X|filename.bmp| */
159 /* get filename */
160 pos = strchr(ptr, '|');
161 if ((pos - ptr) <
162 (int)sizeof(imgname)-ROCKBOX_DIR_LEN-2)
164 memcpy(imgname, bmpdir, bmpdirlen);
165 imgname[bmpdirlen] = '/';
166 memcpy(&imgname[bmpdirlen+1],
167 ptr, pos - ptr);
168 imgname[bmpdirlen+1+pos-ptr] = 0;
170 else
172 /* filename too long */
173 imgname[0] = 0;
176 /* load the image */
177 return load_wps_backdrop(imgname);
180 break;
181 #endif
183 case 'P':
184 /* progress bar image */
186 int ret = 0;
187 char *ptr = buf+2;
188 char *pos = NULL;
189 char imgname[MAX_PATH];
191 /* format: %P|filename.bmp| */
193 /* get filename */
194 pos = strchr(ptr, '|');
195 if ((pos - ptr) <
196 (int)sizeof(imgname)-ROCKBOX_DIR_LEN-2)
198 memcpy(imgname, bmpdir, bmpdirlen);
199 imgname[bmpdirlen] = '/';
200 memcpy(&imgname[bmpdirlen+1],
201 ptr, pos - ptr);
202 imgname[bmpdirlen+1+pos-ptr] = 0;
204 else
205 /* filename too long */
206 imgname[0] = 0;
208 ptr = pos+1;
210 /* load the image */
211 data->progressbar.bm.data=data->img_buf_ptr;
212 ret = read_bmp_file(imgname, &data->progressbar.bm,
213 data->img_buf_free,
214 FORMAT_ANY|FORMAT_TRANSPARENT);
216 if (ret > 0)
218 #if LCD_DEPTH == 16
219 if (ret % 2) ret++;
220 /* Always consume an even number of bytes */
221 #endif
223 data->img_buf_ptr += ret;
224 data->img_buf_free -= ret;
226 if (data->progressbar.bm.width <= LCD_WIDTH) {
227 data->progressbar.have_bitmap_pb=true;
228 return true;
229 } else
230 return false;
236 break;
238 case 'x':
239 /* Preload images so the %xd# tag can display it */
241 int ret = 0;
242 int n;
243 char *ptr = buf+1;
244 char *pos = NULL;
245 char imgname[MAX_PATH];
246 char qual = *ptr;
248 if (qual == 'l' || qual == '|') /* format:
249 %x|n|filename.bmp|x|y|
251 %xl|n|filename.bmp|x|y|
254 ptr = strchr(ptr, '|') + 1;
255 pos = strchr(ptr, '|');
256 if (pos)
258 /* get the image ID */
259 n = get_image_id(*ptr);
261 if(n < 0 || n >= MAX_IMAGES)
263 /* Skip the rest of the line */
264 while(*buf != '\n')
265 buf++;
266 return false;
268 ptr = pos+1;
270 /* check the image number and load state */
271 if (data->img[n].loaded)
273 /* Skip the rest of the line */
274 while(*buf != '\n')
275 buf++;
276 return false;
278 else
280 /* get filename */
281 pos = strchr(ptr, '|');
283 if (pos == NULL)
284 return false;
286 if ((pos - ptr) <
287 (int)sizeof(imgname)-ROCKBOX_DIR_LEN-2)
289 memcpy(imgname, bmpdir, bmpdirlen);
290 imgname[bmpdirlen] = '/';
291 memcpy(&imgname[bmpdirlen+1],
292 ptr, pos - ptr);
293 imgname[bmpdirlen+1+pos-ptr] = 0;
295 else
296 /* filename too long */
297 imgname[0] = 0;
299 ptr = pos+1;
301 /* get x-position */
302 pos = strchr(ptr, '|');
303 if (pos)
304 data->img[n].x = atoi(ptr);
305 else
307 /* weird syntax, bail out */
308 buf++;
309 return false;
312 /* get y-position */
313 ptr = pos+1;
314 pos = strchr(ptr, '|');
315 if (pos)
316 data->img[n].y = atoi(ptr);
317 else
319 /* weird syntax, bail out */
320 buf++;
321 return false;
324 /* load the image */
325 data->img[n].bm.data = data->img_buf_ptr;
326 ret = read_bmp_file(imgname, &data->img[n].bm,
327 data->img_buf_free,
328 FORMAT_ANY|FORMAT_TRANSPARENT);
330 if (ret > 0)
332 #if LCD_DEPTH == 16
333 if (ret % 2) ret++;
334 /* Always consume an even number of bytes */
335 #endif
337 data->img_buf_ptr += ret;
338 data->img_buf_free -= ret;
339 data->img[n].loaded = true;
340 if(qual == '|')
341 data->img[n].always_display = true;
343 return true;
349 break;
350 #endif
352 /* no of these tags found */
353 return false;
357 /* draws the statusbar on the given wps-screen */
358 #ifdef HAVE_LCD_BITMAP
359 static void gui_wps_statusbar_draw(struct gui_wps *wps, bool force)
361 bool draw = global_settings.statusbar;
363 if(wps->data->wps_sb_tag
364 && wps->data->show_sb_on_wps)
365 draw = true;
366 else if(wps->data->wps_sb_tag)
367 draw = false;
368 if(draw)
369 gui_statusbar_draw(wps->statusbar, force);
371 #else
372 #define gui_wps_statusbar_draw(wps, force) \
373 gui_statusbar_draw((wps)->statusbar, (force))
374 #endif
376 /* Extract a part from a path.
378 * buf - buffer extract part to.
379 * buf_size - size of buffer.
380 * path - path to extract from.
381 * level - what to extract. 0 is file name, 1 is parent of file, 2 is
382 * parent of parent, etc.
384 * Returns buf if the desired level was found, NULL otherwise.
386 static char* get_dir(char* buf, int buf_size, const char* path, int level)
388 const char* sep;
389 const char* last_sep;
390 int len;
392 sep = path + strlen(path);
393 last_sep = sep;
395 while (sep > path)
397 if ('/' == *(--sep))
399 if (!level)
401 break;
404 level--;
405 last_sep = sep - 1;
409 if (level || (last_sep <= sep))
411 return NULL;
414 len = MIN(last_sep - sep, buf_size - 1);
415 strncpy(buf, sep + 1, len);
416 buf[len] = 0;
417 return buf;
420 /* Get the tag specified by the two characters at fmt.
422 * cid3 - ID3 data to get tag values from.
423 * nid3 - next-song ID3 data to get tag values from.
424 * tag - string (of two characters) specifying the tag to get.
425 * buf - buffer to certain tags, such as track number, play time or
426 * directory name.
427 * buf_size - size of buffer.
428 * flags - returns the type of the line. See constants i wps-display.h
430 * Returns the tag. NULL indicates the tag wasn't available.
432 static char* get_tag(struct wps_data* wps_data,
433 struct mp3entry* cid3,
434 struct mp3entry* nid3,
435 const char* tag,
436 char* buf,
437 int buf_size,
438 unsigned char* tag_len,
439 unsigned short* subline_time_mult,
440 unsigned char* flags,
441 int *intval)
443 struct mp3entry *id3 = cid3; /* default to current song */
444 int limit = *intval;
445 #ifndef HAVE_LCD_CHARCELLS
446 (void)wps_data;
447 #endif
448 if ((0 == tag[0]) || (0 == tag[1]))
450 *tag_len = 0;
451 return NULL;
454 *tag_len = 2;
456 *intval = 0;
458 switch (tag[0])
460 case 'I': /* ID3 Information */
461 id3 = nid3; /* display next-song data */
462 *flags |= WPS_REFRESH_DYNAMIC;
463 if(!id3)
464 return NULL; /* no such info (yet) */
465 /* fall-through */
466 case 'i': /* ID3 Information */
467 *flags |= WPS_REFRESH_STATIC;
468 switch (tag[1])
470 case 't': /* ID3 Title */
471 return id3->title;
473 case 'a': /* ID3 Artist */
474 return id3->artist;
476 case 'n': /* ID3 Track Number */
477 if (id3->track_string)
478 return id3->track_string;
480 if (id3->tracknum) {
481 snprintf(buf, buf_size, "%d", id3->tracknum);
482 return buf;
484 return NULL;
486 case 'd': /* ID3 Album/Disc */
487 return id3->album;
489 case 'c': /* ID3 Composer */
490 return id3->composer;
492 case 'C': /* ID3 Comment */
493 return id3->comment;
495 case 'A': /* ID3 Albumartist */
496 return id3->albumartist;
498 case 'y': /* year */
499 if( id3->year_string )
500 return id3->year_string;
502 if (id3->year) {
503 snprintf(buf, buf_size, "%d", id3->year);
504 return buf;
506 return NULL;
508 case 'g': /* genre */
509 return id3->genre_string;
511 case 'v': /* id3 version */
512 switch (id3->id3version)
514 case ID3_VER_1_0:
515 return "1";
517 case ID3_VER_1_1:
518 return "1.1";
520 case ID3_VER_2_2:
521 return "2.2";
523 case ID3_VER_2_3:
524 return "2.3";
526 case ID3_VER_2_4:
527 return "2.4";
529 default:
530 return NULL;
533 break;
535 case 'F': /* File Information */
536 id3 = nid3;
537 *flags |= WPS_REFRESH_DYNAMIC;
538 if(!id3)
539 return NULL; /* no such info (yet) */
540 /* fall-through */
541 case 'f': /* File Information */
542 *flags |= WPS_REFRESH_STATIC;
543 switch(tag[1])
545 case 'v': /* VBR file? */
546 return id3->vbr ? "(avg)" : NULL;
548 case 'b': /* File Bitrate */
549 if(id3->bitrate)
550 snprintf(buf, buf_size, "%d", id3->bitrate);
551 else
552 snprintf(buf, buf_size, "?");
553 return buf;
555 case 'f': /* File Frequency */
556 snprintf(buf, buf_size, "%ld", id3->frequency);
557 return buf;
559 case 'p': /* File Path */
560 return id3->path;
562 case 'm': /* File Name - With Extension */
563 return get_dir(buf, buf_size, id3->path, 0);
565 case 'n': /* File Name */
566 if (get_dir(buf, buf_size, id3->path, 0))
568 /* Remove extension */
569 char* sep = strrchr(buf, '.');
571 if (NULL != sep)
573 *sep = 0;
576 return buf;
578 else
580 return NULL;
583 case 's': /* File Size (in kilobytes) */
584 snprintf(buf, buf_size, "%ld", id3->filesize / 1024);
585 return buf;
587 case 'c': /* File Codec */
588 if(id3->codectype == AFMT_UNKNOWN)
589 *intval = AFMT_NUM_CODECS;
590 else
591 *intval = id3->codectype;
592 return id3_get_codec(id3);
594 break;
596 case 'p': /* Playlist/Song Information */
597 switch(tag[1])
599 case 'b': /* progress bar */
600 *flags |= WPS_REFRESH_PLAYER_PROGRESS;
601 #ifdef HAVE_LCD_CHARCELLS
602 snprintf(buf, buf_size, "%c",
603 wps_data->wps_progress_pat[0]);
604 wps_data->full_line_progressbar=0;
605 return buf;
606 #else
607 /* default values : */
608 wps_data->progress_top = -1;
609 wps_data->progress_height = 6;
610 wps_data->progress_start = 0;
611 wps_data->progress_end = 0;
613 char *prev=strchr(tag, '|');
614 if (prev) {
615 char *p=strchr(prev+1, '|');
616 if (p) {
617 wps_data->progress_height=atoi(++prev);
618 prev=strchr(prev, '|');
619 p=strchr(++p, '|');
620 if (p) {
621 wps_data->progress_start=atoi(++prev);
622 prev=strchr(prev, '|');
623 p=strchr(++p, '|');
624 if (p) {
625 wps_data->progress_end=atoi(++prev);
626 prev=strchr(prev, '|');
627 p=strchr(++p, '|');
628 if(p)
629 wps_data->progress_top = atoi(++prev);
632 if (wps_data->progress_height<3)
633 wps_data->progress_height=3;
634 if (wps_data->progress_end<wps_data->progress_start+3)
635 wps_data->progress_end=0;
639 return "\x01";
640 #endif
641 case 'f': /* full-line progress bar */
642 #ifdef HAVE_LCD_CHARCELLS
643 if(is_new_player()) {
644 *flags |= WPS_REFRESH_PLAYER_PROGRESS;
645 *flags |= WPS_REFRESH_DYNAMIC;
646 wps_data->full_line_progressbar=1;
647 /* we need 11 characters (full line) for
648 progress-bar */
649 snprintf(buf, buf_size, " ");
651 else
653 /* Tell the user if we have an OldPlayer */
654 snprintf(buf, buf_size, " <Old LCD> ");
656 return buf;
657 #endif
658 case 'p': /* Playlist Position */
659 *flags |= WPS_REFRESH_STATIC;
660 snprintf(buf, buf_size, "%d",
661 playlist_get_display_index());
662 return buf;
664 case 'n': /* Playlist Name (without path) */
665 *flags |= WPS_REFRESH_STATIC;
666 return playlist_name(NULL, buf, buf_size);
668 case 'e': /* Playlist Total Entries */
669 *flags |= WPS_REFRESH_STATIC;
670 snprintf(buf, buf_size, "%d", playlist_amount());
671 return buf;
673 case 'c': /* Current Time in Song */
674 *flags |= WPS_REFRESH_DYNAMIC;
675 format_time(buf, buf_size,
676 id3->elapsed + wps_state.ff_rewind_count);
677 return buf;
679 case 'r': /* Remaining Time in Song */
680 *flags |= WPS_REFRESH_DYNAMIC;
681 format_time(buf, buf_size,
682 id3->length - id3->elapsed -
683 wps_state.ff_rewind_count);
684 return buf;
686 case 't': /* Total Time */
687 *flags |= WPS_REFRESH_STATIC;
688 format_time(buf, buf_size, id3->length);
689 return buf;
691 #ifdef HAVE_LCD_BITMAP
692 case 'm': /* Peak Meter */
693 *flags |= WPS_REFRESH_PEAK_METER;
694 return "\x01";
695 #endif
696 case 's': /* shuffle */
697 *flags |= WPS_REFRESH_DYNAMIC;
698 if ( global_settings.playlist_shuffle )
699 return "s";
700 else
701 return NULL;
702 break;
704 case 'v': /* volume */
705 *flags |= WPS_REFRESH_DYNAMIC;
706 snprintf(buf, buf_size, "%d", global_settings.volume);
707 *intval = limit * (global_settings.volume
708 - sound_min(SOUND_VOLUME))
709 / (sound_max(SOUND_VOLUME)
710 - sound_min(SOUND_VOLUME)) + 1;
711 return buf;
714 break;
716 #if (CONFIG_CODEC == SWCODEC)
717 case 'S': /* DSP/Equalizer/Sound settings */
718 switch (tag[1])
720 case 'p': /* pitch */
721 *intval = sound_get_pitch();
722 snprintf(buf, buf_size, "%d.%d",
723 *intval / 10, *intval % 10);
724 return buf;
726 break;
727 #endif
729 case 'm':
730 switch (tag[1])
732 case 'm': /* playback repeat mode */
733 *flags |= WPS_REFRESH_DYNAMIC;
734 *intval = global_settings.repeat_mode + 1;
735 snprintf(buf, buf_size, "%d", *intval);
736 return buf;
738 /* playback status */
739 case 'p': /* play */
740 *flags |= WPS_REFRESH_DYNAMIC;
741 int status = audio_status();
742 *intval = 1;
743 if (status == AUDIO_STATUS_PLAY && \
744 !(status & AUDIO_STATUS_PAUSE))
745 *intval = 2;
746 if (audio_status() & AUDIO_STATUS_PAUSE && \
747 (! status_get_ffmode()))
748 *intval = 3;
749 if (status_get_ffmode() == STATUS_FASTFORWARD)
750 *intval = 4;
751 if (status_get_ffmode() == STATUS_FASTBACKWARD)
752 *intval = 5;
753 snprintf(buf, buf_size, "%d", *intval);
754 return buf;
756 #ifdef HAS_BUTTON_HOLD
757 case 'h': /* hold */
758 *flags |= WPS_REFRESH_DYNAMIC;
759 if (button_hold())
760 return "h";
761 else
762 return NULL;
763 #endif
764 #ifdef HAS_REMOTE_BUTTON_HOLD
765 case 'r': /* remote hold */
766 *flags |= WPS_REFRESH_DYNAMIC;
767 if (remote_button_hold())
768 return "r";
769 else
770 return NULL;
771 #endif
773 break;
775 case 'b': /* battery info */
776 *flags |= WPS_REFRESH_DYNAMIC;
777 switch (tag[1])
779 case 'l': /* battery level */
781 int l = battery_level();
782 limit = MAX(limit, 2);
783 if (l > -1)
785 snprintf(buf, buf_size, "%d", l);
786 /* First enum is used for "unknown level". */
787 *intval = (limit - 1) * l / 100 + 1 + 1;
789 else
791 *intval = 1;
792 return "?";
794 return buf;
797 case 'v': /* battery voltage */
799 unsigned int v = battery_voltage();
800 snprintf(buf, buf_size, "%d.%02d", v/100, v%100);
801 return buf;
804 case 't': /* estimated battery time */
806 int t = battery_time();
807 if (t >= 0)
808 snprintf(buf, buf_size, "%dh %dm", t / 60, t % 60);
809 else
810 strncpy(buf, "?h ?m", buf_size);
811 return buf;
814 case 's': /* sleep timer */
816 if (get_sleep_timer() == 0)
818 return NULL;
820 else
822 format_time(buf, buf_size, \
823 get_sleep_timer() * 1000);
824 return buf;
828 #if CONFIG_CHARGING
829 case 'p': /* External power plugged in? */
831 if(charger_input_state==CHARGER)
832 return "p";
833 else
834 return NULL;
836 #endif
837 #if CONFIG_CHARGING >= CHARGING_MONITOR
838 case 'c': /* Charging */
840 if (charge_state == CHARGING || charge_state == TOPOFF) {
841 return "c";
842 } else {
843 return NULL;
846 #endif
848 break;
850 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
851 case 'l': /* VIRTUAL_LED */
853 switch(tag[1])
855 case 'h': /* Only one we have so far HDD LED */
856 *flags |= WPS_REFRESH_DYNAMIC;
857 if(led_read(HZ/2))
858 return "h";
859 else
860 return NULL;
863 break;
864 #endif
866 case 'D': /* Directory path information */
867 id3 = nid3; /* next song please! */
868 *flags |= WPS_REFRESH_DYNAMIC;
869 if(!id3)
870 return NULL; /* no such info (yet) */
871 /* fall-through */
872 case 'd': /* Directory path information */
874 int level = tag[1] - '0';
875 *flags |= WPS_REFRESH_STATIC;
876 /* d1 through d9 */
877 if ((0 < level) && (9 > level))
879 return get_dir(buf, buf_size, id3->path, level);
882 break;
884 case 't': /* set sub line time multiplier */
886 int d = 1;
887 int time_mult = 0;
888 bool have_point = false;
889 bool have_tenth = false;
891 while (((tag[d] >= '0') &&
892 (tag[d] <= '9')) ||
893 (tag[d] == '.'))
895 if (tag[d] != '.')
897 time_mult = time_mult * 10;
898 time_mult = time_mult + tag[d] - '0';
899 if (have_point)
901 have_tenth = true;
902 d++;
903 break;
906 else
908 have_point = true;
910 d++;
913 if (have_tenth == false)
914 time_mult *= 10;
916 *subline_time_mult = time_mult;
917 *tag_len = d;
919 buf[0] = 0;
920 return buf;
922 break;
923 case 'r': /* Runtime database Information and Replaygain */
924 switch(tag[1])
926 case 'p': /* Playcount */
927 *flags |= WPS_REFRESH_STATIC;
928 snprintf(buf, buf_size, "%ld", cid3->playcount);
929 return buf;
930 case 'r': /* Rating */
931 *flags |= WPS_REFRESH_STATIC;
932 *intval = cid3->rating+1;
933 snprintf(buf, buf_size, "%d", cid3->rating);
934 return buf;
935 #if CONFIG_CODEC == SWCODEC
936 case 'g': /* ReplayGain */
937 *flags |= WPS_REFRESH_STATIC;
938 if (global_settings.replaygain == 0)
939 *intval = 1; /* off */
940 else
942 int type = get_replaygain_mode(
943 id3->track_gain_string != NULL,
944 id3->album_gain_string != NULL);
946 if (type < 0)
947 *intval = 6; /* no tag */
948 else
949 *intval = type + 2;
951 if (global_settings.replaygain_type == REPLAYGAIN_SHUFFLE)
952 *intval += 2;
955 switch (*intval)
957 case 1:
958 case 6:
959 return "+0.00 dB";
960 break;
961 case 2:
962 case 4:
963 strncpy(buf, id3->track_gain_string, buf_size);
964 break;
965 case 3:
966 case 5:
967 strncpy(buf, id3->album_gain_string, buf_size);
968 break;
970 return buf;
971 #endif
973 break;
974 #ifdef CONFIG_RTC
975 case 'c': /* Real Time Clock display */
976 *flags |= WPS_REFRESH_DYNAMIC;
977 #if CONFIG_RTC == RTC_DS1339_DS3231
978 if(!rtc_detected)
979 return NULL;
980 else
981 #endif
983 int value;
984 char *format = 0;
985 char *bufptr = buf;
986 struct tm* tm = get_time();
987 int i;
988 for (i=1;/*break*/;i++) {
989 switch(tag[i])
991 case 'a': /* abbreviated weekday name (Sun..Sat) */
992 value = tm->tm_wday;
993 if (value > 6 || value < 0) continue;
994 value = snprintf(
995 bufptr,buf_size,"%s",str(dayname[value]));
996 bufptr += value;
997 buf_size -= value;
998 continue;
999 case 'b': /* abbreviated month name (Jan..Dec) */
1000 value = tm->tm_mon;
1001 if (value > 11 || value < 0) continue;
1002 value = snprintf(
1003 bufptr,buf_size,"%s",str(monthname[value]));
1004 bufptr += value;
1005 buf_size -= value;
1006 continue;
1007 case 'd': /* day of month (01..31) */
1008 value = tm->tm_mday;
1009 if (value > 31 || value < 1) continue;
1010 format = "%02d";
1011 break;
1012 case 'e': /* day of month, blank padded ( 1..31) */
1013 value = tm->tm_mday;
1014 if (value > 31 || value < 1) continue;
1015 format = "%2d";
1016 break;
1017 case 'H': /* hour (00..23) */
1018 value = tm->tm_hour;
1019 if (value > 23) continue;
1020 format = "%02d";
1021 break;
1022 case 'k': /* hour ( 0..23) */
1023 value = tm->tm_hour;
1024 if (value > 23) continue;
1025 format = "%2d";
1026 break;
1027 case 'I': /* hour (01..12) */
1028 value = tm->tm_hour;
1029 if (value > 23) continue;
1030 value %= 12;
1031 if (value == 0) value = 12;
1032 format = "%02d";
1033 break;
1034 case 'l': /* hour ( 1..12) */
1035 value = tm->tm_hour;
1036 if (value > 23 || value < 0) continue;
1037 value %= 12;
1038 if (value == 0) value = 12;
1039 format = "%2d";
1040 break;
1041 case 'm': /* month (01..12) */
1042 value = tm->tm_mon;
1043 if (value > 11 || value < 0) continue;
1044 value++;
1045 format = "%02d";
1046 break;
1047 case 'M': /* minute (00..59) */
1048 value = tm->tm_min;
1049 if (value > 59 || value < 0) continue;
1050 format = "%02d";
1051 break;
1052 case 'S': /* second (00..59) */
1053 value = tm->tm_sec;
1054 if (value > 59 || value < 0) continue;
1055 format = "%02d";
1056 break;
1057 case 'y': /* last two digits of year (00..99) */
1058 value = tm->tm_year;
1059 value %= 100;
1060 format = "%02d";
1061 break;
1062 case 'Y': /* year (1970...) */
1063 value = tm->tm_year;
1064 if (value > 199 || value < 100) continue;
1065 value += 1900;
1066 format = "%04d";
1067 break;
1068 case 'p': /* upper case AM or PM indicator */
1069 if (tm->tm_hour/12 == 0) format = "AM";
1070 else format = "PM";
1071 snprintf(bufptr,buf_size,"%s",format);
1072 bufptr += 2;
1073 buf_size -= 2;
1074 continue;
1075 case 'P': /* lower case am or pm indicator */
1076 if (tm->tm_hour/12 == 0) format = "am";
1077 else format = "pm";
1078 snprintf(bufptr,buf_size,"%s",format);
1079 bufptr += 2;
1080 buf_size -= 2;
1081 continue;
1082 case 'u': /* day of week (1..7); 1 is Monday */
1083 value = tm->tm_wday;
1084 if (value < 0 || value > 6) continue;
1085 value++;
1086 format = "%1d";
1087 break;
1088 case 'w': /* day of week (0..6); 0 is Sunday */
1089 value = tm->tm_wday;
1090 if (value < 0 || value > 6) continue;
1091 format = "%1d";
1092 break;
1093 default:
1094 if (tag[i] == 'c') {
1095 i++;
1096 value = -1;
1097 break;
1098 } else if (tag[i] == '\n') {
1099 value = -1;
1100 break;
1102 snprintf(bufptr,buf_size,"%c",tag[i]);
1103 bufptr++;
1104 buf_size--;
1105 continue;
1106 } /* switch */
1107 if (value < 0) break;
1109 value = snprintf(bufptr, buf_size, format, value);
1110 bufptr += value;
1111 buf_size -= value;
1112 } /* while */
1113 *tag_len = i;
1114 return buf;
1116 #endif /* CONFIG_RTC */
1117 #if CONFIG_CODEC == SWCODEC
1118 case 'x':
1119 *flags |= WPS_REFRESH_DYNAMIC;
1120 switch(tag[1])
1122 case 'd': /* crossfeed */
1123 if(global_settings.crossfeed)
1124 return "d";
1125 else
1126 return NULL;
1127 case 'f': /* crossfade */
1128 *intval = global_settings.crossfade+1;
1129 snprintf(buf, buf_size, "%d", global_settings.crossfade);
1130 return buf;
1132 break;
1133 #endif
1135 return NULL;
1138 #ifdef HAVE_LCD_BITMAP
1139 /* clears the area where the image was shown */
1140 static void clear_image_pos(struct gui_wps *gwps, int n)
1142 if(!gwps)
1143 return;
1144 struct wps_data *data = gwps->data;
1145 gwps->display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1146 gwps->display->fillrect(data->img[n].x, data->img[n].y,
1147 data->img[n].bm.width, data->img[n].bm.height);
1148 gwps->display->set_drawmode(DRMODE_SOLID);
1150 #endif
1152 /* Skip to the end of the current %? conditional.
1154 * fmt - string to skip it. Should point to somewhere after the leading
1155 * "<" char (and before or at the last ">").
1156 * num - number of |'s to skip, or 0 to skip to the end (the ">").
1157 * enums - If not NULL, set to the number of |'s found in the current
1158 * conditional (sub-conditionals are ignored). num should be 0
1159 * to find all |'s.
1161 * Returns the new position in fmt.
1163 static const char* skip_conditional(struct gui_wps *gwps, const char* fmt,
1164 int num, int *enums)
1166 int level = 1;
1167 int count = num;
1168 const char *last_alternative = NULL;
1169 #ifdef HAVE_LCD_BITMAP
1170 struct wps_data *data = NULL;
1171 int last_x=-1, last_y=-1, last_w=-1, last_h=-1;
1172 if(gwps)
1173 data = gwps->data;
1174 if (enums)
1175 *enums = 0;
1176 #else
1177 (void)gwps;
1178 #endif
1179 while (*fmt)
1181 switch (*fmt++)
1183 case '%':
1184 #ifdef HAVE_LCD_BITMAP
1185 if(data && *(fmt) == 'x' && *(fmt+1) == 'd' )
1187 fmt +=2;
1188 int n = *fmt;
1189 if(n >= 'a' && n <= 'z')
1190 n -= 'a';
1191 if(n >= 'A' && n <= 'Z')
1192 n = n - 'A' + 26;
1193 if(last_x != data->img[n].x || last_y != data->img[n].y
1194 || last_w != data->img[n].bm.width
1195 || last_h != data->img[n].bm.height)
1197 last_x = data->img[n].x;
1198 last_y = data->img[n].y;
1199 last_w = data->img[n].bm.width;
1200 last_h = data->img[n].bm.height;
1201 clear_image_pos(gwps,n);
1204 #endif
1205 break;
1207 case '|':
1208 if(1 == level) {
1209 if (enums)
1210 (*enums)++;
1211 last_alternative = fmt;
1212 if(num) {
1213 count--;
1214 if(count == 0)
1215 return fmt;
1216 continue;
1219 continue;
1221 case '>':
1222 if (0 == --level)
1224 /* We're just skipping to the end */
1225 if(num == 0)
1226 return fmt;
1228 /* If we are parsing an enum, we'll return the selected
1229 item. If there weren't enough items in the enum, we'll
1230 return the last one found. */
1231 if(count && last_alternative)
1233 return last_alternative;
1235 return fmt - 1;
1237 continue;
1239 default:
1240 continue;
1243 switch (*fmt++)
1245 case 0:
1246 case '%':
1247 case '|':
1248 case '<':
1249 case '>':
1250 break;
1252 case '?':
1253 while (*fmt && ('<' != *fmt))
1254 fmt++;
1256 if ('<' == *fmt)
1257 fmt++;
1259 level++;
1260 break;
1262 default:
1263 break;
1267 return fmt;
1270 /* Generate the display based on id3 information and format string.
1272 * buf - char buffer to write the display to.
1273 * buf_size - the size of buffer.
1274 * id3 - the ID3 data to format with.
1275 * nid3 - the ID3 data of the next song (might by NULL)
1276 * fmt - format description.
1277 * flags - returns the type of the line. See constants i wps-display.h
1279 static void format_display(struct gui_wps *gwps, char* buf,
1280 int buf_size,
1281 struct mp3entry* id3,
1282 struct mp3entry* nid3, /* next song's id3 */
1283 const char* fmt,
1284 struct align_pos* align,
1285 unsigned short* subline_time_mult,
1286 unsigned char* flags)
1288 char temp_buf[128];
1289 char* buf_start = buf;
1290 char* buf_end = buf + buf_size - 1; /* Leave room for end null */
1291 char* value = NULL;
1292 int level = 0;
1293 unsigned char tag_length;
1294 int intval;
1295 int cur_align;
1296 char* cur_align_start;
1297 #ifdef HAVE_LCD_BITMAP
1298 struct gui_img *img = gwps->data->img;
1299 int n;
1300 #endif
1302 cur_align_start = buf;
1303 cur_align = WPS_ALIGN_LEFT;
1304 *subline_time_mult = DEFAULT_SUBLINE_TIME_MULTIPLIER;
1306 align->left = 0;
1307 align->center = 0;
1308 align->right = 0;
1310 while (fmt && *fmt && buf < buf_end)
1312 switch (*fmt)
1314 case '%':
1315 ++fmt;
1316 break;
1318 case '|':
1319 case '>':
1320 if (level > 0)
1322 fmt = skip_conditional(NULL, fmt, 0, NULL);
1323 level--;
1324 continue;
1326 /* Else fall through */
1328 default:
1329 *buf++ = *fmt++;
1330 continue;
1333 switch (*fmt)
1335 case 0:
1336 *buf++ = '%';
1337 break;
1338 case 'a':
1339 ++fmt;
1340 /* remember where the current aligned text started */
1341 switch (cur_align)
1343 case WPS_ALIGN_LEFT:
1344 align->left = cur_align_start;
1345 break;
1347 case WPS_ALIGN_CENTER:
1348 align->center = cur_align_start;
1349 break;
1351 case WPS_ALIGN_RIGHT:
1352 align->right = cur_align_start;
1353 break;
1355 /* start a new alignment */
1356 switch (*fmt)
1358 case 'l':
1359 cur_align = WPS_ALIGN_LEFT;
1360 break;
1361 case 'c':
1362 cur_align = WPS_ALIGN_CENTER;
1363 break;
1364 case 'r':
1365 cur_align = WPS_ALIGN_RIGHT;
1366 break;
1368 *buf++=0;
1369 cur_align_start = buf;
1370 ++fmt;
1371 break;
1372 case 's':
1373 *flags |= WPS_REFRESH_SCROLL;
1374 ++fmt;
1375 break;
1377 case 'x': /* image support */
1378 #ifdef HAVE_LCD_BITMAP
1379 if ('d' == *(fmt+1) )
1381 fmt+=2;
1383 /* get the image ID */
1384 n = *fmt;
1385 if(n >= 'a' && n <= 'z')
1386 n -= 'a';
1387 if(n >= 'A' && n <= 'Z')
1388 n = n - 'A' + 26;
1389 if (n >= 0 && n < MAX_IMAGES && img[n].loaded) {
1390 img[n].display = true;
1394 #endif
1395 fmt++;
1396 break;
1399 case '%':
1400 case '|':
1401 case '<':
1402 case '>':
1403 case ';':
1404 *buf++ = *fmt++;
1405 break;
1407 case '?':
1408 fmt++;
1409 /* Get number of "|" chars in the current conditional;
1410 * used by get_tag when calculating levels.
1412 skip_conditional(gwps, fmt, 0, &intval);
1413 value = get_tag(gwps->data, id3, nid3, fmt, temp_buf,
1414 sizeof(temp_buf),&tag_length,
1415 subline_time_mult, flags, &intval);
1417 while (*fmt && ('<' != *fmt))
1418 fmt++;
1420 if ('<' == *fmt)
1421 fmt++;
1423 /* No value, so skip to else part, using a sufficiently high
1424 value to "hit" the last part of the conditional */
1425 if ((!value) || (!strlen(value)))
1426 fmt = skip_conditional(NULL, fmt, 1000, NULL);
1427 else
1428 if(intval > 1) /* enum */
1429 fmt = skip_conditional(NULL, fmt, intval - 1, NULL);
1431 level++;
1432 break;
1434 default:
1435 intval = 1;
1436 value = get_tag(gwps->data, id3, nid3, fmt, temp_buf,
1437 sizeof(temp_buf), &tag_length,
1438 subline_time_mult, flags,&intval);
1439 fmt += tag_length;
1441 if (value)
1443 while (*value && (buf < buf_end))
1444 *buf++ = *value++;
1449 /* remember where the current aligned text started */
1450 switch (cur_align)
1452 case WPS_ALIGN_LEFT:
1453 align->left = cur_align_start;
1454 break;
1456 case WPS_ALIGN_CENTER:
1457 align->center = cur_align_start;
1458 break;
1460 case WPS_ALIGN_RIGHT:
1461 align->right = cur_align_start;
1462 break;
1465 *buf = 0;
1467 /* if resulting line is an empty line, set the subline time to 0 */
1468 if (buf - buf_start == 0)
1469 *subline_time_mult = 0;
1471 /* If no flags have been set, the line didn't contain any format codes.
1472 We still want to refresh it. */
1473 if(*flags == 0)
1474 *flags = WPS_REFRESH_STATIC;
1477 /* fades the volume */
1478 void fade(bool fade_in)
1480 int fp_global_vol = global_settings.volume << 8;
1481 int fp_min_vol = sound_min(SOUND_VOLUME) << 8;
1482 int fp_step = (fp_global_vol - fp_min_vol) / 30;
1484 if (fade_in) {
1485 /* fade in */
1486 int fp_volume = fp_min_vol;
1488 /* zero out the sound */
1489 sound_set_volume(fp_min_vol >> 8);
1491 sleep(HZ/10); /* let audio thread run */
1492 audio_resume();
1494 while (fp_volume < fp_global_vol - fp_step) {
1495 fp_volume += fp_step;
1496 sound_set_volume(fp_volume >> 8);
1497 sleep(1);
1499 sound_set_volume(global_settings.volume);
1501 else {
1502 /* fade out */
1503 int fp_volume = fp_global_vol;
1505 while (fp_volume > fp_min_vol + fp_step) {
1506 fp_volume -= fp_step;
1507 sound_set_volume(fp_volume >> 8);
1508 sleep(1);
1510 audio_pause();
1511 #ifndef SIMULATOR
1512 /* let audio thread run and wait for the mas to run out of data */
1513 while (!mp3_pause_done())
1514 #endif
1515 sleep(HZ/10);
1517 /* reset volume to what it was before the fade */
1518 sound_set_volume(global_settings.volume);
1522 /* Set format string to use for WPS, splitting it into lines */
1523 void gui_wps_format(struct wps_data *data)
1525 char* buf = data->format_buffer;
1526 char* start_of_line = data->format_buffer;
1527 int line = 0;
1528 int subline;
1529 char c;
1530 if(!data)
1531 return;
1533 for (line=0; line<WPS_MAX_LINES; line++)
1535 for (subline=0; subline<WPS_MAX_SUBLINES; subline++)
1537 data->format_lines[line][subline] = 0;
1538 data->time_mult[line][subline] = 0;
1540 data->subline_expire_time[line] = 0;
1541 data->curr_subline[line] = SUBLINE_RESET;
1544 line = 0;
1545 subline = 0;
1546 buf = skip_utf8_bom(buf);
1547 data->format_lines[line][subline] = buf;
1549 while ((*buf) && (line < WPS_MAX_LINES))
1551 c = *buf;
1553 switch (c)
1556 * skip % sequences so "%;" doesn't start a new subline
1557 * don't skip %x lines (pre-load bitmaps)
1559 case '%':
1560 buf++;
1561 break;
1563 case '\r': /* CR */
1564 *buf = 0;
1565 break;
1567 case '\n': /* LF */
1568 *buf = 0;
1570 if (*start_of_line != '#') /* A comment? */
1571 line++;
1573 if (line < WPS_MAX_LINES)
1575 /* the next line starts on the next byte */
1576 subline = 0;
1577 data->format_lines[line][subline] = buf+1;
1578 start_of_line = data->format_lines[line][subline];
1580 break;
1582 case ';': /* start a new subline */
1583 *buf = 0;
1584 subline++;
1585 if (subline < WPS_MAX_SUBLINES)
1587 data->format_lines[line][subline] = buf+1;
1589 else /* exceeded max sublines, skip rest of line */
1591 while (*(++buf))
1593 if ((*buf == '\r') || (*buf == '\n'))
1595 break;
1598 buf--;
1599 subline = 0;
1601 break;
1603 buf++;
1607 #ifdef HAVE_LCD_BITMAP
1608 /* Display images */
1609 static void wps_draw_image(struct gui_wps *gwps, int n)
1611 struct screen *display = gwps->display;
1612 struct wps_data *data = gwps->data;
1613 if(data->img[n].always_display)
1614 display->set_drawmode(DRMODE_FG);
1615 else
1616 display->set_drawmode(DRMODE_SOLID);
1618 #if LCD_DEPTH > 1
1619 if(data->img[n].bm.format == FORMAT_MONO) {
1620 #endif
1621 display->mono_bitmap(data->img[n].bm.data, data->img[n].x,
1622 data->img[n].y, data->img[n].bm.width,
1623 data->img[n].bm.height);
1624 #if LCD_DEPTH > 1
1625 } else {
1626 display->transparent_bitmap((fb_data *)data->img[n].bm.data,
1627 data->img[n].x,
1628 data->img[n].y, data->img[n].bm.width,
1629 data->img[n].bm.height);
1631 #endif
1633 static void wps_display_images(struct gui_wps *gwps, bool always)
1635 if(!gwps || !gwps->data || !gwps->display) return;
1636 int n;
1637 struct wps_data *data = gwps->data;
1638 struct screen *display = gwps->display;
1639 for (n = 0; n < MAX_IMAGES; n++) {
1640 if (data->img[n].loaded) {
1641 if( (!always && data->img[n].display)
1642 || (always && data->img[n].always_display) )
1643 wps_draw_image(gwps, n);
1646 display->set_drawmode(DRMODE_SOLID);
1648 #endif
1650 #if 0 /* currently unused */
1651 void gui_wps_reset(struct gui_wps *gui_wps)
1653 if(!gui_wps || !gui_wps->data)
1654 return;
1655 gui_wps->data->wps_loaded = false;
1656 memset(&gui_wps->data->format_buffer, 0,
1657 sizeof(gui_wps->data->format_buffer));
1659 #endif
1661 bool gui_wps_refresh(struct gui_wps *gwps, int ffwd_offset,
1662 unsigned char refresh_mode)
1664 char buf[MAX_PATH];
1665 unsigned char flags;
1666 int i;
1667 bool update_line;
1668 bool only_one_subline;
1669 bool new_subline_refresh;
1670 bool reset_subline;
1671 int search;
1672 int search_start;
1673 struct align_pos format_align;
1674 struct wps_data *data = gwps->data;
1675 struct wps_state *state = gwps->state;
1676 struct screen *display = gwps->display;
1677 if(!gwps || !data || !state || !display)
1679 return false;
1681 #ifdef HAVE_LCD_BITMAP
1682 int h = font_get(FONT_UI)->height;
1683 int offset = 0;
1684 gui_wps_statusbar_draw(gwps, true);
1685 if(data->wps_sb_tag && data->show_sb_on_wps)
1686 offset = STATUSBAR_HEIGHT;
1687 else if ( global_settings.statusbar && !data->wps_sb_tag)
1688 offset = STATUSBAR_HEIGHT;
1690 /* to find out wether the peak meter is enabled we
1691 assume it wasn't until we find a line that contains
1692 the peak meter. We can't use peak_meter_enabled itself
1693 because that would mean to turn off the meter thread
1694 temporarily. (That shouldn't matter unless yield
1695 or sleep is called but who knows...)
1697 bool enable_pm = false;
1699 /* Set images to not to be displayed */
1700 for (i = 0; i < MAX_IMAGES; i++) {
1701 data->img[i].display = false;
1703 #endif
1704 /* reset to first subline if refresh all flag is set */
1705 if (refresh_mode == WPS_REFRESH_ALL)
1707 for (i=0; i<WPS_MAX_LINES; i++)
1709 data->curr_subline[i] = SUBLINE_RESET;
1713 #ifdef HAVE_LCD_CHARCELLS
1714 for (i=0; i<8; i++) {
1715 if (data->wps_progress_pat[i]==0)
1716 data->wps_progress_pat[i]=display->get_locked_pattern();
1718 #endif
1720 if (!state->id3)
1722 display->stop_scroll();
1723 return false;
1726 state->ff_rewind_count = ffwd_offset;
1728 for (i = 0; i < WPS_MAX_LINES; i++)
1730 reset_subline = (data->curr_subline[i] == SUBLINE_RESET);
1731 new_subline_refresh = false;
1732 only_one_subline = false;
1734 /* if time to advance to next sub-line */
1735 if (TIME_AFTER(current_tick, data->subline_expire_time[i] - 1) ||
1736 reset_subline)
1738 /* search all sublines until the next subline with time > 0
1739 is found or we get back to the subline we started with */
1740 if (reset_subline)
1741 search_start = 0;
1742 else
1743 search_start = data->curr_subline[i];
1744 for (search=0; search<WPS_MAX_SUBLINES; search++)
1746 data->curr_subline[i]++;
1748 /* wrap around if beyond last defined subline or WPS_MAX_SUBLINES */
1749 if ((!data->format_lines[i][data->curr_subline[i]]) ||
1750 (data->curr_subline[i] == WPS_MAX_SUBLINES))
1752 if (data->curr_subline[i] == 1)
1753 only_one_subline = true;
1754 data->curr_subline[i] = 0;
1757 /* if back where we started after search or
1758 only one subline is defined on the line */
1759 if (((search > 0) && (data->curr_subline[i] == search_start)) ||
1760 only_one_subline)
1762 /* no other subline with a time > 0 exists */
1763 data->subline_expire_time[i] = (reset_subline?
1764 current_tick : data->subline_expire_time[i]) + 100 * HZ;
1765 break;
1767 else
1769 /* get initial time multiplier and
1770 line type flags for this subline */
1771 format_display(gwps, buf, sizeof(buf),
1772 state->id3, state->nid3,
1773 data->format_lines[i][data->curr_subline[i]],
1774 &format_align,
1775 &data->time_mult[i][data->curr_subline[i]],
1776 &data->line_type[i][data->curr_subline[i]]);
1778 /* only use this subline if subline time > 0 */
1779 if (data->time_mult[i][data->curr_subline[i]] > 0)
1781 new_subline_refresh = true;
1782 data->subline_expire_time[i] = (reset_subline?
1783 current_tick : data->subline_expire_time[i]) +
1784 BASE_SUBLINE_TIME * data->time_mult[i][data->curr_subline[i]];
1785 break;
1792 update_line = false;
1794 if ( !data->format_lines[i][data->curr_subline[i]] )
1795 break;
1797 if ((data->line_type[i][data->curr_subline[i]] & refresh_mode) ||
1798 (refresh_mode == WPS_REFRESH_ALL) ||
1799 new_subline_refresh)
1801 flags = 0;
1802 #ifdef HAVE_LCD_BITMAP
1803 int left_width, left_xpos;
1804 int center_width, center_xpos;
1805 int right_width, right_xpos;
1806 int space_width;
1807 int string_height;
1808 int ypos;
1809 #endif
1811 format_display(gwps, buf, sizeof(buf),
1812 state->id3, state->nid3,
1813 data->format_lines[i][data->curr_subline[i]],
1814 &format_align,
1815 &data->time_mult[i][data->curr_subline[i]],
1816 &flags);
1817 data->line_type[i][data->curr_subline[i]] = flags;
1819 #ifdef HAVE_LCD_BITMAP
1820 /* progress */
1821 if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1823 int sb_y;
1824 if (data->progress_top == -1)
1825 sb_y = i*h + offset + ((h > data->progress_height + 1)
1826 ? (h - data->progress_height) / 2 : 1);
1827 else
1828 sb_y = data->progress_top;
1830 if (!data->progress_end)
1831 data->progress_end=display->width;
1833 if (gwps->data->progressbar.have_bitmap_pb)
1834 gui_bitmap_scrollbar_draw(display, data->progressbar.bm,
1835 data->progress_start, sb_y,
1836 data->progress_end-data->progress_start,
1837 data->progressbar.bm.height,
1838 state->id3->length?state->id3->length:1, 0,
1839 state->id3->length?state->id3->elapsed + state->ff_rewind_count:0,
1840 HORIZONTAL);
1841 else
1842 gui_scrollbar_draw(display, data->progress_start, sb_y,
1843 data->progress_end-data->progress_start,
1844 data->progress_height,
1845 state->id3->length?state->id3->length:1, 0,
1846 state->id3->length?state->id3->elapsed + state->ff_rewind_count:0,
1847 HORIZONTAL);
1848 #ifdef AB_REPEAT_ENABLE
1849 if ( ab_repeat_mode_enabled() )
1850 ab_draw_markers(display, state->id3->length,
1851 data->progress_start, data->progress_end, sb_y,
1852 data->progress_height);
1853 #endif
1855 if (cuesheet_is_enabled() && state->id3->cuesheet_type)
1857 cue_draw_markers(display, state->id3->length,
1858 data->progress_start, data->progress_end,
1859 sb_y+1, data->progress_height-2);
1862 update_line = true;
1864 if (flags & refresh_mode & WPS_REFRESH_PEAK_METER) {
1865 /* peak meter */
1866 int peak_meter_y;
1868 update_line = true;
1869 peak_meter_y = i * h + offset;
1871 /* The user might decide to have the peak meter in the last
1872 line so that it is only displayed if no status bar is
1873 visible. If so we neither want do draw nor enable the
1874 peak meter. */
1875 if (peak_meter_y + h <= display->height) {
1876 /* found a line with a peak meter -> remember that we must
1877 enable it later */
1878 enable_pm = true;
1879 peak_meter_screen(gwps->display, 0, peak_meter_y,
1880 MIN(h, display->height - peak_meter_y));
1883 #else
1884 /* progress */
1885 if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS) {
1886 if (data->full_line_progressbar)
1887 draw_player_fullbar(gwps, buf, sizeof(buf));
1888 else
1889 draw_player_progress(gwps);
1891 #endif
1892 #ifdef HAVE_LCD_BITMAP
1893 /* calculate different string sizes and positions */
1894 display->getstringsize((unsigned char *)" ", &space_width, &string_height);
1895 if (format_align.left != 0) {
1896 display->getstringsize((unsigned char *)format_align.left,
1897 &left_width, &string_height);
1899 else {
1900 left_width = 0;
1902 left_xpos = 0;
1904 if (format_align.center != 0) {
1905 display->getstringsize((unsigned char *)format_align.center,
1906 &center_width, &string_height);
1908 else {
1909 center_width = 0;
1911 center_xpos=(display->width - center_width) / 2;
1913 if (format_align.right != 0) {
1914 display->getstringsize((unsigned char *)format_align.right,
1915 &right_width, &string_height);
1917 else {
1918 right_width = 0;
1920 right_xpos = (display->width - right_width);
1922 /* Checks for overlapping strings.
1923 If needed the overlapping strings will be merged, separated by a
1924 space */
1926 /* CASE 1: left and centered string overlap */
1927 /* there is a left string, need to merge left and center */
1928 if ((left_width != 0 && center_width != 0) &&
1929 (left_xpos + left_width + space_width > center_xpos)) {
1930 /* replace the former separator '\0' of left and
1931 center string with a space */
1932 *(--format_align.center) = ' ';
1933 /* calculate the new width and position of the merged string */
1934 left_width = left_width + space_width + center_width;
1935 left_xpos = 0;
1936 /* there is no centered string anymore */
1937 center_width = 0;
1939 /* there is no left string, move center to left */
1940 if ((left_width == 0 && center_width != 0) &&
1941 (left_xpos + left_width > center_xpos)) {
1942 /* move the center string to the left string */
1943 format_align.left = format_align.center;
1944 /* calculate the new width and position of the string */
1945 left_width = center_width;
1946 left_xpos = 0;
1947 /* there is no centered string anymore */
1948 center_width = 0;
1951 /* CASE 2: centered and right string overlap */
1952 /* there is a right string, need to merge center and right */
1953 if ((center_width != 0 && right_width != 0) &&
1954 (center_xpos + center_width + space_width > right_xpos)) {
1955 /* replace the former separator '\0' of center and
1956 right string with a space */
1957 *(--format_align.right) = ' ';
1958 /* move the center string to the right after merge */
1959 format_align.right = format_align.center;
1960 /* calculate the new width and position of the merged string */
1961 right_width = center_width + space_width + right_width;
1962 right_xpos = (display->width - right_width);
1963 /* there is no centered string anymore */
1964 center_width = 0;
1966 /* there is no right string, move center to right */
1967 if ((center_width != 0 && right_width == 0) &&
1968 (center_xpos + center_width > right_xpos)) {
1969 /* move the center string to the right string */
1970 format_align.right = format_align.center;
1971 /* calculate the new width and position of the string */
1972 right_width = center_width;
1973 right_xpos = (display->width - right_width);
1974 /* there is no centered string anymore */
1975 center_width = 0;
1978 /* CASE 3: left and right overlap
1979 There is no center string anymore, either there never
1980 was one or it has been merged in case 1 or 2 */
1981 /* there is a left string, need to merge left and right */
1982 if ((left_width != 0 && center_width == 0 && right_width != 0) &&
1983 (left_xpos + left_width + space_width > right_xpos)) {
1984 /* replace the former separator '\0' of left and
1985 right string with a space */
1986 *(--format_align.right) = ' ';
1987 /* calculate the new width and position of the string */
1988 left_width = left_width + space_width + right_width;
1989 left_xpos = 0;
1990 /* there is no right string anymore */
1991 right_width = 0;
1993 /* there is no left string, move right to left */
1994 if ((left_width == 0 && center_width == 0 && right_width != 0) &&
1995 (left_xpos + left_width > right_xpos)) {
1996 /* move the right string to the left string */
1997 format_align.left = format_align.right;
1998 /* calculate the new width and position of the string */
1999 left_width = right_width;
2000 left_xpos = 0;
2001 /* there is no right string anymore */
2002 right_width = 0;
2005 #endif
2007 if (flags & WPS_REFRESH_SCROLL) {
2009 /* scroll line */
2010 if ((refresh_mode & WPS_REFRESH_SCROLL) ||
2011 new_subline_refresh) {
2012 #ifdef HAVE_LCD_BITMAP
2013 ypos = (i*string_height)+display->getymargin();
2014 update_line = true;
2016 if (left_width>display->width) {
2017 display->puts_scroll(0, i,
2018 (unsigned char *)format_align.left);
2019 } else {
2020 /* clear the line first */
2021 display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
2022 display->fillrect(0, ypos, display->width, string_height);
2023 display->set_drawmode(DRMODE_SOLID);
2025 /* Nasty hack: we output an empty scrolling string,
2026 which will reset the scroller for that line */
2027 display->puts_scroll(0, i, (unsigned char *)"");
2029 /* print aligned strings */
2030 if (left_width != 0)
2032 display->putsxy(left_xpos, ypos,
2033 (unsigned char *)format_align.left);
2035 if (center_width != 0)
2037 display->putsxy(center_xpos, ypos,
2038 (unsigned char *)format_align.center);
2040 if (right_width != 0)
2042 display->putsxy(right_xpos, ypos,
2043 (unsigned char *)format_align.right);
2046 #else
2047 display->puts_scroll(0, i, buf);
2048 update_line = true;
2049 #endif
2052 else if (flags & (WPS_REFRESH_DYNAMIC | WPS_REFRESH_STATIC))
2054 /* dynamic / static line */
2055 if ((refresh_mode & (WPS_REFRESH_DYNAMIC|WPS_REFRESH_STATIC)) ||
2056 new_subline_refresh)
2058 #ifdef HAVE_LCD_BITMAP
2059 ypos = (i*string_height)+display->getymargin();
2060 update_line = true;
2062 /* clear the line first */
2063 display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
2064 display->fillrect(0, ypos, display->width, string_height);
2065 display->set_drawmode(DRMODE_SOLID);
2067 /* Nasty hack: we output an empty scrolling string,
2068 which will reset the scroller for that line */
2069 display->puts_scroll(0, i, (unsigned char *)"");
2071 /* print aligned strings */
2072 if (left_width != 0)
2074 display->putsxy(left_xpos, ypos,
2075 (unsigned char *)format_align.left);
2077 if (center_width != 0)
2079 display->putsxy(center_xpos, ypos,
2080 (unsigned char *)format_align.center);
2082 if (right_width != 0)
2084 display->putsxy(right_xpos, ypos,
2085 (unsigned char *)format_align.right);
2087 #else
2088 update_line = true;
2089 display->puts(0, i, buf);
2090 #endif
2094 #ifdef HAVE_LCD_BITMAP
2095 if (update_line) {
2096 wps_display_images(gwps,false);
2098 #endif
2101 #ifdef HAVE_LCD_BITMAP
2102 /* Display all images */
2103 wps_display_images(gwps,true);
2104 display->update();
2105 /* Now we know wether the peak meter is used.
2106 So we can enable / disable the peak meter thread */
2107 data->peak_meter_enabled = enable_pm;
2108 #endif
2110 #if CONFIG_BACKLIGHT
2111 if (global_settings.caption_backlight && state->id3) {
2112 /* turn on backlight n seconds before track ends, and turn it off n
2113 seconds into the new track. n == backlight_timeout, or 5s */
2114 int n = backlight_timeout_value[global_settings.backlight_timeout]
2115 * 1000;
2117 if ( n < 1000 )
2118 n = 5000; /* use 5s if backlight is always on or off */
2120 if (((state->id3->elapsed < 1000) ||
2121 ((state->id3->length - state->id3->elapsed) < (unsigned)n)) &&
2122 (state->paused == false))
2123 backlight_on();
2125 #endif
2126 #ifdef HAVE_REMOTE_LCD
2127 if (global_settings.remote_caption_backlight && state->id3) {
2128 /* turn on remote backlight n seconds before track ends, and turn it
2129 off n seconds into the new track. n == remote_backlight_timeout,
2130 or 5s */
2131 int n = backlight_timeout_value[global_settings.remote_backlight_timeout]
2132 * 1000;
2134 if ( n < 1000 )
2135 n = 5000; /* use 5s if backlight is always on or off */
2137 if (((state->id3->elapsed < 1000) ||
2138 ((state->id3->length - state->id3->elapsed) < (unsigned)n)) &&
2139 (state->paused == false))
2140 remote_backlight_on();
2142 #endif
2143 return true;
2146 #ifdef HAVE_LCD_CHARCELLS
2147 static bool draw_player_progress(struct gui_wps *gwps)
2149 char player_progressbar[7];
2150 char binline[36];
2151 int songpos = 0;
2152 int i,j;
2153 struct wps_state *state = gwps->state;
2154 struct screen *display = gwps->display;
2155 if (!state->id3)
2156 return false;
2158 memset(binline, 1, sizeof binline);
2159 memset(player_progressbar, 1, sizeof player_progressbar);
2161 if(state->id3->elapsed >= state->id3->length)
2162 songpos = 0;
2163 else
2165 if(state->wps_time_countup == false)
2166 songpos = ((state->id3->elapsed - state->ff_rewind_count) * 36) /
2167 state->id3->length;
2168 else
2169 songpos = ((state->id3->elapsed + state->ff_rewind_count) * 36) /
2170 state->id3->length;
2172 for (i=0; i < songpos; i++)
2173 binline[i] = 0;
2175 for (i=0; i<=6; i++) {
2176 for (j=0;j<5;j++) {
2177 player_progressbar[i] <<= 1;
2178 player_progressbar[i] += binline[i*5+j];
2181 display->define_pattern(gwps->data->wps_progress_pat[0], player_progressbar);
2182 return true;
2185 static char map_fullbar_char(char ascii_val)
2187 if (ascii_val >= '0' && ascii_val <= '9') {
2188 return(ascii_val - '0');
2190 else if (ascii_val == ':') {
2191 return(10);
2193 else
2194 return(11); /* anything besides a number or ':' is mapped to <blank> */
2197 static void draw_player_fullbar(struct gui_wps *gwps, char* buf, int buf_size)
2199 int i,j,lcd_char_pos;
2201 char player_progressbar[7];
2202 char binline[36];
2203 static const char numbers[12][4][3]={
2204 {{1,1,1},{1,0,1},{1,0,1},{1,1,1}},/*0*/
2205 {{0,1,0},{1,1,0},{0,1,0},{0,1,0}},/*1*/
2206 {{1,1,1},{0,0,1},{0,1,0},{1,1,1}},/*2*/
2207 {{1,1,1},{0,0,1},{0,1,1},{1,1,1}},/*3*/
2208 {{1,0,0},{1,1,0},{1,1,1},{0,1,0}},/*4*/
2209 {{1,1,1},{1,1,0},{0,0,1},{1,1,0}},/*5*/
2210 {{1,1,1},{1,0,0},{1,1,1},{1,1,1}},/*6*/
2211 {{1,1,1},{0,0,1},{0,1,0},{1,0,0}},/*7*/
2212 {{1,1,1},{1,1,1},{1,0,1},{1,1,1}},/*8*/
2213 {{1,1,1},{1,1,1},{0,0,1},{1,1,1}},/*9*/
2214 {{0,0,0},{0,1,0},{0,0,0},{0,1,0}},/*:*/
2215 {{0,0,0},{0,0,0},{0,0,0},{0,0,0}} /*<blank>*/
2218 int songpos = 0;
2219 int digits[6];
2220 int time;
2221 char timestr[7];
2223 struct wps_state *state = gwps->state;
2224 struct screen *display = gwps->display;
2225 struct wps_data *data = gwps->data;
2227 for (i=0; i < buf_size; i++)
2228 buf[i] = ' ';
2230 if(state->id3->elapsed >= state->id3->length)
2231 songpos = 55;
2232 else {
2233 if(state->wps_time_countup == false)
2234 songpos = ((state->id3->elapsed - state->ff_rewind_count) * 55) /
2235 state->id3->length;
2236 else
2237 songpos = ((state->id3->elapsed + state->ff_rewind_count) * 55) /
2238 state->id3->length;
2241 time=(state->id3->elapsed + state->ff_rewind_count);
2243 memset(timestr, 0, sizeof(timestr));
2244 format_time(timestr, sizeof(timestr), time);
2245 for(lcd_char_pos=0; lcd_char_pos<6; lcd_char_pos++) {
2246 digits[lcd_char_pos] = map_fullbar_char(timestr[lcd_char_pos]);
2249 /* build the progressbar-icons */
2250 for (lcd_char_pos=0; lcd_char_pos<6; lcd_char_pos++) {
2251 memset(binline, 0, sizeof binline);
2252 memset(player_progressbar, 0, sizeof player_progressbar);
2254 /* make the character (progressbar & digit)*/
2255 for (i=0; i<7; i++) {
2256 for (j=0;j<5;j++) {
2257 /* make the progressbar */
2258 if (lcd_char_pos==(songpos/5)) {
2259 /* partial */
2260 if ((j<(songpos%5))&&(i>4))
2261 binline[i*5+j] = 1;
2262 else
2263 binline[i*5+j] = 0;
2265 else {
2266 if (lcd_char_pos<(songpos/5)) {
2267 /* full character */
2268 if (i>4)
2269 binline[i*5+j] = 1;
2272 /* insert the digit */
2273 if ((j<3)&&(i<4)) {
2274 if (numbers[digits[lcd_char_pos]][i][j]==1)
2275 binline[i*5+j] = 1;
2280 for (i=0; i<=6; i++) {
2281 for (j=0;j<5;j++) {
2282 player_progressbar[i] <<= 1;
2283 player_progressbar[i] += binline[i*5+j];
2287 display->define_pattern(data->wps_progress_pat[lcd_char_pos+1],
2288 player_progressbar);
2289 buf[lcd_char_pos]=data->wps_progress_pat[lcd_char_pos+1];
2293 /* make rest of the progressbar if necessary */
2294 if (songpos/5>5) {
2296 /* set the characters positions that use the full 5 pixel wide bar */
2297 for (lcd_char_pos=6; lcd_char_pos < (songpos/5); lcd_char_pos++)
2298 buf[lcd_char_pos] = 0x86; /* '_' */
2300 /* build the partial bar character for the tail character position */
2301 memset(binline, 0, sizeof binline);
2302 memset(player_progressbar, 0, sizeof player_progressbar);
2304 for (i=5; i<7; i++) {
2305 for (j=0;j<5;j++) {
2306 if (j<(songpos%5)) {
2307 binline[i*5+j] = 1;
2312 for (i=0; i<7; i++) {
2313 for (j=0;j<5;j++) {
2314 player_progressbar[i] <<= 1;
2315 player_progressbar[i] += binline[i*5+j];
2319 display->define_pattern(data->wps_progress_pat[7],player_progressbar);
2321 buf[songpos/5]=data->wps_progress_pat[7];
2324 #endif
2326 /* set volume */
2327 void setvol(void)
2329 if (global_settings.volume < sound_min(SOUND_VOLUME))
2330 global_settings.volume = sound_min(SOUND_VOLUME);
2331 if (global_settings.volume > sound_max(SOUND_VOLUME))
2332 global_settings.volume = sound_max(SOUND_VOLUME);
2333 sound_set_volume(global_settings.volume);
2334 settings_save();
2336 /* return true if screen restore is needed
2337 return false otherwise
2339 bool update_onvol_change(struct gui_wps * gwps)
2341 gui_wps_statusbar_draw(gwps, false);
2342 gui_wps_refresh(gwps, 0, WPS_REFRESH_NON_STATIC);
2344 #ifdef HAVE_LCD_CHARCELLS
2345 gui_splash(gwps->display,0, false, "Vol: %d dB ",
2346 sound_val2phys(SOUND_VOLUME, global_settings.volume));
2347 return true;
2348 #endif
2349 return false;
2352 bool ffwd_rew(int button)
2354 static const int ff_rew_steps[] = {
2355 1000, 2000, 3000, 4000,
2356 5000, 6000, 8000, 10000,
2357 15000, 20000, 25000, 30000,
2358 45000, 60000
2361 unsigned int step = 0; /* current ff/rewind step */
2362 unsigned int max_step = 0; /* maximum ff/rewind step */
2363 int ff_rewind_count = 0; /* current ff/rewind count (in ticks) */
2364 int direction = -1; /* forward=1 or backward=-1 */
2365 long accel_tick = 0; /* next time at which to bump the step size */
2366 bool exit = false;
2367 bool usb = false;
2368 int i = 0;
2370 if (button == ACTION_NONE)
2372 status_set_ffmode(0);
2373 return usb;
2375 while (!exit)
2377 switch ( button )
2379 case ACTION_WPS_SEEKFWD:
2380 direction = 1;
2381 case ACTION_WPS_SEEKBACK:
2382 if (wps_state.ff_rewind)
2384 if (direction == 1)
2386 /* fast forwarding, calc max step relative to end */
2387 max_step = (wps_state.id3->length -
2388 (wps_state.id3->elapsed +
2389 ff_rewind_count)) *
2390 FF_REWIND_MAX_PERCENT / 100;
2392 else
2394 /* rewinding, calc max step relative to start */
2395 max_step = (wps_state.id3->elapsed + ff_rewind_count) *
2396 FF_REWIND_MAX_PERCENT / 100;
2399 max_step = MAX(max_step, MIN_FF_REWIND_STEP);
2401 if (step > max_step)
2402 step = max_step;
2404 ff_rewind_count += step * direction;
2406 if (global_settings.ff_rewind_accel != 0 &&
2407 current_tick >= accel_tick)
2409 step *= 2;
2410 accel_tick = current_tick +
2411 global_settings.ff_rewind_accel*HZ;
2414 else
2416 if ( (audio_status() & AUDIO_STATUS_PLAY) &&
2417 wps_state.id3 && wps_state.id3->length )
2419 if (!wps_state.paused)
2420 #if (CONFIG_CODEC == SWCODEC)
2421 audio_pre_ff_rewind();
2422 #else
2423 audio_pause();
2424 #endif
2425 #if CONFIG_KEYPAD == PLAYER_PAD
2426 FOR_NB_SCREENS(i)
2427 gui_wps[i].display->stop_scroll();
2428 #endif
2429 if (direction > 0)
2430 status_set_ffmode(STATUS_FASTFORWARD);
2431 else
2432 status_set_ffmode(STATUS_FASTBACKWARD);
2434 wps_state.ff_rewind = true;
2436 step = ff_rew_steps[global_settings.ff_rewind_min_step];
2438 accel_tick = current_tick +
2439 global_settings.ff_rewind_accel*HZ;
2441 else
2442 break;
2445 if (direction > 0) {
2446 if ((wps_state.id3->elapsed + ff_rewind_count) >
2447 wps_state.id3->length)
2448 ff_rewind_count = wps_state.id3->length -
2449 wps_state.id3->elapsed;
2451 else {
2452 if ((int)(wps_state.id3->elapsed + ff_rewind_count) < 0)
2453 ff_rewind_count = -wps_state.id3->elapsed;
2456 FOR_NB_SCREENS(i)
2457 gui_wps_refresh(&gui_wps[i],
2458 (wps_state.wps_time_countup == false)?
2459 ff_rewind_count:-ff_rewind_count,
2460 WPS_REFRESH_PLAYER_PROGRESS |
2461 WPS_REFRESH_DYNAMIC);
2463 break;
2465 case ACTION_WPS_STOPSEEK:
2466 wps_state.id3->elapsed = wps_state.id3->elapsed+ff_rewind_count;
2467 audio_ff_rewind(wps_state.id3->elapsed);
2468 ff_rewind_count = 0;
2469 wps_state.ff_rewind = false;
2470 status_set_ffmode(0);
2471 #if (CONFIG_CODEC != SWCODEC)
2472 if (!wps_state.paused)
2473 audio_resume();
2474 #endif
2475 #ifdef HAVE_LCD_CHARCELLS
2476 gui_wps_display();
2477 #endif
2478 exit = true;
2479 break;
2481 default:
2482 if(default_event_handler(button) == SYS_USB_CONNECTED) {
2483 status_set_ffmode(0);
2484 usb = true;
2485 exit = true;
2487 break;
2489 if (!exit)
2490 button = get_action(CONTEXT_WPS,TIMEOUT_BLOCK);
2492 action_signalscreenchange();
2493 return usb;
2496 bool gui_wps_display(void)
2498 int i;
2499 if (!wps_state.id3 && !(audio_status() & AUDIO_STATUS_PLAY))
2501 global_status.resume_index = -1;
2502 #ifdef HAVE_LCD_CHARCELLS
2503 gui_syncsplash(HZ, true, str(LANG_END_PLAYLIST_PLAYER));
2504 #else
2505 gui_syncstatusbar_draw(&statusbars, true);
2506 gui_syncsplash(HZ, true, str(LANG_END_PLAYLIST_RECORDER));
2507 #endif
2508 return true;
2510 else
2512 FOR_NB_SCREENS(i)
2514 gui_wps[i].display->clear_display();
2515 if (!gui_wps[i].data->wps_loaded) {
2516 if ( !gui_wps[i].data->format_buffer[0] ) {
2517 /* set the default wps for the main-screen */
2518 if(i == 0)
2520 #ifdef HAVE_LCD_BITMAP
2521 #if LCD_DEPTH > 1
2522 unload_wps_backdrop();
2523 #endif
2524 wps_data_load(gui_wps[i].data,
2525 "%s%?it<%?in<%in. |>%it|%fn>\n"
2526 "%s%?ia<%ia|%?d2<%d2|(root)>>\n"
2527 "%s%?id<%id|%?d1<%d1|(root)>> %?iy<(%iy)|>\n"
2528 "\n"
2529 "%al%pc/%pt%ar[%pp:%pe]\n"
2530 "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n"
2531 "%pb\n"
2532 "%pm\n", false);
2533 #else
2534 wps_data_load(gui_wps[i].data,
2535 "%s%pp/%pe: %?it<%it|%fn> - %?ia<%ia|%d2> - %?id<%id|%d1>\n"
2536 "%pc%?ps<*|/>%pt\n", false);
2537 #endif
2539 #if NB_SCREENS == 2
2540 /* set the default wps for the remote-screen */
2541 else if(i == 1)
2543 wps_data_load(gui_wps[i].data,
2544 "%s%?ia<%ia|%?d2<%d2|(root)>>\n"
2545 "%s%?it<%?in<%in. |>%it|%fn>\n"
2546 "%al%pc/%pt%ar[%pp:%pe]\n"
2547 "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n"
2548 "%pb", false);
2550 #endif
2555 yield();
2556 FOR_NB_SCREENS(i)
2558 gui_wps_refresh(&gui_wps[i], 0, WPS_REFRESH_ALL);
2560 return false;
2563 bool update(struct gui_wps *gwps)
2565 bool track_changed = audio_has_changed_track();
2566 bool retcode = false;
2568 gwps->state->nid3 = audio_next_track();
2569 if (track_changed)
2571 gwps->display->stop_scroll();
2572 gwps->state->id3 = audio_current_track();
2574 if (cuesheet_is_enabled() && gwps->state->id3->cuesheet_type
2575 && strcmp(gwps->state->id3->path, curr_cue->audio_filename))
2577 /* the current cuesheet isn't the right one any more */
2579 if (!strcmp(gwps->state->id3->path, temp_cue->audio_filename)) {
2580 /* We have the new cuesheet in memory (temp_cue),
2581 let's make it the current one ! */
2582 memcpy(curr_cue, temp_cue, sizeof(struct cuesheet));
2584 else {
2585 /* We need to parse the new cuesheet */
2587 char cuepath[MAX_PATH];
2588 strncpy(cuepath, gwps->state->id3->path, MAX_PATH);
2589 char *dot = strrchr(cuepath, '.');
2590 strcpy(dot, ".cue");
2592 if (parse_cuesheet(cuepath, curr_cue))
2594 gwps->state->id3->cuesheet_type = 1;
2595 strcpy(curr_cue->audio_filename, gwps->state->id3->path);
2599 cue_spoof_id3(curr_cue, gwps->state->id3);
2602 if (gui_wps_display())
2603 retcode = true;
2604 else{
2605 gui_wps_refresh(gwps, 0, WPS_REFRESH_ALL);
2608 if (gwps->state->id3)
2609 memcpy(gwps->state->current_track_path, gwps->state->id3->path,
2610 sizeof(gwps->state->current_track_path));
2613 if (gwps->state->id3)
2615 if (cuesheet_is_enabled() && gwps->state->id3->cuesheet_type
2616 && (gwps->state->id3->elapsed < curr_cue->curr_track->offset
2617 || (curr_cue->curr_track_idx < curr_cue->track_count - 1
2618 && gwps->state->id3->elapsed >= (curr_cue->curr_track+1)->offset)))
2620 /* We've changed tracks within the cuesheet :
2621 we need to update the ID3 info and refresh the WPS */
2623 cue_find_current_track(curr_cue, gwps->state->id3->elapsed);
2624 cue_spoof_id3(curr_cue, gwps->state->id3);
2626 gwps->display->stop_scroll();
2627 if (gui_wps_display())
2628 retcode = true;
2629 else
2630 gui_wps_refresh(gwps, 0, WPS_REFRESH_ALL);
2632 else
2633 gui_wps_refresh(gwps, 0, WPS_REFRESH_NON_STATIC);
2636 gui_wps_statusbar_draw(gwps, false);
2638 return retcode;
2642 void display_keylock_text(bool locked)
2644 char* s;
2645 int i;
2646 FOR_NB_SCREENS(i)
2647 gui_wps[i].display->stop_scroll();
2649 #ifdef HAVE_LCD_CHARCELLS
2650 if(locked)
2651 s = str(LANG_KEYLOCK_ON_PLAYER);
2652 else
2653 s = str(LANG_KEYLOCK_OFF_PLAYER);
2654 #else
2655 if(locked)
2656 s = str(LANG_KEYLOCK_ON_RECORDER);
2657 else
2658 s = str(LANG_KEYLOCK_OFF_RECORDER);
2659 #endif
2660 gui_syncsplash(HZ, true, s);