Check if the file could be modified, error if not.
[Rockbox.git] / apps / wps-display.c
blob1ed70541dc8a406a48bb6ce90d66e7a8adc0f00c
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 ****************************************************************************/
20 /* ID3 formatting based on code from the MAD Winamp plugin (in_mad.dll),
21 * Copyright (C) 2000-2001 Robert Leslie.
22 * See http://www.mars.org/home/rob/proj/mpeg/ for more information.
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdlib.h>
29 #include "lcd.h"
30 #include "hwcompat.h"
31 #include "font.h"
32 #include "audio.h"
33 #include "id3.h"
34 #include "settings.h"
35 #include "playlist.h"
36 #include "kernel.h"
37 #include "system.h"
38 #include "status.h"
39 #include "wps-display.h"
40 #include "debug.h"
41 #include "mas.h"
42 #include "lang.h"
43 #include "powermgmt.h"
44 #include "sprintf.h"
45 #include "backlight.h"
47 #ifdef HAVE_LCD_BITMAP
48 #include "icons.h"
49 #include "widgets.h"
50 #include "peakmeter.h"
51 #endif
53 #define WPS_CONFIG ROCKBOX_DIR "/default.wps"
55 #ifdef HAVE_LCD_BITMAP
56 #define MAX_LINES 13
57 #define FORMAT_BUFFER_SIZE 800
58 #else
59 #define MAX_LINES 2
60 #define FORMAT_BUFFER_SIZE 400
61 #endif
62 #define MAX_SUBLINES 12
63 #define DEFAULT_SUBLINE_TIME_MULTIPLIER 20 /* (10ths of sec) */
64 #define BASE_SUBLINE_TIME 10 /* base time that multiplier is applied to
65 (1/HZ sec, or 100ths of sec) */
66 #define SUBLINE_RESET -1
68 #ifdef HAVE_LCD_CHARCELLS
69 static unsigned char wps_progress_pat[8]={0,0,0,0,0,0,0,0};
70 static bool full_line_progressbar=0;
71 static bool draw_player_progress(const struct mp3entry* id3,
72 int ff_rewwind_count);
73 static void draw_player_fullbar(char* buf, int buf_size,
74 const struct mp3entry* id3,
75 int ff_rewwind_count);
76 static char map_fullbar_char(char ascii_val);
77 #endif
79 static char format_buffer[FORMAT_BUFFER_SIZE];
80 static char* format_lines[MAX_LINES][MAX_SUBLINES];
81 static unsigned char line_type[MAX_LINES][MAX_SUBLINES];
82 static unsigned short time_mult[MAX_LINES][MAX_SUBLINES];
83 static long subline_expire_time[MAX_LINES];
84 static int curr_subline[MAX_LINES];
86 static int ff_rewind_count;
87 bool wps_time_countup = true;
88 static bool wps_loaded = false;
90 /* Set format string to use for WPS, splitting it into lines */
91 static void wps_format(const char* fmt)
93 char* buf = format_buffer;
94 char* start_of_line = format_buffer;
95 int line = 0;
96 int subline;
98 strncpy(format_buffer, fmt, sizeof(format_buffer));
99 format_buffer[sizeof(format_buffer) - 1] = 0;
101 for (line=0; line<MAX_LINES; line++)
103 for (subline=0; subline<MAX_SUBLINES; subline++)
105 format_lines[line][subline] = 0;
106 time_mult[line][subline] = 0;
108 subline_expire_time[line] = 0;
109 curr_subline[line] = SUBLINE_RESET;
112 line = 0;
113 subline = 0;
114 format_lines[line][subline] = buf;
116 while ((*buf) && (line < MAX_LINES))
118 switch (*buf)
120 /* skip % sequences so "%;" doesn't start a new subline */
121 case '%':
122 buf++;
123 break;
125 case '\r': /* CR */
126 *buf = 0;
127 break;
129 case '\n': /* LF */
130 *buf = 0;
132 if (*start_of_line != '#') /* A comment? */
133 line++;
135 if (line < MAX_LINES)
137 /* the next line starts on the next byte */
138 subline = 0;
139 format_lines[line][subline] = buf+1;
140 start_of_line = format_lines[line][subline];
142 break;
144 case ';': /* start a new subline */
145 *buf = 0;
146 subline++;
147 if (subline < MAX_SUBLINES)
149 format_lines[line][subline] = buf+1;
151 else /* exceeded max sublines, skip rest of line */
153 while (*(++buf))
155 if ((*buf == '\r') || (*buf == '\n'))
157 break;
160 buf--;
161 subline = 0;
163 break;
165 buf++;
169 void wps_reset(void)
171 wps_loaded = false;
172 memset(&format_buffer, 0, sizeof format_buffer);
175 bool wps_load(const char* file, bool display)
177 int i, s;
178 char buffer[FORMAT_BUFFER_SIZE];
179 int fd;
181 fd = open(file, O_RDONLY);
183 if (fd >= 0)
185 int numread = read(fd, buffer, sizeof(buffer) - 1);
187 if (numread > 0)
189 buffer[numread] = 0;
190 wps_format(buffer);
193 close(fd);
195 if ( display ) {
196 bool any_defined_line;
197 lcd_clear_display();
198 #ifdef HAVE_LCD_BITMAP
199 lcd_setmargins(0,0);
200 #endif
201 for (s=0; s<MAX_SUBLINES; s++)
203 any_defined_line = false;
204 for (i=0; i<MAX_LINES; i++)
206 if (format_lines[i][s])
208 if (*format_lines[i][s] == 0)
209 lcd_puts(0,i," ");
210 else
211 lcd_puts(0,i,format_lines[i][s]);
212 any_defined_line = true;
214 else
216 lcd_puts(0,i," ");
219 if (any_defined_line)
221 lcd_update();
222 sleep(HZ/2);
226 wps_loaded = true;
228 return numread > 0;
231 return false;
234 /* Format time into buf.
236 * buf - buffer to format to.
237 * buf_size - size of buffer.
238 * time - time to format, in milliseconds.
240 static void format_time(char* buf, int buf_size, long time)
242 if ( time < 3600000 ) {
243 snprintf(buf, buf_size, "%d:%02d",
244 (int) (time % 3600000 / 60000), (int) (time % 60000 / 1000));
245 } else {
246 snprintf(buf, buf_size, "%d:%02d:%02d",
247 (int) (time / 3600000), (int) (time % 3600000 / 60000),
248 (int) (time % 60000 / 1000));
252 /* Extract a part from a path.
254 * buf - buffer extract part to.
255 * buf_size - size of buffer.
256 * path - path to extract from.
257 * level - what to extract. 0 is file name, 1 is parent of file, 2 is
258 * parent of parent, etc.
260 * Returns buf if the desired level was found, NULL otherwise.
262 static char* get_dir(char* buf, int buf_size, const char* path, int level)
264 const char* sep;
265 const char* last_sep;
266 int len;
268 sep = path + strlen(path);
269 last_sep = sep;
271 while (sep > path)
273 if ('/' == *(--sep))
275 if (!level)
277 break;
280 level--;
281 last_sep = sep - 1;
285 if (level || (last_sep <= sep))
287 return NULL;
290 len = MIN(last_sep - sep, buf_size - 1);
291 strncpy(buf, sep + 1, len);
292 buf[len] = 0;
293 return buf;
296 /* Get the tag specified by the two characters at fmt.
298 * id3 - ID3 data to get tag values from.
299 * nid3 - next-song ID3 data to get tag values from.
300 * tag - string (of two characters) specifying the tag to get.
301 * buf - buffer to certain tags, such as track number, play time or
302 * directory name.
303 * buf_size - size of buffer.
304 * flags - returns the type of the line. See constants i wps-display.h
306 * Returns the tag. NULL indicates the tag wasn't available.
308 static char* get_tag(struct mp3entry* cid3,
309 struct mp3entry* nid3,
310 const char* tag,
311 char* buf,
312 int buf_size,
313 unsigned char* tag_len,
314 unsigned short* subline_time_mult,
315 unsigned char* flags)
317 struct mp3entry *id3 = cid3; /* default to current song */
319 if ((0 == tag[0]) || (0 == tag[1]))
321 *tag_len = 0;
322 return NULL;
325 *tag_len = 2;
327 switch (tag[0])
329 case 'I': /* ID3 Information */
330 id3 = nid3; /* display next-song data */
331 *flags |= WPS_REFRESH_DYNAMIC;
332 if(!id3)
333 return NULL; /* no such info (yet) */
334 /* fall-through */
335 case 'i': /* ID3 Information */
336 *flags |= WPS_REFRESH_STATIC;
337 switch (tag[1])
339 case 't': /* ID3 Title */
340 return id3->title;
342 case 'a': /* ID3 Artist */
343 return id3->artist;
345 case 'n': /* ID3 Track Number */
346 if (id3->track_string)
347 return id3->track_string;
349 if (id3->tracknum) {
350 snprintf(buf, buf_size, "%d", id3->tracknum);
351 return buf;
353 return NULL;
355 case 'd': /* ID3 Album/Disc */
356 return id3->album;
358 case 'c': /* ID3 Composer */
359 return id3->composer;
361 case 'y': /* year */
362 if( id3->year_string )
363 return id3->year_string;
365 if (id3->year) {
366 snprintf(buf, buf_size, "%d", id3->year);
367 return buf;
369 return NULL;
371 case 'g': /* genre */
372 return id3_get_genre(id3);
374 case 'v': /* id3 version */
375 switch (id3->id3version) {
376 case ID3_VER_1_0:
377 return "1";
379 case ID3_VER_1_1:
380 return "1.1";
382 case ID3_VER_2_2:
383 return "2.2";
385 case ID3_VER_2_3:
386 return "2.3";
388 case ID3_VER_2_4:
389 return "2.4";
391 default:
392 return NULL;
395 break;
397 case 'F': /* File Information */
398 id3 = nid3;
399 *flags |= WPS_REFRESH_DYNAMIC;
400 if(!id3)
401 return NULL; /* no such info (yet) */
402 /* fall-through */
403 case 'f': /* File Information */
404 *flags |= WPS_REFRESH_STATIC;
405 switch(tag[1])
407 case 'v': /* VBR file? */
408 return id3->vbr ? "(avg)" : NULL;
410 case 'b': /* File Bitrate */
411 if(id3->bitrate)
412 snprintf(buf, buf_size, "%d", id3->bitrate);
413 else
414 snprintf(buf, buf_size, "?");
415 return buf;
417 case 'f': /* File Frequency */
418 snprintf(buf, buf_size, "%d", id3->frequency);
419 return buf;
421 case 'p': /* File Path */
422 return id3->path;
424 case 'm': /* File Name - With Extension */
425 return get_dir(buf, buf_size, id3->path, 0);
427 case 'n': /* File Name */
428 if (get_dir(buf, buf_size, id3->path, 0))
430 /* Remove extension */
431 char* sep = strrchr(buf, '.');
433 if (NULL != sep)
435 *sep = 0;
438 return buf;
440 else
442 return NULL;
445 case 's': /* File Size (in kilobytes) */
446 snprintf(buf, buf_size, "%d", id3->filesize / 1024);
447 return buf;
449 break;
451 case 'p': /* Playlist/Song Information */
452 switch(tag[1])
454 case 'b': /* progress bar */
455 *flags |= WPS_REFRESH_PLAYER_PROGRESS;
456 #ifdef HAVE_LCD_CHARCELLS
457 snprintf(buf, buf_size, "%c", wps_progress_pat[0]);
458 full_line_progressbar=0;
459 return buf;
460 #else
461 return "\x01";
462 #endif
463 case 'f': /* full-line progress bar */
464 #ifdef HAVE_LCD_CHARCELLS
465 if(is_new_player()) {
466 *flags |= WPS_REFRESH_PLAYER_PROGRESS;
467 *flags |= WPS_REFRESH_DYNAMIC;
468 full_line_progressbar=1;
469 /* we need 11 characters (full line) for
470 progress-bar */
471 snprintf(buf, buf_size, " ");
473 else
475 /* Tell the user if we have an OldPlayer */
476 snprintf(buf, buf_size, " <Old LCD> ");
478 return buf;
479 #endif
480 case 'p': /* Playlist Position */
481 *flags |= WPS_REFRESH_STATIC;
482 snprintf(buf, buf_size, "%d", playlist_get_display_index());
483 return buf;
485 case 'n': /* Playlist Name (without path) */
486 *flags |= WPS_REFRESH_STATIC;
487 return playlist_name(NULL, buf, buf_size);
489 case 'e': /* Playlist Total Entries */
490 *flags |= WPS_REFRESH_STATIC;
491 snprintf(buf, buf_size, "%d", playlist_amount());
492 return buf;
494 case 'c': /* Current Time in Song */
495 *flags |= WPS_REFRESH_DYNAMIC;
496 format_time(buf, buf_size, id3->elapsed + ff_rewind_count);
497 return buf;
499 case 'r': /* Remaining Time in Song */
500 *flags |= WPS_REFRESH_DYNAMIC;
501 format_time(buf, buf_size,
502 id3->length - id3->elapsed - ff_rewind_count);
503 return buf;
505 case 't': /* Total Time */
506 *flags |= WPS_REFRESH_STATIC;
507 format_time(buf, buf_size, id3->length);
508 return buf;
510 #ifdef HAVE_LCD_BITMAP
511 case 'm': /* Peak Meter */
512 *flags |= WPS_REFRESH_PEAK_METER;
513 return "\x01";
514 #endif
515 case 's': /* shuffle */
516 if ( global_settings.playlist_shuffle )
517 return "s";
518 else
519 return NULL;
520 break;
522 case 'v': /* volume */
523 *flags |= WPS_REFRESH_DYNAMIC;
524 snprintf(buf, buf_size, "%d%%", global_settings.volume);
525 return buf;
528 break;
530 case 'b': /* battery info */
531 *flags |= WPS_REFRESH_DYNAMIC;
532 switch (tag[1]) {
533 case 'l': /* battery level */
535 int l = battery_level();
536 if (l > -1)
537 snprintf(buf, buf_size, "%d%%", l);
538 else
539 return "?%";
540 return buf;
543 case 't': /* estimated battery time */
545 int t = battery_time();
546 if (t >= 0)
547 snprintf(buf, buf_size, "%dh %dm", t / 60, t % 60);
548 else
549 strncpy(buf, "?h ?m", buf_size);
550 return buf;
553 break;
555 case 'D': /* Directory path information */
556 id3 = nid3; /* next song please! */
557 *flags |= WPS_REFRESH_DYNAMIC;
558 if(!id3)
559 return NULL; /* no such info (yet) */
560 /* fall-through */
561 case 'd': /* Directory path information */
563 int level = tag[1] - '0';
564 *flags |= WPS_REFRESH_STATIC;
565 /* d1 through d9 */
566 if ((0 < level) && (9 > level))
568 return get_dir(buf, buf_size, id3->path, level);
571 break;
573 case 't': /* set sub line time multiplier */
575 int d = 1;
576 int time_mult = 0;
577 bool have_point = false;
578 bool have_tenth = false;
580 while (((tag[d] >= '0') &&
581 (tag[d] <= '9')) ||
582 (tag[d] == '.'))
584 if (tag[d] != '.')
586 time_mult = time_mult * 10;
587 time_mult = time_mult + tag[d] - '0';
588 if (have_point)
590 have_tenth = true;
591 d++;
592 break;
595 else
597 have_point = true;
599 d++;
602 if (have_tenth == false)
603 time_mult *= 10;
605 *subline_time_mult = time_mult;
606 *tag_len = d;
608 buf[0] = 0;
609 return buf;
611 break;
614 return NULL;
617 /* Skip to the end of the current %? conditional.
619 * fmt - string to skip it. Should point to somewhere after the leading
620 * "<" char (and before or at the last ">").
621 * to_else - if true, skip to the else part (after the "|", if any), else skip
622 * to the end (the ">").
624 * Returns the new position in fmt.
626 static const char* skip_conditional(const char* fmt, bool to_else)
628 int level = 1;
630 while (*fmt)
632 switch (*fmt++)
634 case '%':
635 break;
637 case '|':
638 if (to_else && (1 == level))
639 return fmt;
641 continue;
643 case '>':
644 if (0 == --level)
646 if (to_else)
647 fmt--;
649 return fmt;
651 continue;
653 default:
654 continue;
657 switch (*fmt++)
659 case 0:
660 case '%':
661 case '|':
662 case '<':
663 case '>':
664 break;
666 case '?':
667 while (*fmt && ('<' != *fmt))
668 fmt++;
670 if ('<' == *fmt)
671 fmt++;
673 level++;
674 break;
676 default:
677 break;
681 return fmt;
684 /* Generate the display based on id3 information and format string.
686 * buf - char buffer to write the display to.
687 * buf_size - the size of buffer.
688 * id3 - the ID3 data to format with.
689 * nid3 - the ID3 data of the next song (might by NULL)
690 * fmt - format description.
691 * flags - returns the type of the line. See constants i wps-display.h
693 static void format_display(char* buf,
694 int buf_size,
695 struct mp3entry* id3,
696 struct mp3entry* nid3, /* next song's id3 */
697 const char* fmt,
698 unsigned short* subline_time_mult,
699 unsigned char* flags)
701 char temp_buf[128];
702 char* buf_start = buf;
703 char* buf_end = buf + buf_size - 1; /* Leave room for end null */
704 char* value = NULL;
705 int level = 0;
706 unsigned char tag_length;
708 *subline_time_mult = DEFAULT_SUBLINE_TIME_MULTIPLIER;
710 while (fmt && *fmt && buf < buf_end)
712 switch (*fmt)
714 case '%':
715 ++fmt;
716 break;
718 case '|':
719 case '>':
720 if (level > 0)
722 fmt = skip_conditional(fmt, false);
723 level--;
724 continue;
726 /* Else fall through */
728 default:
729 *buf++ = *fmt++;
730 continue;
733 switch (*fmt)
735 case 0:
736 *buf++ = '%';
737 break;
739 case 's':
740 *flags |= WPS_REFRESH_SCROLL;
741 ++fmt;
742 break;
744 case '%':
745 case '|':
746 case '<':
747 case '>':
748 case ';':
749 *buf++ = *fmt++;
750 break;
752 case '?':
753 fmt++;
754 value = get_tag(id3, nid3, fmt, temp_buf, sizeof(temp_buf),
755 &tag_length, subline_time_mult, flags);
757 while (*fmt && ('<' != *fmt))
758 fmt++;
760 if ('<' == *fmt)
761 fmt++;
763 /* No value, so skip to else part */
764 if ((!value) || (!strlen(value)))
765 fmt = skip_conditional(fmt, true);
767 level++;
768 break;
770 default:
771 value = get_tag(id3, nid3, fmt, temp_buf, sizeof(temp_buf),
772 &tag_length, subline_time_mult, flags);
773 fmt += tag_length;
775 if (value)
777 while (*value && (buf < buf_end))
778 *buf++ = *value++;
783 *buf = 0;
785 /* if resulting line is an empty line, set the subline time to 0 */
786 if (*buf_start == 0)
787 *subline_time_mult = 0;
789 /* If no flags have been set, the line didn't contain any format codes.
790 We still want to refresh it. */
791 if(*flags == 0)
792 *flags = WPS_REFRESH_STATIC;
795 bool wps_refresh(struct mp3entry* id3,
796 struct mp3entry* nid3,
797 int ffwd_offset,
798 unsigned char refresh_mode)
800 char buf[MAX_PATH];
801 unsigned char flags;
802 int i;
803 bool update_line;
804 bool only_one_subline;
805 bool new_subline_refresh;
806 int search;
807 int search_start;
808 #ifdef HAVE_LCD_BITMAP
809 int h = font_get(FONT_UI)->height;
810 int offset = global_settings.statusbar ? STATUSBAR_HEIGHT : 0;
811 /* to find out wether the peak meter is enabled we
812 assume it wasn't until we find a line that contains
813 the peak meter. We can't use peak_meter_enabled itself
814 because that would mean to turn off the meter thread
815 temporarily. (That shouldn't matter unless yield
816 or sleep is called but who knows...)
818 bool enable_pm = false;
819 #endif
821 /* reset to first subline if refresh all flag is set */
822 if (refresh_mode == WPS_REFRESH_ALL)
824 for (i=0; i<MAX_LINES; i++)
826 curr_subline[i] = SUBLINE_RESET;
830 #ifdef HAVE_LCD_CHARCELLS
831 for (i=0; i<8; i++) {
832 if (wps_progress_pat[i]==0)
833 wps_progress_pat[i]=lcd_get_locked_pattern();
835 #endif
837 if (!id3)
839 lcd_stop_scroll();
840 return false;
843 ff_rewind_count = ffwd_offset;
845 for (i = 0; i < MAX_LINES; i++)
847 new_subline_refresh = false;
848 only_one_subline = false;
850 /* if time to advance to next sub-line */
851 if (TIME_AFTER(current_tick, subline_expire_time[i] - 1) ||
852 (curr_subline[i] == SUBLINE_RESET))
854 /* search all sublines until the next subline with time > 0
855 is found or we get back to the subline we started with */
856 if (curr_subline[i] == SUBLINE_RESET)
857 search_start = 0;
858 else
859 search_start = curr_subline[i];
860 for (search=0; search<MAX_SUBLINES; search++)
862 curr_subline[i]++;
864 /* wrap around if beyond last defined subline or MAX_SUBLINES */
865 if ((!format_lines[i][curr_subline[i]]) ||
866 (curr_subline[i] == MAX_SUBLINES))
868 if (curr_subline[i] == 1)
869 only_one_subline = true;
870 curr_subline[i] = 0;
873 /* if back where we started after search or
874 only one subline is defined on the line */
875 if (((search > 0) && (curr_subline[i] == search_start)) ||
876 only_one_subline)
878 /* no other subline with a time > 0 exists */
879 subline_expire_time[i] = current_tick + 100 * HZ;
880 break;
882 else
884 /* get initial time multiplier and
885 line type flags for this subline */
886 format_display(buf, sizeof(buf), id3, nid3,
887 format_lines[i][curr_subline[i]],
888 &time_mult[i][curr_subline[i]],
889 &line_type[i][curr_subline[i]]);
891 /* only use this subline if subline time > 0 */
892 if (time_mult[i][curr_subline[i]] > 0)
894 new_subline_refresh = true;
895 subline_expire_time[i] = current_tick +
896 BASE_SUBLINE_TIME * time_mult[i][curr_subline[i]];
897 break;
904 update_line = false;
906 if ( !format_lines[i][curr_subline[i]] )
907 break;
909 if ((line_type[i][curr_subline[i]] & refresh_mode) ||
910 (refresh_mode == WPS_REFRESH_ALL) ||
911 new_subline_refresh)
913 flags = 0;
914 format_display(buf, sizeof(buf), id3, nid3,
915 format_lines[i][curr_subline[i]],
916 &time_mult[i][curr_subline[i]],
917 &flags);
918 line_type[i][curr_subline[i]] = flags;
920 #ifdef HAVE_LCD_BITMAP
921 /* progress */
922 if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS) {
923 scrollbar(0, i*h + offset + 1, LCD_WIDTH, 6,
924 id3->length?id3->length:1, 0,
925 id3->length?id3->elapsed + ff_rewind_count:0,
926 HORIZONTAL);
927 update_line = true;
929 if (flags & refresh_mode & WPS_REFRESH_PEAK_METER) {
930 /* peak meter */
931 int peak_meter_y;
933 update_line = true;
934 peak_meter_y = i * h + offset;
936 /* The user might decide to have the peak meter in the last
937 line so that it is only displayed if no status bar is
938 visible. If so we neither want do draw nor enable the
939 peak meter. */
940 if (peak_meter_y + h <= LCD_HEIGHT) {
941 /* found a line with a peak meter -> remember that we must
942 enable it later */
943 enable_pm = true;
944 peak_meter_draw(0, peak_meter_y, LCD_WIDTH,
945 MIN(h, LCD_HEIGHT - peak_meter_y));
948 #else
949 /* progress */
950 if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS) {
951 if (full_line_progressbar)
952 draw_player_fullbar(buf, sizeof(buf),
953 id3, ff_rewind_count);
954 else
955 draw_player_progress(id3, ff_rewind_count);
957 #endif
959 if (flags & WPS_REFRESH_SCROLL) {
961 /* scroll line */
962 if ((refresh_mode & WPS_REFRESH_SCROLL) ||
963 new_subline_refresh) {
964 lcd_puts_scroll(0, i, buf);
965 update_line = true;
968 else if (flags & (WPS_REFRESH_DYNAMIC | WPS_REFRESH_STATIC))
970 /* dynamic / static line */
971 if ((refresh_mode & (WPS_REFRESH_DYNAMIC|WPS_REFRESH_STATIC)) ||
972 new_subline_refresh)
974 update_line = true;
975 lcd_puts(0, i, buf);
979 #ifdef HAVE_LCD_BITMAP
980 if (update_line) {
981 lcd_update_rect(0, i*h + offset, LCD_WIDTH, h);
983 #endif
985 #ifdef HAVE_LCD_BITMAP
986 /* Now we know wether the peak meter is used.
987 So we can enable / disable the peak meter thread */
988 peak_meter_enabled = enable_pm;
989 #endif
991 #if defined(CONFIG_BACKLIGHT) && !defined(SIMULATOR)
992 if (global_settings.caption_backlight && id3) {
993 /* turn on backlight n seconds before track ends, and turn it off n
994 seconds into the new track. n == backlight_timeout, or 5s */
995 int n =
996 backlight_timeout_value[global_settings.backlight_timeout] * 1000;
998 if ( n < 1000 )
999 n = 5000; /* use 5s if backlight is always on or off */
1001 if ((id3->elapsed < 1000) ||
1002 ((id3->length - id3->elapsed) < (unsigned)n))
1003 backlight_on();
1005 #endif
1006 return true;
1009 bool wps_display(struct mp3entry* id3,
1010 struct mp3entry* nid3)
1012 lcd_clear_display();
1014 if (!id3 && !(audio_status() & AUDIO_STATUS_PLAY))
1016 #ifdef HAVE_LCD_CHARCELLS
1017 lcd_puts(0, 0, str(LANG_END_PLAYLIST_PLAYER));
1018 #else
1019 lcd_puts(0, 2, str(LANG_END_PLAYLIST_RECORDER));
1020 lcd_update();
1021 #endif
1022 global_settings.resume_index = -1;
1023 status_draw(true);
1024 sleep(HZ);
1025 return true;
1027 else
1029 if (!wps_loaded) {
1030 if ( !format_buffer[0] ) {
1031 #ifdef HAVE_LCD_BITMAP
1032 wps_format("%s%?it<%?in<%in. |>%it|%fn>\n"
1033 "%s%?ia<%ia|%?d2<%d2|(root)>>\n"
1034 "%s%?id<%id|%?d1<%d1|(root)>> %?iy<(%iy)|>\n"
1035 "\n"
1036 "%pc/%pt [%pp:%pe]\n"
1037 "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n"
1038 "%pb\n"
1039 "%pm\n");
1040 #else
1041 wps_format("%s%pp/%pe: %?it<%it|%fn> - %?ia<%ia|%d2> - %?id<%id|%d1>\n"
1042 "%pc%?ps<*|/>%pt\n");
1043 #endif
1047 yield();
1048 wps_refresh(id3, nid3, 0, WPS_REFRESH_ALL);
1049 status_draw(true);
1050 lcd_update();
1051 return false;
1054 #ifdef HAVE_LCD_CHARCELLS
1055 static bool draw_player_progress(const struct mp3entry* id3,
1056 int ff_rewwind_count)
1058 char player_progressbar[7];
1059 char binline[36];
1060 int songpos = 0;
1061 int i,j;
1063 if (!id3)
1064 return false;
1066 memset(binline, 1, sizeof binline);
1067 memset(player_progressbar, 1, sizeof player_progressbar);
1069 if(id3->elapsed >= id3->length)
1070 songpos = 0;
1071 else
1073 if(wps_time_countup == false)
1074 songpos = ((id3->elapsed - ff_rewwind_count) * 36) / id3->length;
1075 else
1076 songpos = ((id3->elapsed + ff_rewwind_count) * 36) / id3->length;
1078 for (i=0; i < songpos; i++)
1079 binline[i] = 0;
1081 for (i=0; i<=6; i++) {
1082 for (j=0;j<5;j++) {
1083 player_progressbar[i] <<= 1;
1084 player_progressbar[i] += binline[i*5+j];
1087 lcd_define_pattern(wps_progress_pat[0], player_progressbar);
1088 return true;
1091 static void draw_player_fullbar(char* buf, int buf_size,
1092 const struct mp3entry* id3,
1093 int ff_rewwind_count)
1095 int i,j,lcd_char_pos;
1097 char player_progressbar[7];
1098 char binline[36];
1099 static const char numbers[12][4][3]={
1100 {{1,1,1},{1,0,1},{1,0,1},{1,1,1}},/*0*/
1101 {{0,1,0},{1,1,0},{0,1,0},{0,1,0}},/*1*/
1102 {{1,1,1},{0,0,1},{0,1,0},{1,1,1}},/*2*/
1103 {{1,1,1},{0,0,1},{0,1,1},{1,1,1}},/*3*/
1104 {{1,0,0},{1,1,0},{1,1,1},{0,1,0}},/*4*/
1105 {{1,1,1},{1,1,0},{0,0,1},{1,1,0}},/*5*/
1106 {{1,1,1},{1,0,0},{1,1,1},{1,1,1}},/*6*/
1107 {{1,1,1},{0,0,1},{0,1,0},{1,0,0}},/*7*/
1108 {{1,1,1},{1,1,1},{1,0,1},{1,1,1}},/*8*/
1109 {{1,1,1},{1,1,1},{0,0,1},{1,1,1}},/*9*/
1110 {{0,0,0},{0,1,0},{0,0,0},{0,1,0}},/*:*/
1111 {{0,0,0},{0,0,0},{0,0,0},{0,0,0}} /*<blank>*/
1114 int songpos = 0;
1115 int digits[6];
1116 int time;
1117 char timestr[7];
1119 for (i=0; i < buf_size; i++)
1120 buf[i] = ' ';
1122 if(id3->elapsed >= id3->length)
1123 songpos = 55;
1124 else {
1125 if(wps_time_countup == false)
1126 songpos = ((id3->elapsed - ff_rewwind_count) * 55) / id3->length;
1127 else
1128 songpos = ((id3->elapsed + ff_rewwind_count) * 55) / id3->length;
1131 time=(id3->elapsed + ff_rewind_count);
1133 memset(timestr, 0, sizeof(timestr));
1134 format_time(timestr, sizeof(timestr), time);
1135 for(lcd_char_pos=0; lcd_char_pos<6; lcd_char_pos++) {
1136 digits[lcd_char_pos] = map_fullbar_char(timestr[lcd_char_pos]);
1139 /* build the progressbar-icons */
1140 for (lcd_char_pos=0; lcd_char_pos<6; lcd_char_pos++) {
1141 memset(binline, 0, sizeof binline);
1142 memset(player_progressbar, 0, sizeof player_progressbar);
1144 /* make the character (progressbar & digit)*/
1145 for (i=0; i<7; i++) {
1146 for (j=0;j<5;j++) {
1147 /* make the progressbar */
1148 if (lcd_char_pos==(songpos/5)) {
1149 /* partial */
1150 if ((j<(songpos%5))&&(i>4))
1151 binline[i*5+j] = 1;
1152 else
1153 binline[i*5+j] = 0;
1155 else {
1156 if (lcd_char_pos<(songpos/5)) {
1157 /* full character */
1158 if (i>4)
1159 binline[i*5+j] = 1;
1162 /* insert the digit */
1163 if ((j<3)&&(i<4)) {
1164 if (numbers[digits[lcd_char_pos]][i][j]==1)
1165 binline[i*5+j] = 1;
1170 for (i=0; i<=6; i++) {
1171 for (j=0;j<5;j++) {
1172 player_progressbar[i] <<= 1;
1173 player_progressbar[i] += binline[i*5+j];
1177 lcd_define_pattern(wps_progress_pat[lcd_char_pos+1],player_progressbar);
1178 buf[lcd_char_pos]=wps_progress_pat[lcd_char_pos+1];
1182 /* make rest of the progressbar if necessary */
1183 if (songpos/5>5) {
1185 /* set the characters positions that use the full 5 pixel wide bar */
1186 for (lcd_char_pos=6; lcd_char_pos < (songpos/5); lcd_char_pos++)
1187 buf[lcd_char_pos] = 0x86; /* '_' */
1189 /* build the partial bar character for the tail character position */
1190 memset(binline, 0, sizeof binline);
1191 memset(player_progressbar, 0, sizeof player_progressbar);
1193 for (i=5; i<7; i++) {
1194 for (j=0;j<5;j++) {
1195 if (j<(songpos%5)) {
1196 binline[i*5+j] = 1;
1201 for (i=0; i<7; i++) {
1202 for (j=0;j<5;j++) {
1203 player_progressbar[i] <<= 1;
1204 player_progressbar[i] += binline[i*5+j];
1208 lcd_define_pattern(wps_progress_pat[7],player_progressbar);
1210 buf[songpos/5]=wps_progress_pat[7];
1214 static char map_fullbar_char(char ascii_val)
1216 if (ascii_val >= '0' && ascii_val <= '9') {
1217 return(ascii_val - '0');
1219 else if (ascii_val == ':') {
1220 return(10);
1222 else
1223 return(11); /* anything besides a number or ':' is mapped to <blank> */
1227 #endif
1229 /* -----------------------------------------------------------------
1230 * vim: et sw=4 ts=8 sts=4 tw=78