1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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"
32 #include "powermgmt.h"
35 #ifdef HAVE_LCD_CHARCELLS
39 #include "mp3_playback.h"
40 #include "backlight.h"
44 #include "scrollbar.h"
47 #ifdef HAVE_LCD_BITMAP
48 #include "peakmeter.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
);
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)
84 * returns the image_id between
87 #ifdef HAVE_LCD_BITMAP
88 static int get_image_id(int c
)
90 if(c
>= 'a' && c
<= 'z')
92 else if(c
>= 'A' && c
<= 'Z')
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
109 * it returns true if one of these tags is found and handled
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;
118 #ifndef HAVE_LCD_BITMAP
119 /* no bitmap-lcd == no bitmap loading */
123 buf
= skip_utf8_bom(buf
);
134 #ifdef HAVE_LCD_BITMAP
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;
151 /* Backdrop image - must be the same size as the LCD */
155 char imgname
[MAX_PATH
];
157 /* format: %X|filename.bmp| */
160 pos
= strchr(ptr
, '|');
162 (int)sizeof(imgname
)-ROCKBOX_DIR_LEN
-2)
164 memcpy(imgname
, bmpdir
, bmpdirlen
);
165 imgname
[bmpdirlen
] = '/';
166 memcpy(&imgname
[bmpdirlen
+1],
168 imgname
[bmpdirlen
+1+pos
-ptr
] = 0;
172 /* filename too long */
177 return load_wps_backdrop(imgname
);
184 /* progress bar image */
189 char imgname
[MAX_PATH
];
191 /* format: %P|filename.bmp| */
194 pos
= strchr(ptr
, '|');
196 (int)sizeof(imgname
)-ROCKBOX_DIR_LEN
-2)
198 memcpy(imgname
, bmpdir
, bmpdirlen
);
199 imgname
[bmpdirlen
] = '/';
200 memcpy(&imgname
[bmpdirlen
+1],
202 imgname
[bmpdirlen
+1+pos
-ptr
] = 0;
205 /* filename too long */
211 data
->progressbar
.bm
.data
=data
->img_buf_ptr
;
212 ret
= read_bmp_file(imgname
, &data
->progressbar
.bm
,
214 FORMAT_ANY
|FORMAT_TRANSPARENT
);
220 /* Always consume an even number of bytes */
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;
239 /* Preload images so the %xd# tag can display it */
245 char imgname
[MAX_PATH
];
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
, '|');
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 */
270 /* check the image number and load state */
271 if (data
->img
[n
].loaded
)
273 /* Skip the rest of the line */
281 pos
= strchr(ptr
, '|');
287 (int)sizeof(imgname
)-ROCKBOX_DIR_LEN
-2)
289 memcpy(imgname
, bmpdir
, bmpdirlen
);
290 imgname
[bmpdirlen
] = '/';
291 memcpy(&imgname
[bmpdirlen
+1],
293 imgname
[bmpdirlen
+1+pos
-ptr
] = 0;
296 /* filename too long */
302 pos
= strchr(ptr
, '|');
304 data
->img
[n
].x
= atoi(ptr
);
307 /* weird syntax, bail out */
314 pos
= strchr(ptr
, '|');
316 data
->img
[n
].y
= atoi(ptr
);
319 /* weird syntax, bail out */
325 data
->img
[n
].bm
.data
= data
->img_buf_ptr
;
326 ret
= read_bmp_file(imgname
, &data
->img
[n
].bm
,
328 FORMAT_ANY
|FORMAT_TRANSPARENT
);
334 /* Always consume an even number of bytes */
337 data
->img_buf_ptr
+= ret
;
338 data
->img_buf_free
-= ret
;
339 data
->img
[n
].loaded
= true;
341 data
->img
[n
].always_display
= true;
352 /* no of these tags found */
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
)
366 else if(wps
->data
->wps_sb_tag
)
369 gui_statusbar_draw(wps
->statusbar
, force
);
372 #define gui_wps_statusbar_draw(wps, force) \
373 gui_statusbar_draw((wps)->statusbar, (force))
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
)
389 const char* last_sep
;
392 sep
= path
+ strlen(path
);
409 if (level
|| (last_sep
<= sep
))
414 len
= MIN(last_sep
- sep
, buf_size
- 1);
415 strncpy(buf
, sep
+ 1, len
);
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
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
,
438 unsigned char* tag_len
,
439 unsigned short* subline_time_mult
,
440 unsigned char* flags
,
443 struct mp3entry
*id3
= cid3
; /* default to current song */
445 #ifndef HAVE_LCD_CHARCELLS
448 if ((0 == tag
[0]) || (0 == tag
[1]))
460 case 'I': /* ID3 Information */
461 id3
= nid3
; /* display next-song data */
462 *flags
|= WPS_REFRESH_DYNAMIC
;
464 return NULL
; /* no such info (yet) */
466 case 'i': /* ID3 Information */
467 *flags
|= WPS_REFRESH_STATIC
;
470 case 't': /* ID3 Title */
473 case 'a': /* ID3 Artist */
476 case 'n': /* ID3 Track Number */
477 if (id3
->track_string
)
478 return id3
->track_string
;
481 snprintf(buf
, buf_size
, "%d", id3
->tracknum
);
486 case 'd': /* ID3 Album/Disc */
489 case 'c': /* ID3 Composer */
490 return id3
->composer
;
492 case 'C': /* ID3 Comment */
495 case 'A': /* ID3 Albumartist */
496 return id3
->albumartist
;
499 if( id3
->year_string
)
500 return id3
->year_string
;
503 snprintf(buf
, buf_size
, "%d", id3
->year
);
508 case 'g': /* genre */
509 return id3_get_genre(id3
);
511 case 'v': /* id3 version */
512 switch (id3
->id3version
)
535 case 'F': /* File Information */
537 *flags
|= WPS_REFRESH_DYNAMIC
;
539 return NULL
; /* no such info (yet) */
541 case 'f': /* File Information */
542 *flags
|= WPS_REFRESH_STATIC
;
545 case 'v': /* VBR file? */
546 return id3
->vbr
? "(avg)" : NULL
;
548 case 'b': /* File Bitrate */
550 snprintf(buf
, buf_size
, "%d", id3
->bitrate
);
552 snprintf(buf
, buf_size
, "?");
555 case 'f': /* File Frequency */
556 snprintf(buf
, buf_size
, "%ld", id3
->frequency
);
559 case 'p': /* File 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
, '.');
583 case 's': /* File Size (in kilobytes) */
584 snprintf(buf
, buf_size
, "%ld", id3
->filesize
/ 1024);
587 case 'c': /* File Codec */
588 if(id3
->codectype
== AFMT_UNKNOWN
)
589 *intval
= AFMT_NUM_CODECS
;
591 *intval
= id3
->codectype
;
592 return id3_get_codec(id3
);
596 case 'p': /* Playlist/Song Information */
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;
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
, '|');
615 char *p
=strchr(prev
+1, '|');
617 wps_data
->progress_height
=atoi(++prev
);
618 prev
=strchr(prev
, '|');
621 wps_data
->progress_start
=atoi(++prev
);
622 prev
=strchr(prev
, '|');
625 wps_data
->progress_end
=atoi(++prev
);
626 prev
=strchr(prev
, '|');
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;
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
649 snprintf(buf
, buf_size
, " ");
653 /* Tell the user if we have an OldPlayer */
654 snprintf(buf
, buf_size
, " <Old LCD> ");
658 case 'p': /* Playlist Position */
659 *flags
|= WPS_REFRESH_STATIC
;
660 snprintf(buf
, buf_size
, "%d",
661 playlist_get_display_index());
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());
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
);
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
);
686 case 't': /* Total Time */
687 *flags
|= WPS_REFRESH_STATIC
;
688 format_time(buf
, buf_size
, id3
->length
);
691 #ifdef HAVE_LCD_BITMAP
692 case 'm': /* Peak Meter */
693 *flags
|= WPS_REFRESH_PEAK_METER
;
696 case 's': /* shuffle */
697 *flags
|= WPS_REFRESH_DYNAMIC
;
698 if ( global_settings
.playlist_shuffle
)
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;
716 #if (CONFIG_CODEC == SWCODEC)
717 case 'S': /* DSP/Equalizer/Sound settings */
720 case 'p': /* pitch */
721 *intval
= sound_get_pitch();
722 snprintf(buf
, buf_size
, "%d.%d",
723 *intval
/ 10, *intval
% 10);
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
);
738 /* playback status */
740 *flags
|= WPS_REFRESH_DYNAMIC
;
741 int status
= audio_status();
743 if (status
== AUDIO_STATUS_PLAY
&& \
744 !(status
& AUDIO_STATUS_PAUSE
))
746 if (audio_status() & AUDIO_STATUS_PAUSE
&& \
747 (! status_get_ffmode()))
749 if (status_get_ffmode() == STATUS_FASTFORWARD
)
751 if (status_get_ffmode() == STATUS_FASTBACKWARD
)
753 snprintf(buf
, buf_size
, "%d", *intval
);
756 #ifdef HAS_BUTTON_HOLD
758 *flags
|= WPS_REFRESH_DYNAMIC
;
764 #ifdef HAS_REMOTE_BUTTON_HOLD
765 case 'r': /* remote hold */
766 *flags
|= WPS_REFRESH_DYNAMIC
;
767 if (remote_button_hold())
775 case 'b': /* battery info */
776 *flags
|= WPS_REFRESH_DYNAMIC
;
779 case 'l': /* battery level */
781 int l
= battery_level();
782 limit
= MAX(limit
, 2);
785 snprintf(buf
, buf_size
, "%d", l
);
786 /* First enum is used for "unknown level". */
787 *intval
= (limit
- 1) * l
/ 100 + 1 + 1;
797 case 'v': /* battery voltage */
799 unsigned int v
= battery_voltage();
800 snprintf(buf
, buf_size
, "%d.%02d", v
/100, v
%100);
804 case 't': /* estimated battery time */
806 int t
= battery_time();
808 snprintf(buf
, buf_size
, "%dh %dm", t
/ 60, t
% 60);
810 strncpy(buf
, "?h ?m", buf_size
);
814 case 's': /* sleep timer */
816 if (get_sleep_timer() == 0)
822 format_time(buf
, buf_size
, \
823 get_sleep_timer() * 1000);
829 case 'p': /* External power plugged in? */
831 if(charger_input_state
==CHARGER
)
837 #if CONFIG_CHARGING >= CHARGING_MONITOR
838 case 'c': /* Charging */
840 if (charge_state
== CHARGING
|| charge_state
== TOPOFF
) {
850 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
851 case 'l': /* VIRTUAL_LED */
855 case 'h': /* Only one we have so far HDD LED */
856 *flags
|= WPS_REFRESH_DYNAMIC
;
866 case 'D': /* Directory path information */
867 id3
= nid3
; /* next song please! */
868 *flags
|= WPS_REFRESH_DYNAMIC
;
870 return NULL
; /* no such info (yet) */
872 case 'd': /* Directory path information */
874 int level
= tag
[1] - '0';
875 *flags
|= WPS_REFRESH_STATIC
;
877 if ((0 < level
) && (9 > level
))
879 return get_dir(buf
, buf_size
, id3
->path
, level
);
884 case 't': /* set sub line time multiplier */
888 bool have_point
= false;
889 bool have_tenth
= false;
891 while (((tag
[d
] >= '0') &&
897 time_mult
= time_mult
* 10;
898 time_mult
= time_mult
+ tag
[d
] - '0';
913 if (have_tenth
== false)
916 *subline_time_mult
= time_mult
;
923 case 'r': /* Runtime database Information and Replaygain */
926 case 'p': /* Playcount */
927 *flags
|= WPS_REFRESH_STATIC
;
928 snprintf(buf
, buf_size
, "%ld", cid3
->playcount
);
930 case 'r': /* Rating */
931 *flags
|= WPS_REFRESH_STATIC
;
932 *intval
= cid3
->rating
+1;
933 snprintf(buf
, buf_size
, "%d", cid3
->rating
);
935 #if CONFIG_CODEC == SWCODEC
936 case 'g': /* ReplayGain */
937 *flags
|= WPS_REFRESH_STATIC
;
938 if (global_settings
.replaygain
== 0)
939 *intval
= 1; /* off */
942 int type
= get_replaygain_mode(
943 id3
->track_gain_string
!= NULL
,
944 id3
->album_gain_string
!= NULL
);
947 *intval
= 6; /* no tag */
951 if (global_settings
.replaygain_type
== REPLAYGAIN_SHUFFLE
)
963 strncpy(buf
, id3
->track_gain_string
, buf_size
);
967 strncpy(buf
, id3
->album_gain_string
, buf_size
);
975 case 'c': /* Real Time Clock display */
976 *flags
|= WPS_REFRESH_DYNAMIC
;
977 #if CONFIG_RTC == RTC_DS1339_DS3231
986 struct tm
* tm
= get_time();
988 for (i
=1;/*break*/;i
++) {
991 case 'a': /* abbreviated weekday name (Sun..Sat) */
993 if (value
> 6 || value
< 0) continue;
995 bufptr
,buf_size
,"%s",str(dayname
[value
]));
999 case 'b': /* abbreviated month name (Jan..Dec) */
1001 if (value
> 11 || value
< 0) continue;
1003 bufptr
,buf_size
,"%s",str(monthname
[value
]));
1007 case 'd': /* day of month (01..31) */
1008 value
= tm
->tm_mday
;
1009 if (value
> 31 || value
< 1) continue;
1012 case 'e': /* day of month, blank padded ( 1..31) */
1013 value
= tm
->tm_mday
;
1014 if (value
> 31 || value
< 1) continue;
1017 case 'H': /* hour (00..23) */
1018 value
= tm
->tm_hour
;
1019 if (value
> 23) continue;
1022 case 'k': /* hour ( 0..23) */
1023 value
= tm
->tm_hour
;
1024 if (value
> 23) continue;
1027 case 'I': /* hour (01..12) */
1028 value
= tm
->tm_hour
;
1029 if (value
> 23) continue;
1031 if (value
== 0) value
= 12;
1034 case 'l': /* hour ( 1..12) */
1035 value
= tm
->tm_hour
;
1036 if (value
> 23 || value
< 0) continue;
1038 if (value
== 0) value
= 12;
1041 case 'm': /* month (01..12) */
1043 if (value
> 11 || value
< 0) continue;
1047 case 'M': /* minute (00..59) */
1049 if (value
> 59 || value
< 0) continue;
1052 case 'S': /* second (00..59) */
1054 if (value
> 59 || value
< 0) continue;
1057 case 'y': /* last two digits of year (00..99) */
1058 value
= tm
->tm_year
;
1062 case 'Y': /* year (1970...) */
1063 value
= tm
->tm_year
;
1064 if (value
> 199 || value
< 100) continue;
1068 case 'p': /* upper case AM or PM indicator */
1069 if (tm
->tm_hour
/12 == 0) format
= "AM";
1071 snprintf(bufptr
,buf_size
,"%s",format
);
1075 case 'P': /* lower case am or pm indicator */
1076 if (tm
->tm_hour
/12 == 0) format
= "am";
1078 snprintf(bufptr
,buf_size
,"%s",format
);
1082 case 'u': /* day of week (1..7); 1 is Monday */
1083 value
= tm
->tm_wday
;
1084 if (value
< 0 || value
> 6) continue;
1088 case 'w': /* day of week (0..6); 0 is Sunday */
1089 value
= tm
->tm_wday
;
1090 if (value
< 0 || value
> 6) continue;
1094 if (tag
[i
] == 'c') {
1098 } else if (tag
[i
] == '\n') {
1102 snprintf(bufptr
,buf_size
,"%c",tag
[i
]);
1107 if (value
< 0) break;
1109 value
= snprintf(bufptr
, buf_size
, format
, value
);
1116 #endif /* CONFIG_RTC */
1117 #if CONFIG_CODEC == SWCODEC
1119 *flags
|= WPS_REFRESH_DYNAMIC
;
1122 case 'd': /* crossfeed */
1123 if(global_settings
.crossfeed
)
1127 case 'f': /* crossfade */
1128 *intval
= global_settings
.crossfade
+1;
1129 snprintf(buf
, buf_size
, "%d", global_settings
.crossfade
);
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
)
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
);
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
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
)
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;
1184 #ifdef HAVE_LCD_BITMAP
1185 if(data
&& *(fmt
) == 'x' && *(fmt
+1) == 'd' )
1189 if(n
>= 'a' && n
<= 'z')
1191 if(n
>= 'A' && n
<= 'Z')
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
);
1211 last_alternative
= fmt
;
1224 /* We're just skipping to the end */
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
;
1253 while (*fmt
&& ('<' != *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
,
1281 struct mp3entry
* id3
,
1282 struct mp3entry
* nid3
, /* next song's id3 */
1284 struct align_pos
* align
,
1285 unsigned short* subline_time_mult
,
1286 unsigned char* flags
)
1289 char* buf_start
= buf
;
1290 char* buf_end
= buf
+ buf_size
- 1; /* Leave room for end null */
1293 unsigned char tag_length
;
1296 char* cur_align_start
;
1297 #ifdef HAVE_LCD_BITMAP
1298 struct gui_img
*img
= gwps
->data
->img
;
1302 cur_align_start
= buf
;
1303 cur_align
= WPS_ALIGN_LEFT
;
1304 *subline_time_mult
= DEFAULT_SUBLINE_TIME_MULTIPLIER
;
1310 while (fmt
&& *fmt
&& buf
< buf_end
)
1322 fmt
= skip_conditional(NULL
, fmt
, 0, NULL
);
1326 /* Else fall through */
1340 /* remember where the current aligned text started */
1343 case WPS_ALIGN_LEFT
:
1344 align
->left
= cur_align_start
;
1347 case WPS_ALIGN_CENTER
:
1348 align
->center
= cur_align_start
;
1351 case WPS_ALIGN_RIGHT
:
1352 align
->right
= cur_align_start
;
1355 /* start a new alignment */
1359 cur_align
= WPS_ALIGN_LEFT
;
1362 cur_align
= WPS_ALIGN_CENTER
;
1365 cur_align
= WPS_ALIGN_RIGHT
;
1369 cur_align_start
= buf
;
1373 *flags
|= WPS_REFRESH_SCROLL
;
1377 case 'x': /* image support */
1378 #ifdef HAVE_LCD_BITMAP
1379 if ('d' == *(fmt
+1) )
1383 /* get the image ID */
1385 if(n
>= 'a' && n
<= 'z')
1387 if(n
>= 'A' && n
<= 'Z')
1389 if (n
>= 0 && n
< MAX_IMAGES
&& img
[n
].loaded
) {
1390 img
[n
].display
= true;
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
))
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
);
1428 if(intval
> 1) /* enum */
1429 fmt
= skip_conditional(NULL
, fmt
, intval
- 1, NULL
);
1436 value
= get_tag(gwps
->data
, id3
, nid3
, fmt
, temp_buf
,
1437 sizeof(temp_buf
), &tag_length
,
1438 subline_time_mult
, flags
,&intval
);
1443 while (*value
&& (buf
< buf_end
))
1449 /* remember where the current aligned text started */
1452 case WPS_ALIGN_LEFT
:
1453 align
->left
= cur_align_start
;
1456 case WPS_ALIGN_CENTER
:
1457 align
->center
= cur_align_start
;
1460 case WPS_ALIGN_RIGHT
:
1461 align
->right
= cur_align_start
;
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. */
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;
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 */
1494 while (fp_volume
< fp_global_vol
- fp_step
) {
1495 fp_volume
+= fp_step
;
1496 sound_set_volume(fp_volume
>> 8);
1499 sound_set_volume(global_settings
.volume
);
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);
1512 /* let audio thread run and wait for the mas to run out of data */
1513 while (!mp3_pause_done())
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
;
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
;
1546 buf
= skip_utf8_bom(buf
);
1547 data
->format_lines
[line
][subline
] = buf
;
1549 while ((*buf
) && (line
< WPS_MAX_LINES
))
1556 * skip % sequences so "%;" doesn't start a new subline
1557 * don't skip %x lines (pre-load bitmaps)
1570 if (*start_of_line
!= '#') /* A comment? */
1573 if (line
< WPS_MAX_LINES
)
1575 /* the next line starts on the next byte */
1577 data
->format_lines
[line
][subline
] = buf
+1;
1578 start_of_line
= data
->format_lines
[line
][subline
];
1582 case ';': /* start a new subline */
1585 if (subline
< WPS_MAX_SUBLINES
)
1587 data
->format_lines
[line
][subline
] = buf
+1;
1589 else /* exceeded max sublines, skip rest of line */
1593 if ((*buf
== '\r') || (*buf
== '\n'))
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
);
1616 display
->set_drawmode(DRMODE_SOLID
);
1619 if(data
->img
[n
].bm
.format
== FORMAT_MONO
) {
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
);
1626 display
->transparent_bitmap((fb_data
*)data
->img
[n
].bm
.data
,
1628 data
->img
[n
].y
, data
->img
[n
].bm
.width
,
1629 data
->img
[n
].bm
.height
);
1633 static void wps_display_images(struct gui_wps
*gwps
, bool always
)
1635 if(!gwps
|| !gwps
->data
|| !gwps
->display
) return;
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
);
1650 #if 0 /* currently unused */
1651 void gui_wps_reset(struct gui_wps
*gui_wps
)
1653 if(!gui_wps
|| !gui_wps
->data
)
1655 gui_wps
->data
->wps_loaded
= false;
1656 memset(&gui_wps
->data
->format_buffer
, 0,
1657 sizeof(gui_wps
->data
->format_buffer
));
1661 bool gui_wps_refresh(struct gui_wps
*gwps
, int ffwd_offset
,
1662 unsigned char refresh_mode
)
1665 unsigned char flags
;
1668 bool only_one_subline
;
1669 bool new_subline_refresh
;
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
)
1681 #ifdef HAVE_LCD_BITMAP
1682 int h
= font_get(FONT_UI
)->height
;
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;
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();
1722 display
->stop_scroll();
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) ||
1738 /* search all sublines until the next subline with time > 0
1739 is found or we get back to the subline we started with */
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
)) ||
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
;
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
]],
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
]];
1792 update_line
= false;
1794 if ( !data
->format_lines
[i
][data
->curr_subline
[i
]] )
1797 if ((data
->line_type
[i
][data
->curr_subline
[i
]] & refresh_mode
) ||
1798 (refresh_mode
== WPS_REFRESH_ALL
) ||
1799 new_subline_refresh
)
1802 #ifdef HAVE_LCD_BITMAP
1803 int left_width
, left_xpos
;
1804 int center_width
, center_xpos
;
1805 int right_width
, right_xpos
;
1811 format_display(gwps
, buf
, sizeof(buf
),
1812 state
->id3
, state
->nid3
,
1813 data
->format_lines
[i
][data
->curr_subline
[i
]],
1815 &data
->time_mult
[i
][data
->curr_subline
[i
]],
1817 data
->line_type
[i
][data
->curr_subline
[i
]] = flags
;
1819 #ifdef HAVE_LCD_BITMAP
1821 if (flags
& refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
)
1824 if (data
->progress_top
== -1)
1825 sb_y
= i
*h
+ offset
+ ((h
> data
->progress_height
+ 1)
1826 ? (h
- data
->progress_height
) / 2 : 1);
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,
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,
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
);
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);
1864 if (flags
& refresh_mode
& WPS_REFRESH_PEAK_METER
) {
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
1875 if (peak_meter_y
+ h
<= display
->height
) {
1876 /* found a line with a peak meter -> remember that we must
1879 peak_meter_screen(gwps
->display
, 0, peak_meter_y
,
1880 MIN(h
, display
->height
- peak_meter_y
));
1885 if (flags
& refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
) {
1886 if (data
->full_line_progressbar
)
1887 draw_player_fullbar(gwps
, buf
, sizeof(buf
));
1889 draw_player_progress(gwps
);
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
);
1904 if (format_align
.center
!= 0) {
1905 display
->getstringsize((unsigned char *)format_align
.center
,
1906 ¢er_width
, &string_height
);
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
);
1920 right_xpos
= (display
->width
- right_width
);
1922 /* Checks for overlapping strings.
1923 If needed the overlapping strings will be merged, separated by a
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
;
1936 /* there is no centered string anymore */
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
;
1947 /* there is no centered string anymore */
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 */
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 */
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
;
1990 /* there is no right string anymore */
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
;
2001 /* there is no right string anymore */
2007 if (flags
& WPS_REFRESH_SCROLL
) {
2010 if ((refresh_mode
& WPS_REFRESH_SCROLL
) ||
2011 new_subline_refresh
) {
2012 #ifdef HAVE_LCD_BITMAP
2013 ypos
= (i
*string_height
)+display
->getymargin();
2016 if (left_width
>display
->width
) {
2017 display
->puts_scroll(0, i
,
2018 (unsigned char *)format_align
.left
);
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
);
2047 display
->puts_scroll(0, i
, buf
);
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();
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
);
2089 display
->puts(0, i
, buf
);
2094 #ifdef HAVE_LCD_BITMAP
2096 wps_display_images(gwps
,false);
2101 #ifdef HAVE_LCD_BITMAP
2102 /* Display all images */
2103 wps_display_images(gwps
,true);
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
;
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
]
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))
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,
2131 int n
= backlight_timeout_value
[global_settings
.remote_backlight_timeout
]
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();
2146 #ifdef HAVE_LCD_CHARCELLS
2147 static bool draw_player_progress(struct gui_wps
*gwps
)
2149 char player_progressbar
[7];
2153 struct wps_state
*state
= gwps
->state
;
2154 struct screen
*display
= gwps
->display
;
2158 memset(binline
, 1, sizeof binline
);
2159 memset(player_progressbar
, 1, sizeof player_progressbar
);
2161 if(state
->id3
->elapsed
>= state
->id3
->length
)
2165 if(state
->wps_time_countup
== false)
2166 songpos
= ((state
->id3
->elapsed
- state
->ff_rewind_count
) * 36) /
2169 songpos
= ((state
->id3
->elapsed
+ state
->ff_rewind_count
) * 36) /
2172 for (i
=0; i
< songpos
; i
++)
2175 for (i
=0; i
<=6; i
++) {
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
);
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
== ':') {
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];
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>*/
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
++)
2230 if(state
->id3
->elapsed
>= state
->id3
->length
)
2233 if(state
->wps_time_countup
== false)
2234 songpos
= ((state
->id3
->elapsed
- state
->ff_rewind_count
) * 55) /
2237 songpos
= ((state
->id3
->elapsed
+ state
->ff_rewind_count
) * 55) /
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
++) {
2257 /* make the progressbar */
2258 if (lcd_char_pos
==(songpos
/5)) {
2260 if ((j
<(songpos
%5))&&(i
>4))
2266 if (lcd_char_pos
<(songpos
/5)) {
2267 /* full character */
2272 /* insert the digit */
2274 if (numbers
[digits
[lcd_char_pos
]][i
][j
]==1)
2280 for (i
=0; i
<=6; i
++) {
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 */
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
++) {
2306 if (j
<(songpos
%5)) {
2312 for (i
=0; i
<7; i
++) {
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];
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
);
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
));
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,
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 */
2370 if (button
== ACTION_NONE
)
2372 status_set_ffmode(0);
2379 case ACTION_WPS_SEEKFWD
:
2381 case ACTION_WPS_SEEKBACK
:
2382 if (wps_state
.ff_rewind
)
2386 /* fast forwarding, calc max step relative to end */
2387 max_step
= (wps_state
.id3
->length
-
2388 (wps_state
.id3
->elapsed
+
2390 FF_REWIND_MAX_PERCENT
/ 100;
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
)
2404 ff_rewind_count
+= step
* direction
;
2406 if (global_settings
.ff_rewind_accel
!= 0 &&
2407 current_tick
>= accel_tick
)
2410 accel_tick
= current_tick
+
2411 global_settings
.ff_rewind_accel
*HZ
;
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();
2425 #if CONFIG_KEYPAD == PLAYER_PAD
2427 gui_wps
[i
].display
->stop_scroll();
2430 status_set_ffmode(STATUS_FASTFORWARD
);
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
;
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
;
2452 if ((int)(wps_state
.id3
->elapsed
+ ff_rewind_count
) < 0)
2453 ff_rewind_count
= -wps_state
.id3
->elapsed
;
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
);
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
)
2475 #ifdef HAVE_LCD_CHARCELLS
2482 if(default_event_handler(button
) == SYS_USB_CONNECTED
) {
2483 status_set_ffmode(0);
2490 button
= get_action(CONTEXT_WPS
,TIMEOUT_BLOCK
);
2492 action_signalscreenchange();
2496 bool gui_wps_display(void)
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
));
2505 gui_syncstatusbar_draw(&statusbars
, true);
2506 gui_syncsplash(HZ
, true, str(LANG_END_PLAYLIST_RECORDER
));
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 */
2520 #ifdef HAVE_LCD_BITMAP
2522 unload_wps_backdrop();
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"
2529 "%al%pc/%pt%ar[%pp:%pe]\n"
2530 "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n"
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);
2540 /* set the default wps for the remote-screen */
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"
2558 gui_wps_refresh(&gui_wps
[i
], 0, WPS_REFRESH_ALL
);
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();
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
));
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())
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())
2630 gui_wps_refresh(gwps
, 0, WPS_REFRESH_ALL
);
2633 gui_wps_refresh(gwps
, 0, WPS_REFRESH_NON_STATIC
);
2636 gui_wps_statusbar_draw(gwps
, false);
2642 void display_keylock_text(bool locked
)
2647 gui_wps
[i
].display
->stop_scroll();
2649 #ifdef HAVE_LCD_CHARCELLS
2651 s
= str(LANG_KEYLOCK_ON_PLAYER
);
2653 s
= str(LANG_KEYLOCK_OFF_PLAYER
);
2656 s
= str(LANG_KEYLOCK_ON_RECORDER
);
2658 s
= str(LANG_KEYLOCK_OFF_RECORDER
);
2660 gui_syncsplash(HZ
, true, s
);