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