summaryrefslogtreecommitdiff
path: root/apps/wps-display.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/wps-display.c')
-rw-r--r--apps/wps-display.c1841
1 files changed, 0 insertions, 1841 deletions
diff --git a/apps/wps-display.c b/apps/wps-display.c
deleted file mode 100644
index 58a953e4c0..0000000000
--- a/apps/wps-display.c
+++ /dev/null
@@ -1,1841 +0,0 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2002 Björn Stenberg
11 *
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.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19
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.
23 */
24
25#include <stdio.h>
26#include <string.h>
27#include <stdlib.h>
28
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 "power.h"
45#include "sprintf.h"
46#include "backlight.h"
47#include "button.h"
48#include "abrepeat.h"
49#include "screens.h"
50#include "splash.h"
51#ifdef HAVE_LCD_BITMAP
52#include <ctype.h>
53#include "icons.h"
54#include "widgets.h"
55#include "peakmeter.h"
56
57/* Image stuff */
58#include "bmp.h"
59#include "atoi.h"
60#define MAX_IMAGES (26*2) /* a-z and A-Z */
61#define IMG_BUFSIZE (LCD_HEIGHT * LCD_WIDTH * MAX_IMAGES / 25 ) / 8
62static unsigned char img_buf[IMG_BUFSIZE]; /* image buffer */
63static unsigned char* img_buf_ptr = img_buf; /* where are in image buffer? */
64
65static int img_buf_free = IMG_BUFSIZE; /* free space in image buffer */
66
67struct {
68 unsigned char* ptr; /* pointer */
69 int x; /* x-pos */
70 int y; /* y-pos */
71 int w; /* width */
72 int h; /* height */
73 bool loaded; /* load state */
74 bool display; /* is to be displayed */
75 bool always_display; /* not using the preload/display mechanism */
76} img[MAX_IMAGES] ;
77
78
79#endif
80
81#define WPS_DEFAULTCFG WPS_DIR "/rockbox_default.wps"
82
83#ifdef HAVE_LCD_BITMAP
84#define MAX_LINES (LCD_HEIGHT/5+1)
85#define FORMAT_BUFFER_SIZE 3072
86#else
87#define MAX_LINES 2
88#define FORMAT_BUFFER_SIZE 400
89#endif
90#define MAX_SUBLINES 12
91#define DEFAULT_SUBLINE_TIME_MULTIPLIER 20 /* (10ths of sec) */
92#define BASE_SUBLINE_TIME 10 /* base time that multiplier is applied to
93 (1/HZ sec, or 100ths of sec) */
94#define SUBLINE_RESET -1
95
96#ifdef HAVE_LCD_CHARCELLS
97static unsigned char wps_progress_pat[8]={0,0,0,0,0,0,0,0};
98static bool full_line_progressbar=0;
99static bool draw_player_progress(const struct mp3entry* id3,
100 int ff_rewwind_count);
101static void draw_player_fullbar(char* buf, int buf_size,
102 const struct mp3entry* id3,
103 int ff_rewwind_count);
104static char map_fullbar_char(char ascii_val);
105#endif
106
107struct align_pos {
108 char* left;
109 char* center;
110 char* right;
111};
112static char format_buffer[FORMAT_BUFFER_SIZE];
113static char* format_lines[MAX_LINES][MAX_SUBLINES];
114static struct align_pos format_align[MAX_LINES][MAX_SUBLINES];
115static unsigned char line_type[MAX_LINES][MAX_SUBLINES];
116static unsigned short time_mult[MAX_LINES][MAX_SUBLINES];
117static long subline_expire_time[MAX_LINES];
118static int curr_subline[MAX_LINES];
119
120static int ff_rewind_count;
121bool wps_time_countup = true;
122static bool wps_loaded = false;
123
124#ifdef HAVE_LCD_BITMAP
125/* Display images */
126static void wps_display_images(void) {
127 int n;
128 for (n = 0; n < MAX_IMAGES; n++) {
129 if (img[n].loaded && img[n].display) {
130 if(img[n].always_display)
131 lcd_set_drawmode(DRMODE_FG);
132 else
133 lcd_set_drawmode(DRMODE_SOLID);
134
135 lcd_mono_bitmap(img[n].ptr, img[n].x, img[n].y, img[n].w, img[n].h);
136 lcd_update_rect(img[n].x, img[n].y, img[n].w, img[n].h);
137 }
138 }
139 lcd_set_drawmode(DRMODE_SOLID);
140}
141#endif
142
143/* Set format string to use for WPS, splitting it into lines */
144static void wps_format(const char* fmt, char *bmpdir, size_t bmpdirlen)
145{
146 char* buf = format_buffer;
147 char* start_of_line = format_buffer;
148 int line = 0;
149 int subline;
150#ifndef HAVE_LCD_BITMAP
151 /* no bitmap lcd == no bitmap loading */
152 (void)bmpdir;
153 (void)bmpdirlen;
154#endif
155
156 strncpy(format_buffer, fmt, sizeof(format_buffer));
157 format_buffer[sizeof(format_buffer) - 1] = 0;
158
159 for (line=0; line<MAX_LINES; line++)
160 {
161 for (subline=0; subline<MAX_SUBLINES; subline++)
162 {
163 format_lines[line][subline] = 0;
164 time_mult[line][subline] = 0;
165 }
166 subline_expire_time[line] = 0;
167 curr_subline[line] = SUBLINE_RESET;
168 }
169
170 line = 0;
171 subline = 0;
172 format_lines[line][subline] = buf;
173
174 while ((*buf) && (line < MAX_LINES))
175 {
176 switch (*buf)
177 {
178 /*
179 * skip % sequences so "%;" doesn't start a new subline
180 * don't skip %x lines (pre-load bitmaps)
181 */
182 case '%':
183 if (*(buf+1) != 'x')
184 buf++;
185 break;
186
187 case '\r': /* CR */
188 *buf = 0;
189 break;
190
191 case '\n': /* LF */
192 *buf = 0;
193
194 if (*start_of_line != '#') /* A comment? */
195 line++;
196
197 if (line < MAX_LINES)
198 {
199 /* the next line starts on the next byte */
200 subline = 0;
201 format_lines[line][subline] = buf+1;
202 start_of_line = format_lines[line][subline];
203 }
204 break;
205
206 case ';': /* start a new subline */
207 *buf = 0;
208 subline++;
209 if (subline < MAX_SUBLINES)
210 {
211 format_lines[line][subline] = buf+1;
212 }
213 else /* exceeded max sublines, skip rest of line */
214 {
215 while (*(++buf))
216 {
217 if ((*buf == '\r') || (*buf == '\n'))
218 {
219 break;
220 }
221 }
222 buf--;
223 subline = 0;
224 }
225 break;
226
227 case 'x':
228#ifdef HAVE_LCD_BITMAP
229 /* Preload images so the %xd# tag can display it */
230 {
231 int ret = 0;
232 int n;
233 char *ptr = buf+1;
234 char *pos = NULL;
235 char imgname[MAX_PATH];
236 char qual = *ptr;
237 if (qual == 'l' || qual == '|') /* format:
238 %x|n|filename.bmp|x|y|
239 or
240 %xl|n|filename.bmp|x|y|
241 */
242 {
243 ptr = strchr(ptr, '|') + 1;
244 pos = strchr(ptr, '|');
245 if (pos)
246 {
247 /* get the image ID */
248 n = *ptr;
249 if(n >= 'a' && n <= 'z')
250 n -= 'a';
251 if(n >= 'A' && n <= 'Z')
252 n = n - 'A' + 26;
253
254 if(n < 0 || n >= MAX_IMAGES)
255 {
256 /* Skip the rest of the line */
257 while(*buf != '\n')
258 buf++;
259 break;
260 }
261 ptr = pos+1;
262
263 /* check the image number and load state */
264 if (!img[n].loaded)
265 {
266 /* get filename */
267 pos = strchr(ptr, '|');
268 if ((pos - ptr) <
269 (int)sizeof(imgname)-ROCKBOX_DIR_LEN-2)
270 {
271 memcpy(imgname, bmpdir, bmpdirlen);
272 imgname[bmpdirlen] = '/';
273 memcpy(&imgname[bmpdirlen+1],
274 ptr, pos - ptr);
275 imgname[bmpdirlen+1+pos-ptr] = 0;
276 }
277 else
278 /* filename too long */
279 imgname[0] = 0;
280
281 ptr = pos+1;
282
283 /* get x-position */
284 pos = strchr(ptr, '|');
285 if (pos)
286 img[n].x = atoi(ptr);
287 else
288 {
289 /* weird syntax, bail out */
290 buf++;
291 break;
292 }
293
294 /* get y-position */
295 ptr = pos+1;
296 pos = strchr(ptr, '|');
297 if (pos)
298 img[n].y = atoi(ptr);
299 else
300 {
301 /* weird syntax, bail out */
302 buf++;
303 break;
304 }
305
306 pos++;
307
308 /* reposition buf pointer to next WPS element */
309 while (*pos && *pos != ';' &&
310 *pos != '\r' && *pos != '\n')
311 pos++;
312
313 buf = pos;
314
315 /* load the image */
316 ret = read_bmp_file(imgname, &img[n].w, &img[n].h,
317 img_buf_ptr, img_buf_free);
318 if (ret > 0)
319 {
320 img[n].ptr = img_buf_ptr;
321 img_buf_ptr += ret;
322 img_buf_free -= ret;
323 img[n].loaded = true;
324 if(qual == '|')
325 img[n].always_display = true;
326 }
327 }
328 buf++;
329 }
330 }
331 }
332#endif
333 break;
334 }
335 buf++;
336 }
337}
338
339/* Clear the WPS image cache */
340static void wps_clear(void)
341{
342#ifdef HAVE_LCD_BITMAP
343 int i;
344
345 /* reset image buffer */
346 img_buf_ptr = img_buf;
347 img_buf_free = IMG_BUFSIZE;
348
349 /* set images to unloaded and not displayed */
350 for (i = 0; i < MAX_IMAGES; i++) {
351 img[i].loaded = false;
352 img[i].display = false;
353 img[i].always_display = false;
354 }
355#endif
356
357}
358
359void wps_reset(void)
360{
361 wps_loaded = false;
362 memset(&format_buffer, 0, sizeof format_buffer);
363 wps_clear();
364}
365
366bool wps_load(const char* file, bool display)
367{
368 int i,s;
369 char buffer[FORMAT_BUFFER_SIZE];
370 int fd;
371 size_t bmpdirlen;
372
373 char *bmpdir = strrchr(file, '.');
374 bmpdirlen = bmpdir - file;
375
376 /*
377 * Hardcode loading WPS_DEFAULTCFG to cause a reset ideally this
378 * wants to be a virtual file. Feel free to modify dirbrowse()
379 * if you're feeling brave.
380 */
381 if (! strcmp(file, WPS_DEFAULTCFG) ) {
382 wps_reset();
383 return(false);
384 }
385
386 fd = open(file, O_RDONLY);
387
388 if (fd >= 0)
389 {
390 int numread = read(fd, buffer, sizeof(buffer) - 1);
391
392 if (numread > 0)
393 {
394 wps_clear();
395 buffer[numread] = 0;
396 wps_format(buffer, (char *)file, bmpdirlen);
397 }
398
399 close(fd);
400
401 if ( display ) {
402 bool any_defined_line;
403 lcd_clear_display();
404#ifdef HAVE_LCD_BITMAP
405 lcd_setmargins(0,0);
406#endif
407 for (s=0; s<MAX_SUBLINES; s++)
408 {
409 any_defined_line = false;
410 for (i=0; i<MAX_LINES; i++)
411 {
412 if (format_lines[i][s])
413 {
414 if (*format_lines[i][s] == 0)
415 lcd_puts(0,i," ");
416 else
417 lcd_puts(0,i,format_lines[i][s]);
418 any_defined_line = true;
419 }
420 else
421 {
422 lcd_puts(0,i," ");
423 }
424 }
425 if (any_defined_line)
426 {
427#ifdef HAVE_LCD_BITMAP
428 wps_display_images();
429#endif
430 lcd_update();
431 sleep(HZ/2);
432 }
433 }
434 }
435 wps_loaded = true;
436
437 return numread > 0;
438 }
439
440 return false;
441}
442
443/* Format time into buf.
444 *
445 * buf - buffer to format to.
446 * buf_size - size of buffer.
447 * time - time to format, in milliseconds.
448 */
449void wps_format_time(char* buf, int buf_size, long time)
450{
451 if ( time < 3600000 ) {
452 snprintf(buf, buf_size, "%d:%02d",
453 (int) (time % 3600000 / 60000), (int) (time % 60000 / 1000));
454 } else {
455 snprintf(buf, buf_size, "%d:%02d:%02d",
456 (int) (time / 3600000), (int) (time % 3600000 / 60000),
457 (int) (time % 60000 / 1000));
458 }
459}
460
461/* Extract a part from a path.
462 *
463 * buf - buffer extract part to.
464 * buf_size - size of buffer.
465 * path - path to extract from.
466 * level - what to extract. 0 is file name, 1 is parent of file, 2 is
467 * parent of parent, etc.
468 *
469 * Returns buf if the desired level was found, NULL otherwise.
470 */
471static char* get_dir(char* buf, int buf_size, const char* path, int level)
472{
473 const char* sep;
474 const char* last_sep;
475 int len;
476
477 sep = path + strlen(path);
478 last_sep = sep;
479
480 while (sep > path)
481 {
482 if ('/' == *(--sep))
483 {
484 if (!level)
485 {
486 break;
487 }
488
489 level--;
490 last_sep = sep - 1;
491 }
492 }
493
494 if (level || (last_sep <= sep))
495 {
496 return NULL;
497 }
498
499 len = MIN(last_sep - sep, buf_size - 1);
500 strncpy(buf, sep + 1, len);
501 buf[len] = 0;
502 return buf;
503}
504
505/* Get the tag specified by the two characters at fmt.
506 *
507 * cid3 - ID3 data to get tag values from.
508 * nid3 - next-song ID3 data to get tag values from.
509 * tag - string (of two characters) specifying the tag to get.
510 * buf - buffer to certain tags, such as track number, play time or
511 * directory name.
512 * buf_size - size of buffer.
513 * flags - returns the type of the line. See constants i wps-display.h
514 *
515 * Returns the tag. NULL indicates the tag wasn't available.
516 */
517static char* get_tag(struct mp3entry* cid3,
518 struct mp3entry* nid3,
519 const char* tag,
520 char* buf,
521 int buf_size,
522 unsigned char* tag_len,
523 unsigned short* subline_time_mult,
524 unsigned char* flags,
525 int *intval)
526{
527 struct mp3entry *id3 = cid3; /* default to current song */
528
529 if ((0 == tag[0]) || (0 == tag[1]))
530 {
531 *tag_len = 0;
532 return NULL;
533 }
534
535 *tag_len = 2;
536
537 *intval = 0;
538
539 switch (tag[0])
540 {
541 case 'I': /* ID3 Information */
542 id3 = nid3; /* display next-song data */
543 *flags |= WPS_REFRESH_DYNAMIC;
544 if(!id3)
545 return NULL; /* no such info (yet) */
546 /* fall-through */
547 case 'i': /* ID3 Information */
548 *flags |= WPS_REFRESH_STATIC;
549 switch (tag[1])
550 {
551 case 't': /* ID3 Title */
552 return id3->title;
553
554 case 'a': /* ID3 Artist */
555 return id3->artist;
556
557 case 'n': /* ID3 Track Number */
558 if (id3->track_string)
559 return id3->track_string;
560
561 if (id3->tracknum) {
562 snprintf(buf, buf_size, "%d", id3->tracknum);
563 return buf;
564 }
565 return NULL;
566
567 case 'd': /* ID3 Album/Disc */
568 return id3->album;
569
570 case 'c': /* ID3 Composer */
571 return id3->composer;
572
573 case 'y': /* year */
574 if( id3->year_string )
575 return id3->year_string;
576
577 if (id3->year) {
578 snprintf(buf, buf_size, "%d", id3->year);
579 return buf;
580 }
581 return NULL;
582
583 case 'g': /* genre */
584 return id3_get_genre(id3);
585
586 case 'v': /* id3 version */
587 switch (id3->id3version) {
588 case ID3_VER_1_0:
589 return "1";
590
591 case ID3_VER_1_1:
592 return "1.1";
593
594 case ID3_VER_2_2:
595 return "2.2";
596
597 case ID3_VER_2_3:
598 return "2.3";
599
600 case ID3_VER_2_4:
601 return "2.4";
602
603 default:
604 return NULL;
605 }
606 }
607 break;
608
609 case 'F': /* File Information */
610 id3 = nid3;
611 *flags |= WPS_REFRESH_DYNAMIC;
612 if(!id3)
613 return NULL; /* no such info (yet) */
614 /* fall-through */
615 case 'f': /* File Information */
616 *flags |= WPS_REFRESH_STATIC;
617 switch(tag[1])
618 {
619 case 'v': /* VBR file? */
620 return id3->vbr ? "(avg)" : NULL;
621
622 case 'b': /* File Bitrate */
623 if(id3->bitrate)
624 snprintf(buf, buf_size, "%d", id3->bitrate);
625 else
626 snprintf(buf, buf_size, "?");
627 return buf;
628
629 case 'f': /* File Frequency */
630 snprintf(buf, buf_size, "%ld", id3->frequency);
631 return buf;
632
633 case 'p': /* File Path */
634 return id3->path;
635
636 case 'm': /* File Name - With Extension */
637 return get_dir(buf, buf_size, id3->path, 0);
638
639 case 'n': /* File Name */
640 if (get_dir(buf, buf_size, id3->path, 0))
641 {
642 /* Remove extension */
643 char* sep = strrchr(buf, '.');
644
645 if (NULL != sep)
646 {
647 *sep = 0;
648 }
649
650 return buf;
651 }
652 else
653 {
654 return NULL;
655 }
656
657 case 's': /* File Size (in kilobytes) */
658 snprintf(buf, buf_size, "%ld", id3->filesize / 1024);
659 return buf;
660
661 case 'c': /* File Codec */
662 if(id3->codectype == AFMT_UNKNOWN)
663 *intval = AFMT_NUM_CODECS;
664 else
665 *intval = id3->codectype;
666 return id3_get_codec(id3);
667 }
668 break;
669
670 case 'p': /* Playlist/Song Information */
671 switch(tag[1])
672 {
673 case 'b': /* progress bar */
674 *flags |= WPS_REFRESH_PLAYER_PROGRESS;
675#ifdef HAVE_LCD_CHARCELLS
676 snprintf(buf, buf_size, "%c", wps_progress_pat[0]);
677 full_line_progressbar=0;
678 return buf;
679#else
680 return "\x01";
681#endif
682 case 'f': /* full-line progress bar */
683#ifdef HAVE_LCD_CHARCELLS
684 if(is_new_player()) {
685 *flags |= WPS_REFRESH_PLAYER_PROGRESS;
686 *flags |= WPS_REFRESH_DYNAMIC;
687 full_line_progressbar=1;
688 /* we need 11 characters (full line) for
689 progress-bar */
690 snprintf(buf, buf_size, " ");
691 }
692 else
693 {
694 /* Tell the user if we have an OldPlayer */
695 snprintf(buf, buf_size, " <Old LCD> ");
696 }
697 return buf;
698#endif
699 case 'p': /* Playlist Position */
700 *flags |= WPS_REFRESH_STATIC;
701 snprintf(buf, buf_size, "%d", playlist_get_display_index());
702 return buf;
703
704 case 'n': /* Playlist Name (without path) */
705 *flags |= WPS_REFRESH_STATIC;
706 return playlist_name(NULL, buf, buf_size);
707
708 case 'e': /* Playlist Total Entries */
709 *flags |= WPS_REFRESH_STATIC;
710 snprintf(buf, buf_size, "%d", playlist_amount());
711 return buf;
712
713 case 'c': /* Current Time in Song */
714 *flags |= WPS_REFRESH_DYNAMIC;
715 wps_format_time(buf, buf_size,
716 id3->elapsed + ff_rewind_count);
717 return buf;
718
719 case 'r': /* Remaining Time in Song */
720 *flags |= WPS_REFRESH_DYNAMIC;
721 wps_format_time(buf, buf_size,
722 id3->length - id3->elapsed - ff_rewind_count);
723 return buf;
724
725 case 't': /* Total Time */
726 *flags |= WPS_REFRESH_STATIC;
727 wps_format_time(buf, buf_size, id3->length);
728 return buf;
729
730#ifdef HAVE_LCD_BITMAP
731 case 'm': /* Peak Meter */
732 *flags |= WPS_REFRESH_PEAK_METER;
733 return "\x01";
734#endif
735 case 's': /* shuffle */
736 *flags |= WPS_REFRESH_DYNAMIC;
737 if ( global_settings.playlist_shuffle )
738 return "s";
739 else
740 return NULL;
741 break;
742
743 case 'v': /* volume */
744 *flags |= WPS_REFRESH_DYNAMIC;
745 snprintf(buf, buf_size, "%d%%", global_settings.volume);
746 *intval = global_settings.volume / 10 + 1;
747 return buf;
748
749 }
750 break;
751
752 case 'm':
753 switch (tag[1])
754 {
755 case 'm': /* playback repeat mode */
756 *flags |= WPS_REFRESH_DYNAMIC;
757 *intval = global_settings.repeat_mode + 1;
758 snprintf(buf, buf_size, "%d", *intval);
759 return buf;
760
761 /* playback status */
762 case 'p': /* play */
763 *flags |= WPS_REFRESH_DYNAMIC;
764 int status = audio_status();
765 *intval = 1;
766 if (status == AUDIO_STATUS_PLAY && \
767 !(status & AUDIO_STATUS_PAUSE))
768 *intval = 2;
769 if (audio_status() & AUDIO_STATUS_PAUSE && \
770 (! status_get_ffmode()))
771 *intval = 3;
772 if (status_get_ffmode() == STATUS_FASTFORWARD)
773 *intval = 4;
774 if (status_get_ffmode() == STATUS_FASTBACKWARD)
775 *intval = 5;
776 snprintf(buf, buf_size, "%d", *intval);
777 return buf;
778
779#if CONFIG_KEYPAD == IRIVER_H100_PAD
780 case 'h': /* hold */
781 *flags |= WPS_REFRESH_DYNAMIC;
782 if (button_hold())
783 return "h";
784 else
785 return NULL;
786 case 'r': /* remote hold */
787 *flags |= WPS_REFRESH_DYNAMIC;
788 if (remote_button_hold())
789 return "r";
790 else
791 return NULL;
792#endif
793 }
794 break;
795
796 case 'b': /* battery info */
797 *flags |= WPS_REFRESH_DYNAMIC;
798 switch (tag[1]) {
799 case 'l': /* battery level */
800 {
801 int l = battery_level();
802 if (l > -1)
803 {
804 snprintf(buf, buf_size, "%d%%", l);
805 *intval = l / 20 + 1;
806 }
807 else
808 {
809 *intval = 6;
810 return "?%";
811 }
812 return buf;
813 }
814
815 case 't': /* estimated battery time */
816 {
817 int t = battery_time();
818 if (t >= 0)
819 snprintf(buf, buf_size, "%dh %dm", t / 60, t % 60);
820 else
821 strncpy(buf, "?h ?m", buf_size);
822 return buf;
823 }
824
825 case 'p': /* External power plugged in? */
826 {
827 if(charger_inserted())
828 return "p";
829 else
830 return NULL;
831 }
832 }
833 break;
834
835 case 'D': /* Directory path information */
836 id3 = nid3; /* next song please! */
837 *flags |= WPS_REFRESH_DYNAMIC;
838 if(!id3)
839 return NULL; /* no such info (yet) */
840 /* fall-through */
841 case 'd': /* Directory path information */
842 {
843 int level = tag[1] - '0';
844 *flags |= WPS_REFRESH_STATIC;
845 /* d1 through d9 */
846 if ((0 < level) && (9 > level))
847 {
848 return get_dir(buf, buf_size, id3->path, level);
849 }
850 }
851 break;
852
853 case 't': /* set sub line time multiplier */
854 {
855 int d = 1;
856 int time_mult = 0;
857 bool have_point = false;
858 bool have_tenth = false;
859
860 while (((tag[d] >= '0') &&
861 (tag[d] <= '9')) ||
862 (tag[d] == '.'))
863 {
864 if (tag[d] != '.')
865 {
866 time_mult = time_mult * 10;
867 time_mult = time_mult + tag[d] - '0';
868 if (have_point)
869 {
870 have_tenth = true;
871 d++;
872 break;
873 }
874 }
875 else
876 {
877 have_point = true;
878 }
879 d++;
880 }
881
882 if (have_tenth == false)
883 time_mult *= 10;
884
885 *subline_time_mult = time_mult;
886 *tag_len = d;
887
888 buf[0] = 0;
889 return buf;
890 }
891 break;
892 case 'r': /* Runtime database Information */
893 switch(tag[1])
894 {
895 case 'p': /* Playcount */
896 *flags |= WPS_REFRESH_STATIC;
897 snprintf(buf, buf_size, "%ld", cid3->playcount);
898 return buf;
899 case 'r': /* Rating */
900 *flags |= WPS_REFRESH_STATIC;
901 *intval = cid3->rating+1;
902 snprintf(buf, buf_size, "%d", cid3->rating);
903 return buf;
904 }
905 break;
906 }
907 return NULL;
908}
909
910/* Skip to the end of the current %? conditional.
911 *
912 * fmt - string to skip it. Should point to somewhere after the leading
913 * "<" char (and before or at the last ">").
914 * num - number of |'s to skip, or 0 to skip to the end (the ">").
915 *
916 * Returns the new position in fmt.
917 */
918static const char* skip_conditional(const char* fmt, int num)
919{
920 int level = 1;
921 int count = num;
922 const char *last_alternative = NULL;
923
924 while (*fmt)
925 {
926 switch (*fmt++)
927 {
928 case '%':
929 break;
930
931 case '|':
932 if(1 == level) {
933 last_alternative = fmt;
934 if(num) {
935 count--;
936 if(count == 0)
937 return fmt;
938 continue;
939 }
940 }
941 continue;
942
943 case '>':
944 if (0 == --level)
945 {
946 /* We're just skipping to the end */
947 if(num == 0)
948 return fmt;
949
950 /* If we are parsing an enum, we'll return the selected
951 item. If there weren't enough items in the enum, we'll
952 return the last one found. */
953 if(count && last_alternative)
954 {
955 return last_alternative;
956 }
957 return fmt - 1;
958 }
959 continue;
960
961 default:
962 continue;
963 }
964
965 switch (*fmt++)
966 {
967 case 0:
968 case '%':
969 case '|':
970 case '<':
971 case '>':
972 break;
973
974 case '?':
975 while (*fmt && ('<' != *fmt))
976 fmt++;
977
978 if ('<' == *fmt)
979 fmt++;
980
981 level++;
982 break;
983
984 default:
985 break;
986 }
987 }
988
989 return fmt;
990}
991
992/* Generate the display based on id3 information and format string.
993 *
994 * buf - char buffer to write the display to.
995 * buf_size - the size of buffer.
996 * id3 - the ID3 data to format with.
997 * nid3 - the ID3 data of the next song (might by NULL)
998 * fmt - format description.
999 * flags - returns the type of the line. See constants i wps-display.h
1000 */
1001static void format_display(char* buf,
1002 int buf_size,
1003 struct mp3entry* id3,
1004 struct mp3entry* nid3, /* next song's id3 */
1005 const char* fmt,
1006 struct align_pos* align,
1007 unsigned short* subline_time_mult,
1008 unsigned char* flags)
1009{
1010 char temp_buf[128];
1011 char* buf_start = buf;
1012 char* buf_end = buf + buf_size - 1; /* Leave room for end null */
1013 char* value = NULL;
1014 int level = 0;
1015 unsigned char tag_length;
1016 int intval;
1017 int cur_align;
1018 char* cur_align_start;
1019#ifdef HAVE_LCD_BITMAP
1020 int n;
1021#endif
1022
1023 cur_align_start = buf;
1024 cur_align = WPS_ALIGN_LEFT;
1025 *subline_time_mult = DEFAULT_SUBLINE_TIME_MULTIPLIER;
1026
1027 align->left = 0;
1028 align->center = 0;
1029 align->right = 0;
1030
1031 while (fmt && *fmt && buf < buf_end)
1032 {
1033 switch (*fmt)
1034 {
1035 case '%':
1036 ++fmt;
1037 break;
1038
1039 case '|':
1040 case '>':
1041 if (level > 0)
1042 {
1043 fmt = skip_conditional(fmt, 0);
1044 level--;
1045 continue;
1046 }
1047 /* Else fall through */
1048
1049 default:
1050 *buf++ = *fmt++;
1051 continue;
1052 }
1053
1054 switch (*fmt)
1055 {
1056 case 0:
1057 *buf++ = '%';
1058 break;
1059 case 'a':
1060 ++fmt;
1061 /* remember where the current aligned text started */
1062 switch (cur_align)
1063 {
1064 case WPS_ALIGN_LEFT:
1065 align->left = cur_align_start;
1066 break;
1067
1068 case WPS_ALIGN_CENTER:
1069 align->center = cur_align_start;
1070 break;
1071
1072 case WPS_ALIGN_RIGHT:
1073 align->right = cur_align_start;
1074 break;
1075 }
1076 /* start a new alignment */
1077 switch (*fmt)
1078 {
1079 case 'l':
1080 cur_align = WPS_ALIGN_LEFT;
1081 break;
1082 case 'c':
1083 cur_align = WPS_ALIGN_CENTER;
1084 break;
1085 case 'r':
1086 cur_align = WPS_ALIGN_RIGHT;
1087 break;
1088 }
1089 *buf++=0;
1090 cur_align_start = buf;
1091 ++fmt;
1092 break;
1093 case 's':
1094 *flags |= WPS_REFRESH_SCROLL;
1095 ++fmt;
1096 break;
1097
1098 case 'x': /* image support */
1099#ifdef HAVE_LCD_BITMAP
1100 /* skip preload or regular image tag */
1101 if ('l' == *(fmt+1) || '|' == *(fmt+1))
1102 {
1103 while (*fmt && *fmt != '\n')
1104 fmt++;
1105 }
1106 else if ('d' == *(fmt+1))
1107 {
1108 fmt+=2;
1109
1110 /* get the image ID */
1111 n = *fmt;
1112 if(n >= 'a' && n <= 'z')
1113 n -= 'a';
1114 if(n >= 'A' && n <= 'Z')
1115 n = n - 'A' + 26;
1116
1117 if (n >= 0 && n < MAX_IMAGES && img[n].loaded) {
1118 img[n].display = true;
1119 }
1120 }
1121#endif
1122 fmt++;
1123 break;
1124
1125
1126 case '%':
1127 case '|':
1128 case '<':
1129 case '>':
1130 case ';':
1131 *buf++ = *fmt++;
1132 break;
1133
1134 case '?':
1135 fmt++;
1136 value = get_tag(id3, nid3, fmt, temp_buf, sizeof(temp_buf),
1137 &tag_length, subline_time_mult, flags,
1138 &intval);
1139
1140 while (*fmt && ('<' != *fmt))
1141 fmt++;
1142
1143 if ('<' == *fmt)
1144 fmt++;
1145
1146 /* No value, so skip to else part, using a sufficiently high
1147 value to "hit" the last part of the conditional */
1148 if ((!value) || (!strlen(value)))
1149 fmt = skip_conditional(fmt, 1000);
1150 else
1151 if(intval > 1) /* enum */
1152 fmt = skip_conditional(fmt, intval - 1);
1153
1154 level++;
1155 break;
1156
1157 default:
1158 value = get_tag(id3, nid3, fmt, temp_buf, sizeof(temp_buf),
1159 &tag_length, subline_time_mult, flags,
1160 &intval);
1161 fmt += tag_length;
1162
1163 if (value)
1164 {
1165 while (*value && (buf < buf_end))
1166 *buf++ = *value++;
1167 }
1168 }
1169 }
1170
1171 /* remember where the current aligned text started */
1172 switch (cur_align)
1173 {
1174 case WPS_ALIGN_LEFT:
1175 align->left = cur_align_start;
1176 break;
1177
1178 case WPS_ALIGN_CENTER:
1179 align->center = cur_align_start;
1180 break;
1181
1182 case WPS_ALIGN_RIGHT:
1183 align->right = cur_align_start;
1184 break;
1185 }
1186
1187 *buf = 0;
1188
1189 /* if resulting line is an empty line, set the subline time to 0 */
1190 if (buf - buf_start == 0)
1191 *subline_time_mult = 0;
1192
1193 /* If no flags have been set, the line didn't contain any format codes.
1194 We still want to refresh it. */
1195 if(*flags == 0)
1196 *flags = WPS_REFRESH_STATIC;
1197}
1198
1199bool wps_refresh(struct mp3entry* id3,
1200 struct mp3entry* nid3,
1201 int ffwd_offset,
1202 unsigned char refresh_mode)
1203{
1204 char buf[MAX_PATH];
1205 unsigned char flags;
1206 int i;
1207 bool update_line;
1208 bool only_one_subline;
1209 bool new_subline_refresh;
1210 int search;
1211 int search_start;
1212#ifdef HAVE_LCD_BITMAP
1213 int h = font_get(FONT_UI)->height;
1214 int offset = global_settings.statusbar ? STATUSBAR_HEIGHT : 0;
1215 /* to find out wether the peak meter is enabled we
1216 assume it wasn't until we find a line that contains
1217 the peak meter. We can't use peak_meter_enabled itself
1218 because that would mean to turn off the meter thread
1219 temporarily. (That shouldn't matter unless yield
1220 or sleep is called but who knows...)
1221 */
1222 bool enable_pm = false;
1223
1224 /* Set images to not to be displayed */
1225 for (i = 0; i < MAX_IMAGES; i++) {
1226 img[i].display = false;
1227 }
1228#endif
1229
1230 /* reset to first subline if refresh all flag is set */
1231 if (refresh_mode == WPS_REFRESH_ALL)
1232 {
1233 for (i=0; i<MAX_LINES; i++)
1234 {
1235 curr_subline[i] = SUBLINE_RESET;
1236 }
1237 }
1238
1239#ifdef HAVE_LCD_CHARCELLS
1240 for (i=0; i<8; i++) {
1241 if (wps_progress_pat[i]==0)
1242 wps_progress_pat[i]=lcd_get_locked_pattern();
1243 }
1244#endif
1245
1246 if (!id3)
1247 {
1248 lcd_stop_scroll();
1249 return false;
1250 }
1251
1252 ff_rewind_count = ffwd_offset;
1253
1254 for (i = 0; i < MAX_LINES; i++)
1255 {
1256 new_subline_refresh = false;
1257 only_one_subline = false;
1258
1259 /* if time to advance to next sub-line */
1260 if (TIME_AFTER(current_tick, subline_expire_time[i] - 1) ||
1261 (curr_subline[i] == SUBLINE_RESET))
1262 {
1263 /* search all sublines until the next subline with time > 0
1264 is found or we get back to the subline we started with */
1265 if (curr_subline[i] == SUBLINE_RESET)
1266 search_start = 0;
1267 else
1268 search_start = curr_subline[i];
1269 for (search=0; search<MAX_SUBLINES; search++)
1270 {
1271 curr_subline[i]++;
1272
1273 /* wrap around if beyond last defined subline or MAX_SUBLINES */
1274 if ((!format_lines[i][curr_subline[i]]) ||
1275 (curr_subline[i] == MAX_SUBLINES))
1276 {
1277 if (curr_subline[i] == 1)
1278 only_one_subline = true;
1279 curr_subline[i] = 0;
1280 }
1281
1282 /* if back where we started after search or
1283 only one subline is defined on the line */
1284 if (((search > 0) && (curr_subline[i] == search_start)) ||
1285 only_one_subline)
1286 {
1287 /* no other subline with a time > 0 exists */
1288 subline_expire_time[i] = current_tick + 100 * HZ;
1289 break;
1290 }
1291 else
1292 {
1293 /* get initial time multiplier and
1294 line type flags for this subline */
1295 format_display(buf, sizeof(buf), id3, nid3,
1296 format_lines[i][curr_subline[i]],
1297 &format_align[i][curr_subline[i]],
1298 &time_mult[i][curr_subline[i]],
1299 &line_type[i][curr_subline[i]]);
1300
1301 /* only use this subline if subline time > 0 */
1302 if (time_mult[i][curr_subline[i]] > 0)
1303 {
1304 new_subline_refresh = true;
1305 subline_expire_time[i] = current_tick +
1306 BASE_SUBLINE_TIME * time_mult[i][curr_subline[i]];
1307 break;
1308 }
1309 }
1310 }
1311
1312 }
1313
1314 update_line = false;
1315
1316 if ( !format_lines[i][curr_subline[i]] )
1317 break;
1318
1319 if ((line_type[i][curr_subline[i]] & refresh_mode) ||
1320 (refresh_mode == WPS_REFRESH_ALL) ||
1321 new_subline_refresh)
1322 {
1323 flags = 0;
1324#ifdef HAVE_LCD_BITMAP
1325 int left_width, left_xpos;
1326 int center_width, center_xpos;
1327 int right_width, right_xpos;
1328 int space_width;
1329 int string_height;
1330 int ypos;
1331#endif
1332
1333 format_display(buf, sizeof(buf), id3, nid3,
1334 format_lines[i][curr_subline[i]],
1335 &format_align[i][curr_subline[i]],
1336 &time_mult[i][curr_subline[i]],
1337 &flags);
1338 line_type[i][curr_subline[i]] = flags;
1339
1340#ifdef HAVE_LCD_BITMAP
1341 /* progress */
1342 if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1343 {
1344#define PROGRESS_BAR_HEIGHT 6 /* this should probably be defined elsewhere; config-*.h perhaps? */
1345 int sby = i*h + offset + (h > 7 ? (h - 6) / 2 : 1);
1346 scrollbar(0, sby, LCD_WIDTH, PROGRESS_BAR_HEIGHT,
1347 id3->length?id3->length:1, 0,
1348 id3->length?id3->elapsed + ff_rewind_count:0,
1349 HORIZONTAL);
1350#ifdef AB_REPEAT_ENABLE
1351 if ( ab_repeat_mode_enabled() )
1352 ab_draw_markers(id3->length, 0, sby, LCD_WIDTH, PROGRESS_BAR_HEIGHT);
1353#endif
1354 update_line = true;
1355 }
1356 if (flags & refresh_mode & WPS_REFRESH_PEAK_METER) {
1357 /* peak meter */
1358 int peak_meter_y;
1359
1360 update_line = true;
1361 peak_meter_y = i * h + offset;
1362
1363 /* The user might decide to have the peak meter in the last
1364 line so that it is only displayed if no status bar is
1365 visible. If so we neither want do draw nor enable the
1366 peak meter. */
1367 if (peak_meter_y + h <= LCD_HEIGHT) {
1368 /* found a line with a peak meter -> remember that we must
1369 enable it later */
1370 enable_pm = true;
1371 peak_meter_draw(0, peak_meter_y, LCD_WIDTH,
1372 MIN(h, LCD_HEIGHT - peak_meter_y));
1373 }
1374 }
1375#else
1376 /* progress */
1377 if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS) {
1378 if (full_line_progressbar)
1379 draw_player_fullbar(buf, sizeof(buf),
1380 id3, ff_rewind_count);
1381 else
1382 draw_player_progress(id3, ff_rewind_count);
1383 }
1384#endif
1385#ifdef HAVE_LCD_BITMAP
1386 /* calculate different string sizes and positions */
1387 lcd_getstringsize(" ", &space_width, &string_height);
1388 if (format_align[i][curr_subline[i]].left != 0) {
1389 lcd_getstringsize(format_align[i][curr_subline[i]].left,
1390 &left_width, &string_height);
1391 }
1392 else {
1393 left_width = 0;
1394 }
1395 left_xpos = 0;
1396
1397 if (format_align[i][curr_subline[i]].center != 0) {
1398 lcd_getstringsize(format_align[i][curr_subline[i]].center,
1399 &center_width, &string_height);
1400 }
1401 else {
1402 center_width = 0;
1403 }
1404 center_xpos=(LCD_WIDTH - center_width) / 2;
1405
1406 if (format_align[i][curr_subline[i]].right != 0) {
1407 lcd_getstringsize(format_align[i][curr_subline[i]].right,
1408 &right_width, &string_height);
1409 }
1410 else {
1411 right_width = 0;
1412 }
1413 right_xpos = (LCD_WIDTH - right_width);
1414
1415 /* Checks for overlapping strings.
1416 If needed the overlapping strings will be merged, separated by a
1417 space */
1418
1419 /* CASE 1: left and centered string overlap */
1420 /* there is a left string, need to merge left and center */
1421 if ((left_width != 0 && center_width != 0) &&
1422 (left_xpos + left_width + space_width > center_xpos)) {
1423 /* replace the former separator '\0' of left and
1424 center string with a space */
1425 *(--format_align[i][curr_subline[i]].center) = ' ';
1426 /* calculate the new width and position of the merged string */
1427 left_width = left_width + space_width + center_width;
1428 left_xpos = 0;
1429 /* there is no centered string anymore */
1430 center_width = 0;
1431 }
1432 /* there is no left string, move center to left */
1433 if ((left_width == 0 && center_width != 0) &&
1434 (left_xpos + left_width > center_xpos)) {
1435 /* move the center string to the left string */
1436 format_align[i][curr_subline[i]].left = format_align[i][curr_subline[i]].center;
1437 /* calculate the new width and position of the string */
1438 left_width = center_width;
1439 left_xpos = 0;
1440 /* there is no centered string anymore */
1441 center_width = 0;
1442 }
1443
1444 /* CASE 2: centered and right string overlap */
1445 /* there is a right string, need to merge center and right */
1446 if ((center_width != 0 && right_width != 0) &&
1447 (center_xpos + center_width + space_width > right_xpos)) {
1448 /* replace the former separator '\0' of center and
1449 right string with a space */
1450 *(--format_align[i][curr_subline[i]].right) = ' ';
1451 /* move the center string to the right after merge */
1452 format_align[i][curr_subline[i]].right = format_align[i][curr_subline[i]].center;
1453 /* calculate the new width and position of the merged string */
1454 right_width = center_width + space_width + right_width;
1455 right_xpos = (LCD_WIDTH - right_width);
1456 /* there is no centered string anymore */
1457 center_width = 0;
1458 }
1459 /* there is no right string, move center to right */
1460 if ((center_width != 0 && right_width == 0) &&
1461 (center_xpos + center_width > right_xpos)) {
1462 /* move the center string to the right string */
1463 format_align[i][curr_subline[i]].right = format_align[i][curr_subline[i]].center;
1464 /* calculate the new width and position of the string */
1465 right_width = center_width;
1466 right_xpos = (LCD_WIDTH - right_width);
1467 /* there is no centered string anymore */
1468 center_width = 0;
1469 }
1470
1471 /* CASE 3: left and right overlap
1472 There is no center string anymore, either there never
1473 was one or it has been merged in case 1 or 2 */
1474 /* there is a left string, need to merge left and right */
1475 if ((left_width != 0 && center_width == 0 && right_width != 0) &&
1476 (left_xpos + left_width + space_width > right_xpos)) {
1477 /* replace the former separator '\0' of left and
1478 right string with a space */
1479 *(--format_align[i][curr_subline[i]].right) = ' ';
1480 /* calculate the new width and position of the string */
1481 left_width = left_width + space_width + right_width;
1482 left_xpos = 0;
1483 /* there is no right string anymore */
1484 right_width = 0;
1485 }
1486 /* there is no left string, move right to left */
1487 if ((left_width == 0 && center_width == 0 && right_width != 0) &&
1488 (left_xpos + left_width > right_xpos)) {
1489 /* move the right string to the left string */
1490 format_align[i][curr_subline[i]].left = format_align[i][curr_subline[i]].right;
1491 /* calculate the new width and position of the string */
1492 left_width = right_width;
1493 left_xpos = 0;
1494 /* there is no right string anymore */
1495 right_width = 0;
1496 }
1497
1498#endif
1499
1500 if (flags & WPS_REFRESH_SCROLL) {
1501
1502 /* scroll line */
1503 if ((refresh_mode & WPS_REFRESH_SCROLL) ||
1504 new_subline_refresh) {
1505#ifdef HAVE_LCD_BITMAP
1506 ypos = (i*string_height)+lcd_getymargin();
1507 update_line = true;
1508
1509 if (left_width>LCD_WIDTH) {
1510 lcd_puts_scroll(0, i, format_align[i][curr_subline[i]].left);
1511 } else {
1512 /* clear the line first */
1513 lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1514 lcd_fillrect(0, ypos, LCD_WIDTH, string_height);
1515 lcd_set_drawmode(DRMODE_SOLID);
1516
1517 /* Nasty hack: we output an empty scrolling string,
1518 which will reset the scroller for that line */
1519 lcd_puts_scroll(0, i, "");
1520
1521 /* print aligned strings */
1522 if (left_width != 0)
1523 {
1524 lcd_putsxy(left_xpos, ypos, format_align[i][curr_subline[i]].left);
1525 }
1526 if (center_width != 0)
1527 {
1528 lcd_putsxy(center_xpos, ypos, format_align[i][curr_subline[i]].center);
1529 }
1530 if (right_width != 0)
1531 {
1532 lcd_putsxy(right_xpos, ypos, format_align[i][curr_subline[i]].right);
1533 }
1534 }
1535#else
1536 lcd_puts_scroll(0, i, buf);
1537 update_line = true;
1538#endif
1539 }
1540 }
1541 else if (flags & (WPS_REFRESH_DYNAMIC | WPS_REFRESH_STATIC))
1542 {
1543 /* dynamic / static line */
1544 if ((refresh_mode & (WPS_REFRESH_DYNAMIC|WPS_REFRESH_STATIC)) ||
1545 new_subline_refresh)
1546 {
1547#ifdef HAVE_LCD_BITMAP
1548 ypos = (i*string_height)+lcd_getymargin();
1549 update_line = true;
1550
1551 /* clear the line first */
1552 lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1553 lcd_fillrect(0, ypos, LCD_WIDTH, string_height);
1554 lcd_set_drawmode(DRMODE_SOLID);
1555
1556 /* Nasty hack: we output an empty scrolling string,
1557 which will reset the scroller for that line */
1558 lcd_puts_scroll(0, i, "");
1559
1560 /* print aligned strings */
1561 if (left_width != 0)
1562 {
1563 lcd_putsxy(left_xpos, ypos, format_align[i][curr_subline[i]].left);
1564 }
1565 if (center_width != 0)
1566 {
1567 lcd_putsxy(center_xpos, ypos, format_align[i][curr_subline[i]].center);
1568 }
1569 if (right_width != 0)
1570 {
1571 lcd_putsxy(right_xpos, ypos, format_align[i][curr_subline[i]].right);
1572 }
1573#else
1574 update_line = true;
1575 lcd_puts(0, i, buf);
1576#endif
1577 }
1578 }
1579 }
1580#ifdef HAVE_LCD_BITMAP
1581 if (update_line) {
1582 lcd_update_rect(0, i*h + offset, LCD_WIDTH, h);
1583 }
1584#endif
1585 }
1586
1587#ifdef HAVE_LCD_BITMAP
1588 /* Display all images */
1589 for (i = 0; i < MAX_IMAGES; i++) {
1590 if(img[i].always_display)
1591 img[i].display = img[i].always_display;
1592 }
1593 wps_display_images();
1594
1595 /* Now we know wether the peak meter is used.
1596 So we can enable / disable the peak meter thread */
1597 peak_meter_enabled = enable_pm;
1598#endif
1599
1600#if defined(CONFIG_BACKLIGHT) && !defined(SIMULATOR)
1601 if (global_settings.caption_backlight && id3) {
1602 /* turn on backlight n seconds before track ends, and turn it off n
1603 seconds into the new track. n == backlight_timeout, or 5s */
1604 int n =
1605 backlight_timeout_value[global_settings.backlight_timeout] * 1000;
1606
1607 if ( n < 1000 )
1608 n = 5000; /* use 5s if backlight is always on or off */
1609
1610 if ((id3->elapsed < 1000) ||
1611 ((id3->length - id3->elapsed) < (unsigned)n))
1612 backlight_on();
1613 }
1614#endif
1615 return true;
1616}
1617
1618bool wps_display(struct mp3entry* id3,
1619 struct mp3entry* nid3)
1620{
1621 if (!id3 && !(audio_status() & AUDIO_STATUS_PLAY))
1622 {
1623 global_settings.resume_index = -1;
1624#ifdef HAVE_LCD_CHARCELLS
1625 gui_syncsplash(HZ, true, str(LANG_END_PLAYLIST_PLAYER));
1626#else
1627 status_draw(true);
1628 gui_syncsplash(HZ, true, str(LANG_END_PLAYLIST_RECORDER));
1629#endif
1630 return true;
1631 }
1632 else
1633 {
1634 lcd_clear_display();
1635
1636 if (!wps_loaded) {
1637 if ( !format_buffer[0] ) {
1638#ifdef HAVE_LCD_BITMAP
1639 wps_format("%s%?it<%?in<%in. |>%it|%fn>\n"
1640 "%s%?ia<%ia|%?d2<%d2|(root)>>\n"
1641 "%s%?id<%id|%?d1<%d1|(root)>> %?iy<(%iy)|>\n"
1642 "\n"
1643 "%al%pc/%pt%ar[%pp:%pe]\n"
1644 "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n"
1645 "%pb\n"
1646 "%pm\n", NULL, 0);
1647#else
1648 wps_format("%s%pp/%pe: %?it<%it|%fn> - %?ia<%ia|%d2> - %?id<%id|%d1>\n"
1649 "%pc%?ps<*|/>%pt\n", NULL, 0);
1650#endif
1651 }
1652 }
1653 }
1654 yield();
1655 wps_refresh(id3, nid3, 0, WPS_REFRESH_ALL);
1656 status_draw(true);
1657#ifdef HAVE_LCD_BITMAP
1658 wps_display_images();
1659#endif
1660 lcd_update();
1661 return false;
1662}
1663
1664#ifdef HAVE_LCD_CHARCELLS
1665static bool draw_player_progress(const struct mp3entry* id3,
1666 int ff_rewwind_count)
1667{
1668 char player_progressbar[7];
1669 char binline[36];
1670 int songpos = 0;
1671 int i,j;
1672
1673 if (!id3)
1674 return false;
1675
1676 memset(binline, 1, sizeof binline);
1677 memset(player_progressbar, 1, sizeof player_progressbar);
1678
1679 if(id3->elapsed >= id3->length)
1680 songpos = 0;
1681 else
1682 {
1683 if(wps_time_countup == false)
1684 songpos = ((id3->elapsed - ff_rewwind_count) * 36) / id3->length;
1685 else
1686 songpos = ((id3->elapsed + ff_rewwind_count) * 36) / id3->length;
1687 }
1688 for (i=0; i < songpos; i++)
1689 binline[i] = 0;
1690
1691 for (i=0; i<=6; i++) {
1692 for (j=0;j<5;j++) {
1693 player_progressbar[i] <<= 1;
1694 player_progressbar[i] += binline[i*5+j];
1695 }
1696 }
1697 lcd_define_pattern(wps_progress_pat[0], player_progressbar);
1698 return true;
1699}
1700
1701static void draw_player_fullbar(char* buf, int buf_size,
1702 const struct mp3entry* id3,
1703 int ff_rewwind_count)
1704{
1705 int i,j,lcd_char_pos;
1706
1707 char player_progressbar[7];
1708 char binline[36];
1709 static const char numbers[12][4][3]={
1710 {{1,1,1},{1,0,1},{1,0,1},{1,1,1}},/*0*/
1711 {{0,1,0},{1,1,0},{0,1,0},{0,1,0}},/*1*/
1712 {{1,1,1},{0,0,1},{0,1,0},{1,1,1}},/*2*/
1713 {{1,1,1},{0,0,1},{0,1,1},{1,1,1}},/*3*/
1714 {{1,0,0},{1,1,0},{1,1,1},{0,1,0}},/*4*/
1715 {{1,1,1},{1,1,0},{0,0,1},{1,1,0}},/*5*/
1716 {{1,1,1},{1,0,0},{1,1,1},{1,1,1}},/*6*/
1717 {{1,1,1},{0,0,1},{0,1,0},{1,0,0}},/*7*/
1718 {{1,1,1},{1,1,1},{1,0,1},{1,1,1}},/*8*/
1719 {{1,1,1},{1,1,1},{0,0,1},{1,1,1}},/*9*/
1720 {{0,0,0},{0,1,0},{0,0,0},{0,1,0}},/*:*/
1721 {{0,0,0},{0,0,0},{0,0,0},{0,0,0}} /*<blank>*/
1722 };
1723
1724 int songpos = 0;
1725 int digits[6];
1726 int time;
1727 char timestr[7];
1728
1729 for (i=0; i < buf_size; i++)
1730 buf[i] = ' ';
1731
1732 if(id3->elapsed >= id3->length)
1733 songpos = 55;
1734 else {
1735 if(wps_time_countup == false)
1736 songpos = ((id3->elapsed - ff_rewwind_count) * 55) / id3->length;
1737 else
1738 songpos = ((id3->elapsed + ff_rewwind_count) * 55) / id3->length;
1739 }
1740
1741 time=(id3->elapsed + ff_rewind_count);
1742
1743 memset(timestr, 0, sizeof(timestr));
1744 wps_format_time(timestr, sizeof(timestr), time);
1745 for(lcd_char_pos=0; lcd_char_pos<6; lcd_char_pos++) {
1746 digits[lcd_char_pos] = map_fullbar_char(timestr[lcd_char_pos]);
1747 }
1748
1749 /* build the progressbar-icons */
1750 for (lcd_char_pos=0; lcd_char_pos<6; lcd_char_pos++) {
1751 memset(binline, 0, sizeof binline);
1752 memset(player_progressbar, 0, sizeof player_progressbar);
1753
1754 /* make the character (progressbar & digit)*/
1755 for (i=0; i<7; i++) {
1756 for (j=0;j<5;j++) {
1757 /* make the progressbar */
1758 if (lcd_char_pos==(songpos/5)) {
1759 /* partial */
1760 if ((j<(songpos%5))&&(i>4))
1761 binline[i*5+j] = 1;
1762 else
1763 binline[i*5+j] = 0;
1764 }
1765 else {
1766 if (lcd_char_pos<(songpos/5)) {
1767 /* full character */
1768 if (i>4)
1769 binline[i*5+j] = 1;
1770 }
1771 }
1772 /* insert the digit */
1773 if ((j<3)&&(i<4)) {
1774 if (numbers[digits[lcd_char_pos]][i][j]==1)
1775 binline[i*5+j] = 1;
1776 }
1777 }
1778 }
1779
1780 for (i=0; i<=6; i++) {
1781 for (j=0;j<5;j++) {
1782 player_progressbar[i] <<= 1;
1783 player_progressbar[i] += binline[i*5+j];
1784 }
1785 }
1786
1787 lcd_define_pattern(wps_progress_pat[lcd_char_pos+1],player_progressbar);
1788 buf[lcd_char_pos]=wps_progress_pat[lcd_char_pos+1];
1789
1790 }
1791
1792 /* make rest of the progressbar if necessary */
1793 if (songpos/5>5) {
1794
1795 /* set the characters positions that use the full 5 pixel wide bar */
1796 for (lcd_char_pos=6; lcd_char_pos < (songpos/5); lcd_char_pos++)
1797 buf[lcd_char_pos] = 0x86; /* '_' */
1798
1799 /* build the partial bar character for the tail character position */
1800 memset(binline, 0, sizeof binline);
1801 memset(player_progressbar, 0, sizeof player_progressbar);
1802
1803 for (i=5; i<7; i++) {
1804 for (j=0;j<5;j++) {
1805 if (j<(songpos%5)) {
1806 binline[i*5+j] = 1;
1807 }
1808 }
1809 }
1810
1811 for (i=0; i<7; i++) {
1812 for (j=0;j<5;j++) {
1813 player_progressbar[i] <<= 1;
1814 player_progressbar[i] += binline[i*5+j];
1815 }
1816 }
1817
1818 lcd_define_pattern(wps_progress_pat[7],player_progressbar);
1819
1820 buf[songpos/5]=wps_progress_pat[7];
1821 }
1822}
1823
1824static char map_fullbar_char(char ascii_val)
1825{
1826 if (ascii_val >= '0' && ascii_val <= '9') {
1827 return(ascii_val - '0');
1828 }
1829 else if (ascii_val == ':') {
1830 return(10);
1831 }
1832 else
1833 return(11); /* anything besides a number or ':' is mapped to <blank> */
1834}
1835
1836
1837#endif
1838
1839/* -----------------------------------------------------------------
1840 * vim: et sw=4 ts=8 sts=4 tw=78
1841 */