HAVE_ADJUSTABLE_CPU_FREQ isn't defined for simulators, so we don't have to check...
[Rockbox.git] / apps / gui / gwps-common.c
blob41e20ecadd9bc5b304447ec4e0537b41f69a50d8
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 #include "rtc.h"
27 #include "audio.h"
28 #include "status.h"
29 #include "power.h"
30 #include "powermgmt.h"
31 #include "sound.h"
32 #include "debug.h"
33 #ifdef HAVE_LCD_CHARCELLS
34 #include "hwcompat.h"
35 #endif
36 #include "abrepeat.h"
37 #include "mp3_playback.h"
38 #include "backlight.h"
39 #include "lang.h"
40 #include "misc.h"
41 #include "splash.h"
42 #include "scrollbar.h"
43 #include "led.h"
44 #include "lcd.h"
45 #ifdef HAVE_LCD_BITMAP
46 #include "peakmeter.h"
47 /* Image stuff */
48 #include "bmp.h"
49 #include "atoi.h"
50 #endif
51 #ifdef HAVE_LCD_COLOR
52 #include "backdrop.h"
53 #endif
54 #include "dsp.h"
56 #ifdef HAVE_LCD_CHARCELLS
57 static bool draw_player_progress(struct gui_wps *gwps);
58 static void draw_player_fullbar(struct gui_wps *gwps,
59 char* buf, int buf_size);
60 #endif
62 #define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */
63 /* 3% of 30min file == 54s step size */
64 #define MIN_FF_REWIND_STEP 500
66 /* Skip leading UTF-8 BOM, if present. */
67 static char* skip_utf8_bom(char* buf)
69 unsigned char* s = (unsigned char*) buf;
71 if(s[0] == 0xef && s[1] == 0xbb && s[2] == 0xbf)
73 buf += 3;
76 return buf;
80 * returns the image_id between
81 * a..z and A..Z
83 #ifdef HAVE_LCD_BITMAP
84 static int get_image_id(int c)
86 if(c >= 'a' && c <= 'z')
87 c -= 'a';
88 if(c >= 'A' && c <= 'Z')
89 c = c - 'A' + 26;
90 return c;
92 #endif
95 * parse the given buffer for following static tags:
96 * %x - load image for always display
97 * %X - load backdrop image
98 * %xl - preload image
99 * %we - enable statusbar on wps regardless of the global setting
100 * %wd - disable statusbar on wps regardless of the global setting
101 * and also for:
102 * # - a comment line
104 * it returns true if one of these tags is found and handled
105 * false otherwise
107 bool wps_data_preload_tags(struct wps_data *data, char *buf,
108 const char *bmpdir, size_t bmpdirlen)
110 if(!data || !buf) return false;
112 char c;
113 #ifndef HAVE_LCD_BITMAP
114 /* no bitmap-lcd == no bitmap loading */
115 (void)bmpdir;
116 (void)bmpdirlen;
117 #endif
118 buf = skip_utf8_bom(buf);
120 if(*buf == '#')
121 return true;
122 if('%' != *buf)
123 return false;
124 buf++;
126 c = *buf;
127 switch (c)
129 #ifdef HAVE_LCD_BITMAP
130 case 'w':
132 * if tag found then return because these two tags must be on
133 * must be on their own line
135 if(*(buf+1) == 'd' || *(buf+1) == 'e')
137 data->wps_sb_tag = true;
138 if( *(buf+1) == 'e' )
139 data->show_sb_on_wps = true;
140 return true;
142 break;
144 #ifdef HAVE_LCD_COLOR
145 case 'X':
146 /* Backdrop image - must be the same size as the LCD */
148 int ret = 0;
149 struct bitmap bm;
150 char *ptr = buf+2;
151 char *pos = NULL;
152 char imgname[MAX_PATH];
154 /* format: %X|filename.bmp| */
156 /* get filename */
157 pos = strchr(ptr, '|');
158 if ((pos - ptr) <
159 (int)sizeof(imgname)-ROCKBOX_DIR_LEN-2)
161 memcpy(imgname, bmpdir, bmpdirlen);
162 imgname[bmpdirlen] = '/';
163 memcpy(&imgname[bmpdirlen+1],
164 ptr, pos - ptr);
165 imgname[bmpdirlen+1+pos-ptr] = 0;
167 else
168 /* filename too long */
169 imgname[0] = 0;
171 ptr = pos+1;
173 /* load the image */
174 bm.data=(char*)&wps_backdrop[0][0];
175 ret = read_bmp_file(imgname, &bm,
176 sizeof(wps_backdrop), FORMAT_NATIVE);
178 if ((ret > 0) && (bm.width == LCD_WIDTH)
179 && (bm.height == LCD_HEIGHT)) {
180 data->has_backdrop=true;
181 return true;
182 } else {
183 return false;
188 break;
189 #endif
191 case 'P':
192 /* progress bar image */
194 int ret = 0;
195 char *ptr = buf+2;
196 char *pos = NULL;
197 char imgname[MAX_PATH];
199 /* format: %P|filename.bmp| */
201 /* get filename */
202 pos = strchr(ptr, '|');
203 if ((pos - ptr) <
204 (int)sizeof(imgname)-ROCKBOX_DIR_LEN-2)
206 memcpy(imgname, bmpdir, bmpdirlen);
207 imgname[bmpdirlen] = '/';
208 memcpy(&imgname[bmpdirlen+1],
209 ptr, pos - ptr);
210 imgname[bmpdirlen+1+pos-ptr] = 0;
212 else
213 /* filename too long */
214 imgname[0] = 0;
216 ptr = pos+1;
218 /* load the image */
219 data->progressbar.bm.data=data->img_buf_ptr;
220 ret = read_bmp_file(imgname, &data->progressbar.bm,
221 data->img_buf_free,
222 FORMAT_ANY|FORMAT_TRANSPARENT);
224 if (ret > 0)
226 #if LCD_DEPTH == 16
227 if (ret % 2) ret++;
228 /* Always consume an even number of bytes */
229 #endif
231 data->img_buf_ptr += ret;
232 data->img_buf_free -= ret;
234 if (data->progressbar.bm.width <= LCD_WIDTH) {
235 data->progressbar.have_bitmap_pb=true;
236 return true;
237 } else
238 return false;
244 break;
246 case 'x':
247 /* Preload images so the %xd# tag can display it */
249 int ret = 0;
250 int n;
251 char *ptr = buf+1;
252 char *pos = NULL;
253 char imgname[MAX_PATH];
254 char qual = *ptr;
256 if (qual == 'l' || qual == '|') /* format:
257 %x|n|filename.bmp|x|y|
259 %xl|n|filename.bmp|x|y|
262 ptr = strchr(ptr, '|') + 1;
263 pos = strchr(ptr, '|');
264 if (pos)
266 /* get the image ID */
267 n = get_image_id(*ptr);
269 if(n < 0 || n >= MAX_IMAGES)
271 /* Skip the rest of the line */
272 while(*buf != '\n')
273 buf++;
274 return false;
276 ptr = pos+1;
278 /* check the image number and load state */
279 if (data->img[n].loaded)
281 /* Skip the rest of the line */
282 while(*buf != '\n')
283 buf++;
284 return false;
286 else
288 /* get filename */
289 pos = strchr(ptr, '|');
290 if ((pos - ptr) <
291 (int)sizeof(imgname)-ROCKBOX_DIR_LEN-2)
293 memcpy(imgname, bmpdir, bmpdirlen);
294 imgname[bmpdirlen] = '/';
295 memcpy(&imgname[bmpdirlen+1],
296 ptr, pos - ptr);
297 imgname[bmpdirlen+1+pos-ptr] = 0;
299 else
300 /* filename too long */
301 imgname[0] = 0;
303 ptr = pos+1;
305 /* get x-position */
306 pos = strchr(ptr, '|');
307 if (pos)
308 data->img[n].x = atoi(ptr);
309 else
311 /* weird syntax, bail out */
312 buf++;
313 return false;
316 /* get y-position */
317 ptr = pos+1;
318 pos = strchr(ptr, '|');
319 if (pos)
320 data->img[n].y = atoi(ptr);
321 else
323 /* weird syntax, bail out */
324 buf++;
325 return false;
328 /* load the image */
329 data->img[n].bm.data = data->img_buf_ptr;
330 ret = read_bmp_file(imgname, &data->img[n].bm,
331 data->img_buf_free,
332 FORMAT_ANY|FORMAT_TRANSPARENT);
334 if (ret > 0)
336 #if LCD_DEPTH == 16
337 if (ret % 2) ret++;
338 /* Always consume an even number of bytes */
339 #endif
341 data->img_buf_ptr += ret;
342 data->img_buf_free -= ret;
343 data->img[n].loaded = true;
344 if(qual == '|')
345 data->img[n].always_display = true;
347 return true;
353 break;
354 #endif
356 /* no of these tags found */
357 return false;
361 /* draws the statusbar on the given wps-screen */
362 #ifdef HAVE_LCD_BITMAP
363 static void gui_wps_statusbar_draw(struct gui_wps *wps, bool force)
365 bool draw = global_settings.statusbar;
367 if(wps->data->wps_sb_tag
368 && wps->data->show_sb_on_wps)
369 draw = true;
370 else if(wps->data->wps_sb_tag)
371 draw = false;
372 if(draw)
373 gui_statusbar_draw(wps->statusbar, force);
375 #else
376 #define gui_wps_statusbar_draw(wps, force) \
377 gui_statusbar_draw((wps)->statusbar, (force))
378 #endif
380 /* Format time into buf.
382 * buf - buffer to format to.
383 * buf_size - size of buffer.
384 * time - time to format, in milliseconds.
386 void gui_wps_format_time(char* buf, int buf_size, long time)
388 if ( time < 3600000 ) {
389 snprintf(buf, buf_size, "%d:%02d",
390 (int) (time % 3600000 / 60000), (int) (time % 60000 / 1000));
391 } else {
392 snprintf(buf, buf_size, "%d:%02d:%02d",
393 (int) (time / 3600000), (int) (time % 3600000 / 60000),
394 (int) (time % 60000 / 1000));
398 /* Extract a part from a path.
400 * buf - buffer extract part to.
401 * buf_size - size of buffer.
402 * path - path to extract from.
403 * level - what to extract. 0 is file name, 1 is parent of file, 2 is
404 * parent of parent, etc.
406 * Returns buf if the desired level was found, NULL otherwise.
408 static char* get_dir(char* buf, int buf_size, const char* path, int level)
410 const char* sep;
411 const char* last_sep;
412 int len;
414 sep = path + strlen(path);
415 last_sep = sep;
417 while (sep > path)
419 if ('/' == *(--sep))
421 if (!level)
423 break;
426 level--;
427 last_sep = sep - 1;
431 if (level || (last_sep <= sep))
433 return NULL;
436 len = MIN(last_sep - sep, buf_size - 1);
437 strncpy(buf, sep + 1, len);
438 buf[len] = 0;
439 return buf;
442 /* Get the tag specified by the two characters at fmt.
444 * cid3 - ID3 data to get tag values from.
445 * nid3 - next-song ID3 data to get tag values from.
446 * tag - string (of two characters) specifying the tag to get.
447 * buf - buffer to certain tags, such as track number, play time or
448 * directory name.
449 * buf_size - size of buffer.
450 * flags - returns the type of the line. See constants i wps-display.h
452 * Returns the tag. NULL indicates the tag wasn't available.
454 static char* get_tag(struct wps_data* wps_data,
455 struct mp3entry* cid3,
456 struct mp3entry* nid3,
457 const char* tag,
458 char* buf,
459 int buf_size,
460 unsigned char* tag_len,
461 unsigned short* subline_time_mult,
462 unsigned char* flags,
463 int *intval)
465 struct mp3entry *id3 = cid3; /* default to current song */
466 #ifndef HAVE_LCD_CHARCELLS
467 (void)wps_data;
468 #endif
469 if ((0 == tag[0]) || (0 == tag[1]))
471 *tag_len = 0;
472 return NULL;
475 *tag_len = 2;
477 *intval = 0;
479 switch (tag[0])
481 case 'I': /* ID3 Information */
482 id3 = nid3; /* display next-song data */
483 *flags |= WPS_REFRESH_DYNAMIC;
484 if(!id3)
485 return NULL; /* no such info (yet) */
486 /* fall-through */
487 case 'i': /* ID3 Information */
488 *flags |= WPS_REFRESH_STATIC;
489 switch (tag[1])
491 case 't': /* ID3 Title */
492 return id3->title;
494 case 'a': /* ID3 Artist */
495 return id3->artist;
497 case 'n': /* ID3 Track Number */
498 if (id3->track_string)
499 return id3->track_string;
501 if (id3->tracknum) {
502 snprintf(buf, buf_size, "%d", id3->tracknum);
503 return buf;
505 return NULL;
507 case 'd': /* ID3 Album/Disc */
508 return id3->album;
510 case 'c': /* ID3 Composer */
511 return id3->composer;
513 case 'y': /* year */
514 if( id3->year_string )
515 return id3->year_string;
517 if (id3->year) {
518 snprintf(buf, buf_size, "%d", id3->year);
519 return buf;
521 return NULL;
523 case 'g': /* genre */
524 return id3_get_genre(id3);
526 case 'v': /* id3 version */
527 switch (id3->id3version)
529 case ID3_VER_1_0:
530 return "1";
532 case ID3_VER_1_1:
533 return "1.1";
535 case ID3_VER_2_2:
536 return "2.2";
538 case ID3_VER_2_3:
539 return "2.3";
541 case ID3_VER_2_4:
542 return "2.4";
544 default:
545 return NULL;
548 break;
550 case 'F': /* File Information */
551 id3 = nid3;
552 *flags |= WPS_REFRESH_DYNAMIC;
553 if(!id3)
554 return NULL; /* no such info (yet) */
555 /* fall-through */
556 case 'f': /* File Information */
557 *flags |= WPS_REFRESH_STATIC;
558 switch(tag[1])
560 case 'v': /* VBR file? */
561 return id3->vbr ? "(avg)" : NULL;
563 case 'b': /* File Bitrate */
564 if(id3->bitrate)
565 snprintf(buf, buf_size, "%d", id3->bitrate);
566 else
567 snprintf(buf, buf_size, "?");
568 return buf;
570 case 'f': /* File Frequency */
571 snprintf(buf, buf_size, "%ld", id3->frequency);
572 return buf;
574 case 'p': /* File Path */
575 return id3->path;
577 case 'm': /* File Name - With Extension */
578 return get_dir(buf, buf_size, id3->path, 0);
580 case 'n': /* File Name */
581 if (get_dir(buf, buf_size, id3->path, 0))
583 /* Remove extension */
584 char* sep = strrchr(buf, '.');
586 if (NULL != sep)
588 *sep = 0;
591 return buf;
593 else
595 return NULL;
598 case 's': /* File Size (in kilobytes) */
599 snprintf(buf, buf_size, "%ld", id3->filesize / 1024);
600 return buf;
602 case 'c': /* File Codec */
603 if(id3->codectype == AFMT_UNKNOWN)
604 *intval = AFMT_NUM_CODECS;
605 else
606 *intval = id3->codectype;
607 return id3_get_codec(id3);
609 break;
611 case 'p': /* Playlist/Song Information */
612 switch(tag[1])
614 case 'b': /* progress bar */
615 *flags |= WPS_REFRESH_PLAYER_PROGRESS;
616 #ifdef HAVE_LCD_CHARCELLS
617 snprintf(buf, buf_size, "%c",
618 wps_data->wps_progress_pat[0]);
619 wps_data->full_line_progressbar=0;
620 return buf;
621 #else
622 char *p=strchr(tag, '|');
623 if (p) {
624 wps_data->progress_height=atoi(++p);
625 p=strchr(p, '|');
626 if (p) {
627 wps_data->progress_start=atoi(++p);
628 p=strchr(p, '|');
629 if (p)
630 wps_data->progress_end=atoi(++p);
631 else
632 wps_data->progress_end=0;
633 }else {
634 wps_data->progress_start=0;
635 wps_data->progress_end=0;
638 if (wps_data->progress_height<3)
639 wps_data->progress_height=3;
640 if (wps_data->progress_end<wps_data->progress_start+3)
641 wps_data->progress_end=0;
642 }else {
643 wps_data->progress_height=6;
644 wps_data->progress_start=0;
645 wps_data->progress_end=0;
647 return "\x01";
648 #endif
649 case 'f': /* full-line progress bar */
650 #ifdef HAVE_LCD_CHARCELLS
651 if(is_new_player()) {
652 *flags |= WPS_REFRESH_PLAYER_PROGRESS;
653 *flags |= WPS_REFRESH_DYNAMIC;
654 wps_data->full_line_progressbar=1;
655 /* we need 11 characters (full line) for
656 progress-bar */
657 snprintf(buf, buf_size, " ");
659 else
661 /* Tell the user if we have an OldPlayer */
662 snprintf(buf, buf_size, " <Old LCD> ");
664 return buf;
665 #endif
666 case 'p': /* Playlist Position */
667 *flags |= WPS_REFRESH_STATIC;
668 snprintf(buf, buf_size, "%d",
669 playlist_get_display_index());
670 return buf;
672 case 'n': /* Playlist Name (without path) */
673 *flags |= WPS_REFRESH_STATIC;
674 return playlist_name(NULL, buf, buf_size);
676 case 'e': /* Playlist Total Entries */
677 *flags |= WPS_REFRESH_STATIC;
678 snprintf(buf, buf_size, "%d", playlist_amount());
679 return buf;
681 case 'c': /* Current Time in Song */
682 *flags |= WPS_REFRESH_DYNAMIC;
683 gui_wps_format_time(buf, buf_size,
684 id3->elapsed + wps_state.ff_rewind_count);
685 return buf;
687 case 'r': /* Remaining Time in Song */
688 *flags |= WPS_REFRESH_DYNAMIC;
689 gui_wps_format_time(buf, buf_size,
690 id3->length - id3->elapsed -
691 wps_state.ff_rewind_count);
692 return buf;
694 case 't': /* Total Time */
695 *flags |= WPS_REFRESH_STATIC;
696 gui_wps_format_time(buf, buf_size, id3->length);
697 return buf;
699 #ifdef HAVE_LCD_BITMAP
700 case 'm': /* Peak Meter */
701 *flags |= WPS_REFRESH_PEAK_METER;
702 return "\x01";
703 #endif
704 case 's': /* shuffle */
705 *flags |= WPS_REFRESH_DYNAMIC;
706 if ( global_settings.playlist_shuffle )
707 return "s";
708 else
709 return NULL;
710 break;
712 case 'v': /* volume */
713 *flags |= WPS_REFRESH_DYNAMIC;
714 snprintf(buf, buf_size, "%d", global_settings.volume);
715 *intval = 10 * (global_settings.volume
716 - sound_min(SOUND_VOLUME))
717 / (sound_max(SOUND_VOLUME)
718 - sound_min(SOUND_VOLUME)) + 1;
719 return buf;
722 break;
724 #if (CONFIG_CODEC == SWCODEC)
725 case 'S': /* DSP/Equalizer/Sound settings */
726 switch (tag[1])
728 case 'p': /* pitch */
729 *intval = sound_get_pitch();
730 snprintf(buf, buf_size, "%d.%d",
731 *intval / 10, *intval % 10);
732 return buf;
734 break;
735 #endif
737 case 'm':
738 switch (tag[1])
740 case 'm': /* playback repeat mode */
741 *flags |= WPS_REFRESH_DYNAMIC;
742 *intval = global_settings.repeat_mode + 1;
743 snprintf(buf, buf_size, "%d", *intval);
744 return buf;
746 /* playback status */
747 case 'p': /* play */
748 *flags |= WPS_REFRESH_DYNAMIC;
749 int status = audio_status();
750 *intval = 1;
751 if (status == AUDIO_STATUS_PLAY && \
752 !(status & AUDIO_STATUS_PAUSE))
753 *intval = 2;
754 if (audio_status() & AUDIO_STATUS_PAUSE && \
755 (! status_get_ffmode()))
756 *intval = 3;
757 if (status_get_ffmode() == STATUS_FASTFORWARD)
758 *intval = 4;
759 if (status_get_ffmode() == STATUS_FASTBACKWARD)
760 *intval = 5;
761 snprintf(buf, buf_size, "%d", *intval);
762 return buf;
764 #ifdef HAS_BUTTON_HOLD
765 case 'h': /* hold */
766 *flags |= WPS_REFRESH_DYNAMIC;
767 if (button_hold())
768 return "h";
769 else
770 return NULL;
771 #endif
772 #ifdef HAS_REMOTE_BUTTON_HOLD
773 case 'r': /* remote hold */
774 *flags |= WPS_REFRESH_DYNAMIC;
775 if (remote_button_hold())
776 return "r";
777 else
778 return NULL;
779 #endif
781 break;
783 case 'b': /* battery info */
784 *flags |= WPS_REFRESH_DYNAMIC;
785 switch (tag[1])
787 case 'l': /* battery level */
789 int l = battery_level();
790 if (l > -1)
792 snprintf(buf, buf_size, "%d", l);
793 *intval = l / 20 + 1;
795 else
797 *intval = 6;
798 return "?";
800 return buf;
803 case 'v': /* battery voltage */
805 unsigned int v = battery_voltage();
806 snprintf(buf, buf_size, "%d.%02d", v/100, v%100);
807 return buf;
810 case 't': /* estimated battery time */
812 int t = battery_time();
813 if (t >= 0)
814 snprintf(buf, buf_size, "%dh %dm", t / 60, t % 60);
815 else
816 strncpy(buf, "?h ?m", buf_size);
817 return buf;
820 case 's': /* sleep timer */
822 if (get_sleep_timer() == 0)
824 return NULL;
826 else
828 gui_wps_format_time(buf, buf_size, \
829 get_sleep_timer() * 1000);
830 return buf;
834 #ifdef HAVE_CHARGING
835 case 'p': /* External power plugged in? */
837 if(charger_input_state==CHARGER)
838 return "p";
839 else
840 return NULL;
842 #endif
843 #if defined(HAVE_CHARGE_CTRL) || \
844 defined (HAVE_CHARGE_STATE) || \
845 CONFIG_BATTERY == BATT_LIION2200
846 case 'c': /* Charging */
848 if (charge_state == CHARGING || charge_state == TOPOFF) {
849 return "c";
850 } else {
851 return NULL;
854 #endif
856 break;
858 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
859 case 'l': /* VIRTUAL_LED */
861 switch(tag[1])
863 case 'h': /* Only one we have so far HDD LED */
864 *flags |= WPS_REFRESH_DYNAMIC;
865 if(led_read(HZ/2))
866 return "h";
867 else
868 return NULL;
871 break;
872 #endif
874 case 'D': /* Directory path information */
875 id3 = nid3; /* next song please! */
876 *flags |= WPS_REFRESH_DYNAMIC;
877 if(!id3)
878 return NULL; /* no such info (yet) */
879 /* fall-through */
880 case 'd': /* Directory path information */
882 int level = tag[1] - '0';
883 *flags |= WPS_REFRESH_STATIC;
884 /* d1 through d9 */
885 if ((0 < level) && (9 > level))
887 return get_dir(buf, buf_size, id3->path, level);
890 break;
892 case 't': /* set sub line time multiplier */
894 int d = 1;
895 int time_mult = 0;
896 bool have_point = false;
897 bool have_tenth = false;
899 while (((tag[d] >= '0') &&
900 (tag[d] <= '9')) ||
901 (tag[d] == '.'))
903 if (tag[d] != '.')
905 time_mult = time_mult * 10;
906 time_mult = time_mult + tag[d] - '0';
907 if (have_point)
909 have_tenth = true;
910 d++;
911 break;
914 else
916 have_point = true;
918 d++;
921 if (have_tenth == false)
922 time_mult *= 10;
924 *subline_time_mult = time_mult;
925 *tag_len = d;
927 buf[0] = 0;
928 return buf;
930 break;
931 case 'r': /* Runtime database Information */
932 switch(tag[1])
934 case 'p': /* Playcount */
935 *flags |= WPS_REFRESH_STATIC;
936 snprintf(buf, buf_size, "%ld", cid3->playcount);
937 return buf;
938 case 'r': /* Rating */
939 *flags |= WPS_REFRESH_STATIC;
940 *intval = cid3->rating+1;
941 snprintf(buf, buf_size, "%d", cid3->rating);
942 return buf;
944 break;
945 #ifdef CONFIG_RTC
946 case 'c': /* Real Time Clock display */
947 *flags |= WPS_REFRESH_DYNAMIC;
949 int value;
950 char *format = 0;
951 char *bufptr = buf;
952 struct tm* tm = get_time();
953 int i;
954 for (i=1;/*break*/;i++) {
955 switch(tag[i])
957 case 'a': /* abbreviated weekday name (Sun..Sat) */
958 value = tm->tm_wday;
959 if (value > 6 || value < 0) continue;
960 value = snprintf(
961 bufptr,buf_size,"%s",str(dayname[value]));
962 bufptr += value;
963 buf_size -= value;
964 continue;
965 case 'b': /* abbreviated month name (Jan..Dec) */
966 value = tm->tm_mon;
967 if (value > 11 || value < 0) continue;
968 value = snprintf(
969 bufptr,buf_size,"%s",str(monthname[value]));
970 bufptr += value;
971 buf_size -= value;
972 continue;
973 case 'd': /* day of month (01..31) */
974 value = tm->tm_mday;
975 if (value > 31 || value < 1) continue;
976 format = "%02d";
977 break;
978 case 'e': /* day of month, blank padded ( 1..31) */
979 value = tm->tm_mday;
980 if (value > 31 || value < 1) continue;
981 format = "%2d";
982 break;
983 case 'H': /* hour (00..23) */
984 value = tm->tm_hour;
985 if (value > 23) continue;
986 format = "%02d";
987 break;
988 case 'k': /* hour ( 0..23) */
989 value = tm->tm_hour;
990 if (value > 23) continue;
991 format = "%2d";
992 break;
993 case 'I': /* hour (01..12) */
994 value = tm->tm_hour;
995 if (value > 23) continue;
996 value %= 12;
997 if (value == 0) value = 12;
998 format = "%02d";
999 break;
1000 case 'l': /* hour ( 1..12) */
1001 value = tm->tm_hour;
1002 if (value > 23 || value < 0) continue;
1003 value %= 12;
1004 if (value == 0) value = 12;
1005 format = "%2d";
1006 break;
1007 case 'm': /* month (01..12) */
1008 value = tm->tm_mon;
1009 if (value > 11 || value < 0) continue;
1010 value++;
1011 format = "%02d";
1012 break;
1013 case 'M': /* minute (00..59) */
1014 value = tm->tm_min;
1015 if (value > 59 || value < 0) continue;
1016 format = "%02d";
1017 break;
1018 case 'S': /* second (00..59) */
1019 value = tm->tm_sec;
1020 if (value > 59 || value < 0) continue;
1021 format = "%02d";
1022 break;
1023 case 'y': /* last two digits of year (00..99) */
1024 value = tm->tm_year;
1025 value %= 100;
1026 format = "%02d";
1027 break;
1028 case 'Y': /* year (1970...) */
1029 value = tm->tm_year;
1030 if (value > 199 || value < 100) continue;
1031 value += 1900;
1032 format = "%04d";
1033 break;
1034 case 'p': /* upper case AM or PM indicator */
1035 if (tm->tm_hour/12 == 0) format = "AM";
1036 else format = "PM";
1037 snprintf(bufptr,buf_size,"%s",format);
1038 bufptr += 2;
1039 buf_size -= 2;
1040 continue;
1041 case 'P': /* lower case am or pm indicator */
1042 if (tm->tm_hour/12 == 0) format = "am";
1043 else format = "pm";
1044 snprintf(bufptr,buf_size,"%s",format);
1045 bufptr += 2;
1046 buf_size -= 2;
1047 continue;
1048 case 'u': /* day of week (1..7); 1 is Monday */
1049 value = tm->tm_wday;
1050 if (value < 0 || value > 6) continue;
1051 value++;
1052 format = "%1d";
1053 break;
1054 case 'w': /* day of week (0..6); 0 is Sunday */
1055 value = tm->tm_wday;
1056 if (value < 0 || value > 6) continue;
1057 format = "%1d";
1058 break;
1059 default:
1060 if (tag[i] == 'c') {
1061 i++;
1062 value = -1;
1063 break;
1064 } else if (tag[i] == '\n') {
1065 value = -1;
1066 break;
1068 snprintf(bufptr,buf_size,"%c",tag[i]);
1069 bufptr++;
1070 buf_size--;
1071 continue;
1072 } /* switch */
1073 if (value < 0) break;
1075 value = snprintf(bufptr, buf_size, format, value);
1076 bufptr += value;
1077 buf_size -= value;
1078 } /* while */
1079 *tag_len = i;
1080 return buf;
1082 #endif /* CONFIG_RTC */
1084 return NULL;
1087 #ifdef HAVE_LCD_BITMAP
1088 /* clears the area where the image was shown */
1089 static void clear_image_pos(struct gui_wps *gwps, int n)
1091 if(!gwps)
1092 return;
1093 struct wps_data *data = gwps->data;
1094 gwps->display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1095 gwps->display->fillrect(data->img[n].x, data->img[n].y,
1096 data->img[n].bm.width, data->img[n].bm.height);
1097 gwps->display->set_drawmode(DRMODE_SOLID);
1099 #endif
1101 /* Skip to the end of the current %? conditional.
1103 * fmt - string to skip it. Should point to somewhere after the leading
1104 * "<" char (and before or at the last ">").
1105 * num - number of |'s to skip, or 0 to skip to the end (the ">").
1107 * Returns the new position in fmt.
1109 static const char* skip_conditional(struct gui_wps *gwps, const char* fmt,
1110 int num)
1112 int level = 1;
1113 int count = num;
1114 const char *last_alternative = NULL;
1115 #ifdef HAVE_LCD_BITMAP
1116 struct wps_data *data = NULL;
1117 int last_x=-1, last_y=-1, last_w=-1, last_h=-1;
1118 if(gwps)
1119 data = gwps->data;
1120 #else
1121 (void)gwps;
1122 #endif
1123 while (*fmt)
1125 switch (*fmt++)
1127 case '%':
1128 #ifdef HAVE_LCD_BITMAP
1129 if(data && *(fmt) == 'x' && *(fmt+1) == 'd' )
1131 fmt +=2;
1132 int n = *fmt;
1133 if(n >= 'a' && n <= 'z')
1134 n -= 'a';
1135 if(n >= 'A' && n <= 'Z')
1136 n = n - 'A' + 26;
1137 if(last_x != data->img[n].x || last_y != data->img[n].y
1138 || last_w != data->img[n].bm.width
1139 || last_h != data->img[n].bm.height)
1141 last_x = data->img[n].x;
1142 last_y = data->img[n].y;
1143 last_w = data->img[n].bm.width;
1144 last_h = data->img[n].bm.height;
1145 clear_image_pos(gwps,n);
1148 #endif
1149 break;
1151 case '|':
1152 if(1 == level) {
1153 last_alternative = fmt;
1154 if(num) {
1155 count--;
1156 if(count == 0)
1157 return fmt;
1158 continue;
1161 continue;
1163 case '>':
1164 if (0 == --level)
1166 /* We're just skipping to the end */
1167 if(num == 0)
1168 return fmt;
1170 /* If we are parsing an enum, we'll return the selected
1171 item. If there weren't enough items in the enum, we'll
1172 return the last one found. */
1173 if(count && last_alternative)
1175 return last_alternative;
1177 return fmt - 1;
1179 continue;
1181 default:
1182 continue;
1185 switch (*fmt++)
1187 case 0:
1188 case '%':
1189 case '|':
1190 case '<':
1191 case '>':
1192 break;
1194 case '?':
1195 while (*fmt && ('<' != *fmt))
1196 fmt++;
1198 if ('<' == *fmt)
1199 fmt++;
1201 level++;
1202 break;
1204 default:
1205 break;
1209 return fmt;
1212 /* Generate the display based on id3 information and format string.
1214 * buf - char buffer to write the display to.
1215 * buf_size - the size of buffer.
1216 * id3 - the ID3 data to format with.
1217 * nid3 - the ID3 data of the next song (might by NULL)
1218 * fmt - format description.
1219 * flags - returns the type of the line. See constants i wps-display.h
1221 static void format_display(struct gui_wps *gwps, char* buf,
1222 int buf_size,
1223 struct mp3entry* id3,
1224 struct mp3entry* nid3, /* next song's id3 */
1225 const char* fmt,
1226 struct align_pos* align,
1227 unsigned short* subline_time_mult,
1228 unsigned char* flags)
1230 char temp_buf[128];
1231 char* buf_start = buf;
1232 char* buf_end = buf + buf_size - 1; /* Leave room for end null */
1233 char* value = NULL;
1234 int level = 0;
1235 unsigned char tag_length;
1236 int intval;
1237 int cur_align;
1238 char* cur_align_start;
1239 #ifdef HAVE_LCD_BITMAP
1240 struct gui_img *img = gwps->data->img;
1241 int n;
1242 #endif
1244 cur_align_start = buf;
1245 cur_align = WPS_ALIGN_LEFT;
1246 *subline_time_mult = DEFAULT_SUBLINE_TIME_MULTIPLIER;
1248 align->left = 0;
1249 align->center = 0;
1250 align->right = 0;
1252 while (fmt && *fmt && buf < buf_end)
1254 switch (*fmt)
1256 case '%':
1257 ++fmt;
1258 break;
1260 case '|':
1261 case '>':
1262 if (level > 0)
1264 fmt = skip_conditional(NULL,fmt, 0);
1265 level--;
1266 continue;
1268 /* Else fall through */
1270 default:
1271 *buf++ = *fmt++;
1272 continue;
1275 switch (*fmt)
1277 case 0:
1278 *buf++ = '%';
1279 break;
1280 case 'a':
1281 ++fmt;
1282 /* remember where the current aligned text started */
1283 switch (cur_align)
1285 case WPS_ALIGN_LEFT:
1286 align->left = cur_align_start;
1287 break;
1289 case WPS_ALIGN_CENTER:
1290 align->center = cur_align_start;
1291 break;
1293 case WPS_ALIGN_RIGHT:
1294 align->right = cur_align_start;
1295 break;
1297 /* start a new alignment */
1298 switch (*fmt)
1300 case 'l':
1301 cur_align = WPS_ALIGN_LEFT;
1302 break;
1303 case 'c':
1304 cur_align = WPS_ALIGN_CENTER;
1305 break;
1306 case 'r':
1307 cur_align = WPS_ALIGN_RIGHT;
1308 break;
1310 *buf++=0;
1311 cur_align_start = buf;
1312 ++fmt;
1313 break;
1314 case 's':
1315 *flags |= WPS_REFRESH_SCROLL;
1316 ++fmt;
1317 break;
1319 case 'x': /* image support */
1320 #ifdef HAVE_LCD_BITMAP
1321 if ('d' == *(fmt+1) )
1323 fmt+=2;
1325 /* get the image ID */
1326 n = *fmt;
1327 if(n >= 'a' && n <= 'z')
1328 n -= 'a';
1329 if(n >= 'A' && n <= 'Z')
1330 n = n - 'A' + 26;
1331 if (n >= 0 && n < MAX_IMAGES && img[n].loaded) {
1332 img[n].display = true;
1336 #endif
1337 fmt++;
1338 break;
1341 case '%':
1342 case '|':
1343 case '<':
1344 case '>':
1345 case ';':
1346 *buf++ = *fmt++;
1347 break;
1349 case '?':
1350 fmt++;
1351 value = get_tag(gwps->data, id3, nid3, fmt, temp_buf,
1352 sizeof(temp_buf),&tag_length,
1353 subline_time_mult, flags, &intval);
1355 while (*fmt && ('<' != *fmt))
1356 fmt++;
1358 if ('<' == *fmt)
1359 fmt++;
1361 /* No value, so skip to else part, using a sufficiently high
1362 value to "hit" the last part of the conditional */
1363 if ((!value) || (!strlen(value)))
1364 fmt = skip_conditional(gwps, fmt, 1000);
1365 else
1366 if(intval > 1) /* enum */
1367 fmt = skip_conditional(gwps, fmt, intval - 1);
1369 level++;
1370 break;
1372 default:
1373 value = get_tag(gwps->data, id3, nid3, fmt, temp_buf,
1374 sizeof(temp_buf), &tag_length,
1375 subline_time_mult, flags,&intval);
1376 fmt += tag_length;
1378 if (value)
1380 while (*value && (buf < buf_end))
1381 *buf++ = *value++;
1386 /* remember where the current aligned text started */
1387 switch (cur_align)
1389 case WPS_ALIGN_LEFT:
1390 align->left = cur_align_start;
1391 break;
1393 case WPS_ALIGN_CENTER:
1394 align->center = cur_align_start;
1395 break;
1397 case WPS_ALIGN_RIGHT:
1398 align->right = cur_align_start;
1399 break;
1402 *buf = 0;
1404 /* if resulting line is an empty line, set the subline time to 0 */
1405 if (buf - buf_start == 0)
1406 *subline_time_mult = 0;
1408 /* If no flags have been set, the line didn't contain any format codes.
1409 We still want to refresh it. */
1410 if(*flags == 0)
1411 *flags = WPS_REFRESH_STATIC;
1414 /* fades the volume */
1415 void fade(bool fade_in)
1417 int fp_global_vol = global_settings.volume << 8;
1418 int fp_min_vol = sound_min(SOUND_VOLUME) << 8;
1419 int fp_step = (fp_global_vol - fp_min_vol) / 30;
1421 if (fade_in) {
1422 /* fade in */
1423 int fp_volume = fp_min_vol;
1425 /* zero out the sound */
1426 sound_set_volume(fp_min_vol >> 8);
1428 sleep(HZ/10); /* let audio thread run */
1429 audio_resume();
1431 while (fp_volume < fp_global_vol - fp_step) {
1432 fp_volume += fp_step;
1433 sound_set_volume(fp_volume >> 8);
1434 sleep(1);
1436 sound_set_volume(global_settings.volume);
1438 else {
1439 /* fade out */
1440 int fp_volume = fp_global_vol;
1442 while (fp_volume > fp_min_vol + fp_step) {
1443 fp_volume -= fp_step;
1444 sound_set_volume(fp_volume >> 8);
1445 sleep(1);
1447 audio_pause();
1448 #ifndef SIMULATOR
1449 /* let audio thread run and wait for the mas to run out of data */
1450 while (!mp3_pause_done())
1451 #endif
1452 sleep(HZ/10);
1454 /* reset volume to what it was before the fade */
1455 sound_set_volume(global_settings.volume);
1459 /* Set format string to use for WPS, splitting it into lines */
1460 void gui_wps_format(struct wps_data *data)
1462 char* buf = data->format_buffer;
1463 char* start_of_line = data->format_buffer;
1464 int line = 0;
1465 int subline;
1466 char c;
1467 if(!data)
1468 return;
1470 for (line=0; line<WPS_MAX_LINES; line++)
1472 for (subline=0; subline<WPS_MAX_SUBLINES; subline++)
1474 data->format_lines[line][subline] = 0;
1475 data->time_mult[line][subline] = 0;
1477 data->subline_expire_time[line] = 0;
1478 data->curr_subline[line] = SUBLINE_RESET;
1481 line = 0;
1482 subline = 0;
1483 buf = skip_utf8_bom(buf);
1484 data->format_lines[line][subline] = buf;
1486 while ((*buf) && (line < WPS_MAX_LINES))
1488 c = *buf;
1490 switch (c)
1493 * skip % sequences so "%;" doesn't start a new subline
1494 * don't skip %x lines (pre-load bitmaps)
1496 case '%':
1497 buf++;
1498 break;
1500 case '\r': /* CR */
1501 *buf = 0;
1502 break;
1504 case '\n': /* LF */
1505 *buf = 0;
1507 if (*start_of_line != '#') /* A comment? */
1508 line++;
1510 if (line < WPS_MAX_LINES)
1512 /* the next line starts on the next byte */
1513 subline = 0;
1514 data->format_lines[line][subline] = buf+1;
1515 start_of_line = data->format_lines[line][subline];
1517 break;
1519 case ';': /* start a new subline */
1520 *buf = 0;
1521 subline++;
1522 if (subline < WPS_MAX_SUBLINES)
1524 data->format_lines[line][subline] = buf+1;
1526 else /* exceeded max sublines, skip rest of line */
1528 while (*(++buf))
1530 if ((*buf == '\r') || (*buf == '\n'))
1532 break;
1535 buf--;
1536 subline = 0;
1538 break;
1540 buf++;
1544 #ifdef HAVE_LCD_BITMAP
1545 /* Display images */
1546 static void wps_draw_image(struct gui_wps *gwps, int n)
1548 struct screen *display = gwps->display;
1549 struct wps_data *data = gwps->data;
1550 if(data->img[n].always_display)
1551 display->set_drawmode(DRMODE_FG);
1552 else
1553 display->set_drawmode(DRMODE_SOLID);
1555 #if LCD_DEPTH > 1
1556 if(data->img[n].bm.format == FORMAT_MONO) {
1557 #endif
1558 display->mono_bitmap(data->img[n].bm.data, data->img[n].x,
1559 data->img[n].y, data->img[n].bm.width,
1560 data->img[n].bm.height);
1561 #if LCD_DEPTH > 1
1562 } else {
1563 display->transparent_bitmap((fb_data *)data->img[n].bm.data,
1564 data->img[n].x,
1565 data->img[n].y, data->img[n].bm.width,
1566 data->img[n].bm.height);
1568 #endif
1570 static void wps_display_images(struct gui_wps *gwps, bool always)
1572 if(!gwps || !gwps->data || !gwps->display) return;
1573 int n;
1574 struct wps_data *data = gwps->data;
1575 struct screen *display = gwps->display;
1576 for (n = 0; n < MAX_IMAGES; n++) {
1577 if (data->img[n].loaded) {
1578 if( (!always && data->img[n].display)
1579 || (always && data->img[n].always_display) )
1580 wps_draw_image(gwps, n);
1583 display->set_drawmode(DRMODE_SOLID);
1585 #endif
1587 void gui_wps_reset(struct gui_wps *gui_wps)
1589 if(!gui_wps || !gui_wps->data)
1590 return;
1591 gui_wps->data->wps_loaded = false;
1592 memset(&gui_wps->data->format_buffer, 0,
1593 sizeof(gui_wps->data->format_buffer));
1596 bool gui_wps_refresh(struct gui_wps *gwps, int ffwd_offset,
1597 unsigned char refresh_mode)
1599 char buf[MAX_PATH];
1600 unsigned char flags;
1601 int i;
1602 bool update_line;
1603 bool only_one_subline;
1604 bool new_subline_refresh;
1605 int search;
1606 int search_start;
1607 struct wps_data *data = gwps->data;
1608 struct wps_state *state = gwps->state;
1609 struct screen *display = gwps->display;
1610 if(!gwps || !data || !state || !display)
1612 return false;
1614 #ifdef HAVE_LCD_BITMAP
1615 int h = font_get(FONT_UI)->height;
1616 int offset = 0;
1617 gui_wps_statusbar_draw(gwps, true);
1618 if(data->wps_sb_tag && data->show_sb_on_wps)
1619 offset = STATUSBAR_HEIGHT;
1620 else if ( global_settings.statusbar && !data->wps_sb_tag)
1621 offset = STATUSBAR_HEIGHT;
1623 /* to find out wether the peak meter is enabled we
1624 assume it wasn't until we find a line that contains
1625 the peak meter. We can't use peak_meter_enabled itself
1626 because that would mean to turn off the meter thread
1627 temporarily. (That shouldn't matter unless yield
1628 or sleep is called but who knows...)
1630 bool enable_pm = false;
1632 /* Set images to not to be displayed */
1633 for (i = 0; i < MAX_IMAGES; i++) {
1634 data->img[i].display = false;
1636 #endif
1637 /* reset to first subline if refresh all flag is set */
1638 if (refresh_mode == WPS_REFRESH_ALL)
1640 for (i=0; i<WPS_MAX_LINES; i++)
1642 data->curr_subline[i] = SUBLINE_RESET;
1646 #ifdef HAVE_LCD_CHARCELLS
1647 for (i=0; i<8; i++) {
1648 if (data->wps_progress_pat[i]==0)
1649 data->wps_progress_pat[i]=display->get_locked_pattern();
1651 #endif
1653 if (!state->id3)
1655 display->stop_scroll();
1656 return false;
1659 state->ff_rewind_count = ffwd_offset;
1661 for (i = 0; i < WPS_MAX_LINES; i++)
1663 new_subline_refresh = false;
1664 only_one_subline = false;
1666 /* if time to advance to next sub-line */
1667 if (TIME_AFTER(current_tick, data->subline_expire_time[i] - 1) ||
1668 (data->curr_subline[i] == SUBLINE_RESET))
1670 /* search all sublines until the next subline with time > 0
1671 is found or we get back to the subline we started with */
1672 if (data->curr_subline[i] == SUBLINE_RESET)
1673 search_start = 0;
1674 else
1675 search_start = data->curr_subline[i];
1676 for (search=0; search<WPS_MAX_SUBLINES; search++)
1678 data->curr_subline[i]++;
1680 /* wrap around if beyond last defined subline or WPS_MAX_SUBLINES */
1681 if ((!data->format_lines[i][data->curr_subline[i]]) ||
1682 (data->curr_subline[i] == WPS_MAX_SUBLINES))
1684 if (data->curr_subline[i] == 1)
1685 only_one_subline = true;
1686 data->curr_subline[i] = 0;
1689 /* if back where we started after search or
1690 only one subline is defined on the line */
1691 if (((search > 0) && (data->curr_subline[i] == search_start)) ||
1692 only_one_subline)
1694 /* no other subline with a time > 0 exists */
1695 data->subline_expire_time[i] = current_tick + 100 * HZ;
1696 break;
1698 else
1700 /* get initial time multiplier and
1701 line type flags for this subline */
1702 format_display(gwps, buf, sizeof(buf),
1703 state->id3, state->nid3,
1704 data->format_lines[i][data->curr_subline[i]],
1705 &data->format_align[i][data->curr_subline[i]],
1706 &data->time_mult[i][data->curr_subline[i]],
1707 &data->line_type[i][data->curr_subline[i]]);
1709 /* only use this subline if subline time > 0 */
1710 if (data->time_mult[i][data->curr_subline[i]] > 0)
1712 new_subline_refresh = true;
1713 data->subline_expire_time[i] = current_tick +
1714 BASE_SUBLINE_TIME * data->time_mult[i][data->curr_subline[i]];
1715 break;
1722 update_line = false;
1724 if ( !data->format_lines[i][data->curr_subline[i]] )
1725 break;
1727 if ((data->line_type[i][data->curr_subline[i]] & refresh_mode) ||
1728 (refresh_mode == WPS_REFRESH_ALL) ||
1729 new_subline_refresh)
1731 flags = 0;
1732 #ifdef HAVE_LCD_BITMAP
1733 int left_width, left_xpos;
1734 int center_width, center_xpos;
1735 int right_width, right_xpos;
1736 int space_width;
1737 int string_height;
1738 int ypos;
1739 #endif
1741 format_display(gwps, buf, sizeof(buf),
1742 state->id3, state->nid3,
1743 data->format_lines[i][data->curr_subline[i]],
1744 &data->format_align[i][data->curr_subline[i]],
1745 &data->time_mult[i][data->curr_subline[i]],
1746 &flags);
1747 data->line_type[i][data->curr_subline[i]] = flags;
1749 #ifdef HAVE_LCD_BITMAP
1750 /* progress */
1751 if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1753 int sb_y = i*h + offset + ((h > data->progress_height + 1)
1754 ? (h - data->progress_height) / 2 : 1);
1756 if (!data->progress_end)
1757 data->progress_end=display->width;
1759 if (gwps->data->progressbar.have_bitmap_pb)
1760 gui_bitmap_scrollbar_draw(display, data->progressbar.bm,
1761 data->progress_start, sb_y,
1762 data->progress_end-data->progress_start,
1763 data->progressbar.bm.height,
1764 state->id3->length?state->id3->length:1, 0,
1765 state->id3->length?state->id3->elapsed + state->ff_rewind_count:0,
1766 HORIZONTAL);
1767 else
1768 gui_scrollbar_draw(display, data->progress_start, sb_y,
1769 data->progress_end-data->progress_start,
1770 data->progress_height,
1771 state->id3->length?state->id3->length:1, 0,
1772 state->id3->length?state->id3->elapsed + state->ff_rewind_count:0,
1773 HORIZONTAL);
1774 #ifdef AB_REPEAT_ENABLE
1775 if ( ab_repeat_mode_enabled() )
1776 ab_draw_markers(display, state->id3->length, 0, sb_y,
1777 data->progress_height);
1778 #endif
1779 update_line = true;
1781 if (flags & refresh_mode & WPS_REFRESH_PEAK_METER) {
1782 /* peak meter */
1783 int peak_meter_y;
1785 update_line = true;
1786 peak_meter_y = i * h + offset;
1788 /* The user might decide to have the peak meter in the last
1789 line so that it is only displayed if no status bar is
1790 visible. If so we neither want do draw nor enable the
1791 peak meter. */
1792 if (peak_meter_y + h <= display->height) {
1793 /* found a line with a peak meter -> remember that we must
1794 enable it later */
1795 enable_pm = true;
1796 peak_meter_screen(gwps->display, 0, peak_meter_y,
1797 MIN(h, display->height - peak_meter_y));
1800 #else
1801 /* progress */
1802 if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS) {
1803 if (data->full_line_progressbar)
1804 draw_player_fullbar(gwps, buf, sizeof(buf));
1805 else
1806 draw_player_progress(gwps);
1808 #endif
1809 #ifdef HAVE_LCD_BITMAP
1810 /* calculate different string sizes and positions */
1811 display->getstringsize((unsigned char *)" ", &space_width, &string_height);
1812 if (data->format_align[i][data->curr_subline[i]].left != 0) {
1813 display->getstringsize((unsigned char *)data->format_align[i]
1814 [data->curr_subline[i]].left,
1815 &left_width, &string_height);
1817 else {
1818 left_width = 0;
1820 left_xpos = 0;
1822 if (data->format_align[i][data->curr_subline[i]].center != 0) {
1823 display->getstringsize((unsigned char *)data->format_align[i]
1824 [data->curr_subline[i]].center,
1825 &center_width, &string_height);
1827 else {
1828 center_width = 0;
1830 center_xpos=(display->width - center_width) / 2;
1832 if (data->format_align[i][data->curr_subline[i]].right != 0) {
1833 display->getstringsize((unsigned char *)data->format_align[i]
1834 [data->curr_subline[i]].right,
1835 &right_width, &string_height);
1837 else {
1838 right_width = 0;
1840 right_xpos = (display->width - right_width);
1842 /* Checks for overlapping strings.
1843 If needed the overlapping strings will be merged, separated by a
1844 space */
1846 /* CASE 1: left and centered string overlap */
1847 /* there is a left string, need to merge left and center */
1848 if ((left_width != 0 && center_width != 0) &&
1849 (left_xpos + left_width + space_width > center_xpos)) {
1850 /* replace the former separator '\0' of left and
1851 center string with a space */
1852 *(--data->format_align[i][data->curr_subline[i]].center) = ' ';
1853 /* calculate the new width and position of the merged string */
1854 left_width = left_width + space_width + center_width;
1855 left_xpos = 0;
1856 /* there is no centered string anymore */
1857 center_width = 0;
1859 /* there is no left string, move center to left */
1860 if ((left_width == 0 && center_width != 0) &&
1861 (left_xpos + left_width > center_xpos)) {
1862 /* move the center string to the left string */
1863 data->format_align[i][data->curr_subline[i]].left =
1864 data->format_align[i][data->curr_subline[i]].center;
1865 /* calculate the new width and position of the string */
1866 left_width = center_width;
1867 left_xpos = 0;
1868 /* there is no centered string anymore */
1869 center_width = 0;
1872 /* CASE 2: centered and right string overlap */
1873 /* there is a right string, need to merge center and right */
1874 if ((center_width != 0 && right_width != 0) &&
1875 (center_xpos + center_width + space_width > right_xpos)) {
1876 /* replace the former separator '\0' of center and
1877 right string with a space */
1878 *(--data->format_align[i][data->curr_subline[i]].right) = ' ';
1879 /* move the center string to the right after merge */
1880 data->format_align[i][data->curr_subline[i]].right =
1881 data->format_align[i][data->curr_subline[i]].center;
1882 /* calculate the new width and position of the merged string */
1883 right_width = center_width + space_width + right_width;
1884 right_xpos = (display->width - right_width);
1885 /* there is no centered string anymore */
1886 center_width = 0;
1888 /* there is no right string, move center to right */
1889 if ((center_width != 0 && right_width == 0) &&
1890 (center_xpos + center_width > right_xpos)) {
1891 /* move the center string to the right string */
1892 data->format_align[i][data->curr_subline[i]].right =
1893 data->format_align[i][data->curr_subline[i]].center;
1894 /* calculate the new width and position of the string */
1895 right_width = center_width;
1896 right_xpos = (display->width - right_width);
1897 /* there is no centered string anymore */
1898 center_width = 0;
1901 /* CASE 3: left and right overlap
1902 There is no center string anymore, either there never
1903 was one or it has been merged in case 1 or 2 */
1904 /* there is a left string, need to merge left and right */
1905 if ((left_width != 0 && center_width == 0 && right_width != 0) &&
1906 (left_xpos + left_width + space_width > right_xpos)) {
1907 /* replace the former separator '\0' of left and
1908 right string with a space */
1909 *(--data->format_align[i][data->curr_subline[i]].right) = ' ';
1910 /* calculate the new width and position of the string */
1911 left_width = left_width + space_width + right_width;
1912 left_xpos = 0;
1913 /* there is no right string anymore */
1914 right_width = 0;
1916 /* there is no left string, move right to left */
1917 if ((left_width == 0 && center_width == 0 && right_width != 0) &&
1918 (left_xpos + left_width > right_xpos)) {
1919 /* move the right string to the left string */
1920 data->format_align[i][data->curr_subline[i]].left =
1921 data->format_align[i][data->curr_subline[i]].right;
1922 /* calculate the new width and position of the string */
1923 left_width = right_width;
1924 left_xpos = 0;
1925 /* there is no right string anymore */
1926 right_width = 0;
1929 #endif
1931 if (flags & WPS_REFRESH_SCROLL) {
1933 /* scroll line */
1934 if ((refresh_mode & WPS_REFRESH_SCROLL) ||
1935 new_subline_refresh) {
1936 #ifdef HAVE_LCD_BITMAP
1937 ypos = (i*string_height)+display->getymargin();
1938 update_line = true;
1940 if (left_width>display->width) {
1941 display->puts_scroll(0, i,
1942 (unsigned char *)data->format_align[i]
1943 [data->curr_subline[i]].left);
1944 } else {
1945 /* clear the line first */
1946 display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1947 display->fillrect(0, ypos, display->width, string_height);
1948 display->set_drawmode(DRMODE_SOLID);
1950 /* Nasty hack: we output an empty scrolling string,
1951 which will reset the scroller for that line */
1952 display->puts_scroll(0, i, (unsigned char *)"");
1954 /* print aligned strings */
1955 if (left_width != 0)
1957 display->putsxy(left_xpos, ypos,
1958 (unsigned char *)data->format_align[i]
1959 [data->curr_subline[i]].left);
1961 if (center_width != 0)
1963 display->putsxy(center_xpos, ypos,
1964 (unsigned char *)data->format_align[i]
1965 [data->curr_subline[i]].center);
1967 if (right_width != 0)
1969 display->putsxy(right_xpos, ypos,
1970 (unsigned char *)data->format_align[i]
1971 [data->curr_subline[i]].right);
1974 #else
1975 display->puts_scroll(0, i, buf);
1976 update_line = true;
1977 #endif
1980 else if (flags & (WPS_REFRESH_DYNAMIC | WPS_REFRESH_STATIC))
1982 /* dynamic / static line */
1983 if ((refresh_mode & (WPS_REFRESH_DYNAMIC|WPS_REFRESH_STATIC)) ||
1984 new_subline_refresh)
1986 #ifdef HAVE_LCD_BITMAP
1987 ypos = (i*string_height)+display->getymargin();
1988 update_line = true;
1990 /* clear the line first */
1991 display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1992 display->fillrect(0, ypos, display->width, string_height);
1993 display->set_drawmode(DRMODE_SOLID);
1995 /* Nasty hack: we output an empty scrolling string,
1996 which will reset the scroller for that line */
1997 display->puts_scroll(0, i, (unsigned char *)"");
1999 /* print aligned strings */
2000 if (left_width != 0)
2002 display->putsxy(left_xpos, ypos,
2003 (unsigned char *)data->format_align[i]
2004 [data->curr_subline[i]].left);
2006 if (center_width != 0)
2008 display->putsxy(center_xpos, ypos,
2009 (unsigned char *)data->format_align[i]
2010 [data->curr_subline[i]].center);
2012 if (right_width != 0)
2014 display->putsxy(right_xpos, ypos,
2015 (unsigned char *)data->format_align[i]
2016 [data->curr_subline[i]].right);
2018 #else
2019 update_line = true;
2020 display->puts(0, i, buf);
2021 #endif
2025 #ifdef HAVE_LCD_BITMAP
2026 if (update_line) {
2027 wps_display_images(gwps,false);
2029 #endif
2032 #ifdef HAVE_LCD_BITMAP
2033 /* Display all images */
2034 wps_display_images(gwps,true);
2035 display->update();
2036 /* Now we know wether the peak meter is used.
2037 So we can enable / disable the peak meter thread */
2038 data->peak_meter_enabled = enable_pm;
2039 #endif
2041 #ifdef CONFIG_BACKLIGHT
2042 if (global_settings.caption_backlight && state->id3) {
2043 /* turn on backlight n seconds before track ends, and turn it off n
2044 seconds into the new track. n == backlight_timeout, or 5s */
2045 int n = backlight_timeout_value[global_settings.backlight_timeout]
2046 * 1000;
2048 if ( n < 1000 )
2049 n = 5000; /* use 5s if backlight is always on or off */
2051 if ((state->id3->elapsed < 1000) ||
2052 ((state->id3->length - state->id3->elapsed) < (unsigned)n))
2053 backlight_on();
2055 #endif
2056 #ifdef HAVE_REMOTE_LCD
2057 if (global_settings.remote_caption_backlight && state->id3) {
2058 /* turn on remote backlight n seconds before track ends, and turn it
2059 off n seconds into the new track. n == remote_backlight_timeout,
2060 or 5s */
2061 int n = backlight_timeout_value[global_settings.remote_backlight_timeout]
2062 * 1000;
2064 if ( n < 1000 )
2065 n = 5000; /* use 5s if backlight is always on or off */
2067 if ((state->id3->elapsed < 1000) ||
2068 ((state->id3->length - state->id3->elapsed) < (unsigned)n))
2069 remote_backlight_on();
2071 #endif
2072 return true;
2075 #ifdef HAVE_LCD_CHARCELLS
2076 static bool draw_player_progress(struct gui_wps *gwps)
2078 char player_progressbar[7];
2079 char binline[36];
2080 int songpos = 0;
2081 int i,j;
2082 struct wps_state *state = gwps->state;
2083 struct screen *display = gwps->display;
2084 if (!state->id3)
2085 return false;
2087 memset(binline, 1, sizeof binline);
2088 memset(player_progressbar, 1, sizeof player_progressbar);
2090 if(state->id3->elapsed >= state->id3->length)
2091 songpos = 0;
2092 else
2094 if(state->wps_time_countup == false)
2095 songpos = ((state->id3->elapsed - state->ff_rewind_count) * 36) /
2096 state->id3->length;
2097 else
2098 songpos = ((state->id3->elapsed + state->ff_rewind_count) * 36) /
2099 state->id3->length;
2101 for (i=0; i < songpos; i++)
2102 binline[i] = 0;
2104 for (i=0; i<=6; i++) {
2105 for (j=0;j<5;j++) {
2106 player_progressbar[i] <<= 1;
2107 player_progressbar[i] += binline[i*5+j];
2110 display->define_pattern(gwps->data->wps_progress_pat[0], player_progressbar);
2111 return true;
2114 static char map_fullbar_char(char ascii_val)
2116 if (ascii_val >= '0' && ascii_val <= '9') {
2117 return(ascii_val - '0');
2119 else if (ascii_val == ':') {
2120 return(10);
2122 else
2123 return(11); /* anything besides a number or ':' is mapped to <blank> */
2126 static void draw_player_fullbar(struct gui_wps *gwps, char* buf, int buf_size)
2128 int i,j,lcd_char_pos;
2130 char player_progressbar[7];
2131 char binline[36];
2132 static const char numbers[12][4][3]={
2133 {{1,1,1},{1,0,1},{1,0,1},{1,1,1}},/*0*/
2134 {{0,1,0},{1,1,0},{0,1,0},{0,1,0}},/*1*/
2135 {{1,1,1},{0,0,1},{0,1,0},{1,1,1}},/*2*/
2136 {{1,1,1},{0,0,1},{0,1,1},{1,1,1}},/*3*/
2137 {{1,0,0},{1,1,0},{1,1,1},{0,1,0}},/*4*/
2138 {{1,1,1},{1,1,0},{0,0,1},{1,1,0}},/*5*/
2139 {{1,1,1},{1,0,0},{1,1,1},{1,1,1}},/*6*/
2140 {{1,1,1},{0,0,1},{0,1,0},{1,0,0}},/*7*/
2141 {{1,1,1},{1,1,1},{1,0,1},{1,1,1}},/*8*/
2142 {{1,1,1},{1,1,1},{0,0,1},{1,1,1}},/*9*/
2143 {{0,0,0},{0,1,0},{0,0,0},{0,1,0}},/*:*/
2144 {{0,0,0},{0,0,0},{0,0,0},{0,0,0}} /*<blank>*/
2147 int songpos = 0;
2148 int digits[6];
2149 int time;
2150 char timestr[7];
2152 struct wps_state *state = gwps->state;
2153 struct screen *display = gwps->display;
2154 struct wps_data *data = gwps->data;
2156 for (i=0; i < buf_size; i++)
2157 buf[i] = ' ';
2159 if(state->id3->elapsed >= state->id3->length)
2160 songpos = 55;
2161 else {
2162 if(state->wps_time_countup == false)
2163 songpos = ((state->id3->elapsed - state->ff_rewind_count) * 55) /
2164 state->id3->length;
2165 else
2166 songpos = ((state->id3->elapsed + state->ff_rewind_count) * 55) /
2167 state->id3->length;
2170 time=(state->id3->elapsed + state->ff_rewind_count);
2172 memset(timestr, 0, sizeof(timestr));
2173 gui_wps_format_time(timestr, sizeof(timestr), time);
2174 for(lcd_char_pos=0; lcd_char_pos<6; lcd_char_pos++) {
2175 digits[lcd_char_pos] = map_fullbar_char(timestr[lcd_char_pos]);
2178 /* build the progressbar-icons */
2179 for (lcd_char_pos=0; lcd_char_pos<6; lcd_char_pos++) {
2180 memset(binline, 0, sizeof binline);
2181 memset(player_progressbar, 0, sizeof player_progressbar);
2183 /* make the character (progressbar & digit)*/
2184 for (i=0; i<7; i++) {
2185 for (j=0;j<5;j++) {
2186 /* make the progressbar */
2187 if (lcd_char_pos==(songpos/5)) {
2188 /* partial */
2189 if ((j<(songpos%5))&&(i>4))
2190 binline[i*5+j] = 1;
2191 else
2192 binline[i*5+j] = 0;
2194 else {
2195 if (lcd_char_pos<(songpos/5)) {
2196 /* full character */
2197 if (i>4)
2198 binline[i*5+j] = 1;
2201 /* insert the digit */
2202 if ((j<3)&&(i<4)) {
2203 if (numbers[digits[lcd_char_pos]][i][j]==1)
2204 binline[i*5+j] = 1;
2209 for (i=0; i<=6; i++) {
2210 for (j=0;j<5;j++) {
2211 player_progressbar[i] <<= 1;
2212 player_progressbar[i] += binline[i*5+j];
2216 display->define_pattern(data->wps_progress_pat[lcd_char_pos+1],
2217 player_progressbar);
2218 buf[lcd_char_pos]=data->wps_progress_pat[lcd_char_pos+1];
2222 /* make rest of the progressbar if necessary */
2223 if (songpos/5>5) {
2225 /* set the characters positions that use the full 5 pixel wide bar */
2226 for (lcd_char_pos=6; lcd_char_pos < (songpos/5); lcd_char_pos++)
2227 buf[lcd_char_pos] = 0x86; /* '_' */
2229 /* build the partial bar character for the tail character position */
2230 memset(binline, 0, sizeof binline);
2231 memset(player_progressbar, 0, sizeof player_progressbar);
2233 for (i=5; i<7; i++) {
2234 for (j=0;j<5;j++) {
2235 if (j<(songpos%5)) {
2236 binline[i*5+j] = 1;
2241 for (i=0; i<7; i++) {
2242 for (j=0;j<5;j++) {
2243 player_progressbar[i] <<= 1;
2244 player_progressbar[i] += binline[i*5+j];
2248 display->define_pattern(data->wps_progress_pat[7],player_progressbar);
2250 buf[songpos/5]=data->wps_progress_pat[7];
2253 #endif
2255 /* set volume */
2256 void setvol(void)
2258 if (global_settings.volume < sound_min(SOUND_VOLUME))
2259 global_settings.volume = sound_min(SOUND_VOLUME);
2260 if (global_settings.volume > sound_max(SOUND_VOLUME))
2261 global_settings.volume = sound_max(SOUND_VOLUME);
2262 sound_set_volume(global_settings.volume);
2263 settings_save();
2265 /* return true if screen restore is needed
2266 return false otherwise
2268 bool update_onvol_change(struct gui_wps * gwps)
2270 gui_wps_statusbar_draw(gwps, false);
2271 gui_wps_refresh(gwps, 0, WPS_REFRESH_NON_STATIC);
2273 #ifdef HAVE_LCD_CHARCELLS
2274 gui_splash(gwps->display,0, false, "Vol: %d dB ",
2275 sound_val2phys(SOUND_VOLUME, global_settings.volume));
2276 return true;
2277 #endif
2278 return false;
2281 bool ffwd_rew(int button)
2283 static const int ff_rew_steps[] = {
2284 1000, 2000, 3000, 4000,
2285 5000, 6000, 8000, 10000,
2286 15000, 20000, 25000, 30000,
2287 45000, 60000
2290 unsigned int step = 0; /* current ff/rewind step */
2291 unsigned int max_step = 0; /* maximum ff/rewind step */
2292 int ff_rewind_count = 0; /* current ff/rewind count (in ticks) */
2293 int direction = -1; /* forward=1 or backward=-1 */
2294 long accel_tick = 0; /* next time at which to bump the step size */
2295 bool exit = false;
2296 bool usb = false;
2297 int i = 0;
2299 while (!exit)
2301 switch ( button )
2303 case WPS_FFWD:
2304 #ifdef WPS_RC_FFWD
2305 case WPS_RC_FFWD:
2306 #endif
2307 direction = 1;
2308 case WPS_REW:
2309 #ifdef WPS_RC_REW
2310 case WPS_RC_REW:
2311 #endif
2312 if (wps_state.ff_rewind)
2314 if (direction == 1)
2316 /* fast forwarding, calc max step relative to end */
2317 max_step = (wps_state.id3->length -
2318 (wps_state.id3->elapsed +
2319 ff_rewind_count)) *
2320 FF_REWIND_MAX_PERCENT / 100;
2322 else
2324 /* rewinding, calc max step relative to start */
2325 max_step = (wps_state.id3->elapsed + ff_rewind_count) *
2326 FF_REWIND_MAX_PERCENT / 100;
2329 max_step = MAX(max_step, MIN_FF_REWIND_STEP);
2331 if (step > max_step)
2332 step = max_step;
2334 ff_rewind_count += step * direction;
2336 if (global_settings.ff_rewind_accel != 0 &&
2337 current_tick >= accel_tick)
2339 step *= 2;
2340 accel_tick = current_tick +
2341 global_settings.ff_rewind_accel*HZ;
2344 else
2346 if ( (audio_status() & AUDIO_STATUS_PLAY) &&
2347 wps_state.id3 && wps_state.id3->length )
2349 if (!wps_state.paused)
2350 #if (CONFIG_CODEC == SWCODEC)
2351 audio_pre_ff_rewind();
2352 #else
2353 audio_pause();
2354 #endif
2355 #if CONFIG_KEYPAD == PLAYER_PAD
2356 FOR_NB_SCREENS(i)
2357 gui_wps[i].display->stop_scroll();
2358 #endif
2359 if (direction > 0)
2360 status_set_ffmode(STATUS_FASTFORWARD);
2361 else
2362 status_set_ffmode(STATUS_FASTBACKWARD);
2364 wps_state.ff_rewind = true;
2366 step = ff_rew_steps[global_settings.ff_rewind_min_step];
2368 accel_tick = current_tick +
2369 global_settings.ff_rewind_accel*HZ;
2371 else
2372 break;
2375 if (direction > 0) {
2376 if ((wps_state.id3->elapsed + ff_rewind_count) >
2377 wps_state.id3->length)
2378 ff_rewind_count = wps_state.id3->length -
2379 wps_state.id3->elapsed;
2381 else {
2382 if ((int)(wps_state.id3->elapsed + ff_rewind_count) < 0)
2383 ff_rewind_count = -wps_state.id3->elapsed;
2386 FOR_NB_SCREENS(i)
2387 gui_wps_refresh(&gui_wps[i],
2388 (wps_state.wps_time_countup == false)?
2389 ff_rewind_count:-ff_rewind_count,
2390 WPS_REFRESH_PLAYER_PROGRESS |
2391 WPS_REFRESH_DYNAMIC);
2393 break;
2395 case WPS_PREV:
2396 case WPS_NEXT:
2397 #ifdef WPS_RC_PREV
2398 case WPS_RC_PREV:
2399 case WPS_RC_NEXT:
2400 #endif
2401 wps_state.id3->elapsed = wps_state.id3->elapsed+ff_rewind_count;
2402 audio_ff_rewind(wps_state.id3->elapsed);
2403 ff_rewind_count = 0;
2404 wps_state.ff_rewind = false;
2405 status_set_ffmode(0);
2406 #if (CONFIG_CODEC != SWCODEC)
2407 if (!wps_state.paused)
2408 audio_resume();
2409 #endif
2410 #ifdef HAVE_LCD_CHARCELLS
2411 gui_wps_display();
2412 #endif
2413 exit = true;
2414 break;
2416 default:
2417 if(default_event_handler(button) == SYS_USB_CONNECTED) {
2418 status_set_ffmode(0);
2419 usb = true;
2420 exit = true;
2422 break;
2424 if (!exit)
2425 button = button_get(true);
2428 return usb;
2431 bool gui_wps_display(void)
2433 int i;
2434 if (!wps_state.id3 && !(audio_status() & AUDIO_STATUS_PLAY))
2436 global_settings.resume_index = -1;
2437 #ifdef HAVE_LCD_CHARCELLS
2438 gui_syncsplash(HZ, true, str(LANG_END_PLAYLIST_PLAYER));
2439 #else
2440 gui_syncstatusbar_draw(&statusbars, true);
2441 gui_syncsplash(HZ, true, str(LANG_END_PLAYLIST_RECORDER));
2442 #endif
2443 return true;
2445 else
2447 FOR_NB_SCREENS(i)
2449 gui_wps[i].display->clear_display();
2450 if (!gui_wps[i].data->wps_loaded) {
2451 if ( !gui_wps[i].data->format_buffer[0] ) {
2452 /* set the default wps for the main-screen */
2453 if(i == 0)
2455 #ifdef HAVE_LCD_BITMAP
2456 wps_data_load(gui_wps[i].data,
2457 "%s%?it<%?in<%in. |>%it|%fn>\n"
2458 "%s%?ia<%ia|%?d2<%d2|(root)>>\n"
2459 "%s%?id<%id|%?d1<%d1|(root)>> %?iy<(%iy)|>\n"
2460 "\n"
2461 "%al%pc/%pt%ar[%pp:%pe]\n"
2462 "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n"
2463 "%pb\n"
2464 "%pm\n", false);
2465 #else
2466 wps_data_load(gui_wps[i].data,
2467 "%s%pp/%pe: %?it<%it|%fn> - %?ia<%ia|%d2> - %?id<%id|%d1>\n"
2468 "%pc%?ps<*|/>%pt\n", false);
2469 #endif
2471 #if NB_SCREENS == 2
2472 /* set the default wps for the remote-screen */
2473 else if(i == 1)
2475 wps_data_load(gui_wps[i].data,
2476 "%s%?ia<%ia|%?d2<%d2|(root)>>\n"
2477 "%s%?it<%?in<%in. |>%it|%fn>\n"
2478 "%al%pc/%pt%ar[%pp:%pe]\n"
2479 "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n"
2480 "%pb", false);
2482 #endif
2487 yield();
2488 FOR_NB_SCREENS(i)
2490 gui_wps_refresh(&gui_wps[i], 0, WPS_REFRESH_ALL);
2492 return false;
2495 bool update(struct gui_wps *gwps)
2497 bool track_changed = audio_has_changed_track();
2498 bool retcode = false;
2500 gwps->state->nid3 = audio_next_track();
2501 if (track_changed)
2503 gwps->display->stop_scroll();
2504 gwps->state->id3 = audio_current_track();
2505 if (gui_wps_display())
2506 retcode = true;
2507 else{
2508 gui_wps_refresh(gwps, 0, WPS_REFRESH_ALL);
2511 if (gwps->state->id3)
2512 memcpy(gwps->state->current_track_path, gwps->state->id3->path,
2513 sizeof(gwps->state->current_track_path));
2516 if (gwps->state->id3)
2517 gui_wps_refresh(gwps, 0, WPS_REFRESH_NON_STATIC);
2519 gui_wps_statusbar_draw(gwps, false);
2521 return retcode;
2524 #ifdef WPS_KEYLOCK
2525 void display_keylock_text(bool locked)
2527 char* s;
2528 int i;
2529 FOR_NB_SCREENS(i)
2530 gui_wps[i].display->stop_scroll();
2532 #ifdef HAVE_LCD_CHARCELLS
2533 if(locked)
2534 s = str(LANG_KEYLOCK_ON_PLAYER);
2535 else
2536 s = str(LANG_KEYLOCK_OFF_PLAYER);
2537 #else
2538 if(locked)
2539 s = str(LANG_KEYLOCK_ON_RECORDER);
2540 else
2541 s = str(LANG_KEYLOCK_OFF_RECORDER);
2542 #endif
2543 gui_syncsplash(HZ, true, s);
2546 void waitfor_nokey(void)
2548 /* wait until all keys are released */
2549 while (button_get(false) != BUTTON_NONE)
2550 yield();
2552 #endif