summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/gui/gwps-common.c2024
-rw-r--r--apps/gui/gwps-common.h27
-rw-r--r--apps/gui/gwps.c853
-rw-r--r--apps/gui/gwps.h351
4 files changed, 3255 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
diff --git a/apps/gui/gwps-common.h b/apps/gui/gwps-common.h
new file mode 100644
index 0000000000..5cf052898d
--- /dev/null
+++ b/apps/gui/gwps-common.h
@@ -0,0 +1,27 @@
1#ifndef _GWPS_COMMON_
2#define _GWPS_COMMON_
3#include <stdbool.h>
4#include <sys/types.h> /* for size_t */
5
6/* to avoid the unnecessary include if gwps.h */
7struct mp3entry;
8struct gui_img;
9struct wps_data;
10struct gui_wps;
11struct align_pos;
12
13void gui_wps_format_time(char* buf, int buf_size, long time);
14void fade(bool fade_in);
15void gui_wps_format(struct wps_data *data, const char *bmpdir, size_t bmpdirlen);
16bool gui_wps_refresh(struct gui_wps *gwps, int ffwd_offset,
17 unsigned char refresh_mode);
18bool gui_wps_display(void);
19bool setvol(void);
20bool update(struct gui_wps *gwps);
21bool ffwd_rew(int button);
22#ifdef WPS_KEYLOCK
23void display_keylock_text(bool locked);
24void waitfor_nokey(void);
25#endif
26#endif
27
diff --git a/apps/gui/gwps.c b/apps/gui/gwps.c
new file mode 100644
index 0000000000..4fc9acbece
--- /dev/null
+++ b/apps/gui/gwps.c
@@ -0,0 +1,853 @@
1#include <stdio.h>
2#include <string.h>
3#include <stdlib.h>
4
5#include "system.h"
6#include "file.h"
7#include "lcd.h"
8#include "font.h"
9#include "backlight.h"
10#include "button.h"
11#include "kernel.h"
12#include "tree.h"
13#include "debug.h"
14#include "sprintf.h"
15#include "settings.h"
16#include "gwps.h"
17#include "gwps-common.h"
18#include "audio.h"
19#include "usb.h"
20#include "status.h"
21#include "main_menu.h"
22#include "ata.h"
23#include "screens.h"
24#include "playlist.h"
25#ifdef HAVE_LCD_BITMAP
26#include "icons.h"
27#include "peakmeter.h"
28#endif
29#include "action.h"
30#include "lang.h"
31#include "bookmark.h"
32#include "misc.h"
33#include "sound.h"
34#include "onplay.h"
35#include "abrepeat.h"
36#include "playback.h"
37
38#include "statusbar.h"
39#include "splash.h"
40
41#define WPS_DEFAULTCFG WPS_DIR "/rockbox_default.wps"
42#define RWPS_DEFAULTCFG WPS_DIR "/rockbox_default.rwps"
43/* currently only on wps_state is needed */
44struct wps_state wps_state;
45struct gui_syncwps gui_syncwps;
46struct wps_data wps_datas[NB_SCREENS];
47
48bool keys_locked = false;
49
50long gui_wps_show()
51{
52 long button = 0, lastbutton = 0;
53 bool ignore_keyup = true;
54 bool restore = false;
55 long restoretimer = 0; /* timer to delay screen redraw temporarily */
56 bool exit = false;
57 bool update_track = false;
58 unsigned long right_lastclick = 0;
59 unsigned long left_lastclick = 0;
60 int i;
61
62 wps_state_init();
63
64#ifdef HAVE_LCD_CHARCELLS
65 status_set_audio(true);
66 status_set_param(false);
67#else
68 FOR_NB_SCREENS(i)
69 {
70 if(global_settings.statusbar)
71 gui_syncwps.gui_wps[i].display->setmargins(0, STATUSBAR_HEIGHT);
72 else
73 gui_syncwps.gui_wps[i].display->setmargins(0, 0);
74 }
75#endif
76
77#ifdef AB_REPEAT_ENABLE
78 ab_repeat_init();
79 ab_reset_markers();
80#endif
81
82 if(audio_status() & AUDIO_STATUS_PLAY)
83 {
84 wps_state.id3 = audio_current_track();
85 wps_state.nid3 = audio_next_track();
86 if (wps_state.id3) {
87 if (gui_wps_display())
88 return 0;
89 FOR_NB_SCREENS(i)
90 gui_wps_refresh(&gui_syncwps.gui_wps[i], 0, WPS_REFRESH_ALL);
91 wps_state_update_ctp(wps_state.id3->path);
92 }
93
94 restore = true;
95 }
96 while ( 1 )
97 {
98 bool audio_paused = (audio_status() & AUDIO_STATUS_PAUSE)?true:false;
99
100 /* did someone else (i.e power thread) change audio pause mode? */
101 if (wps_state.paused != audio_paused) {
102 wps_state.paused = audio_paused;
103
104 /* if another thread paused audio, we are probably in car mode,
105 about to shut down. lets save the settings. */
106 if (wps_state.paused) {
107 settings_save();
108#if !defined(HAVE_RTC) && !defined(HAVE_SW_POWEROFF)
109 ata_flush();
110#endif
111 }
112 }
113
114#ifdef HAVE_LCD_BITMAP
115 /* when the peak meter is enabled we want to have a
116 few extra updates to make it look smooth. On the
117 other hand we don't want to waste energy if it
118 isn't displayed */
119 bool pm=false;
120 FOR_NB_SCREENS(i)
121 {
122 if(gui_syncwps.gui_wps[i].data->peak_meter_enabled)
123 pm = true;
124 }
125
126 if (pm) {
127 long next_refresh = current_tick;
128 long next_big_refresh = current_tick + HZ / 5;
129 button = BUTTON_NONE;
130 while (TIME_BEFORE(current_tick, next_big_refresh)) {
131 button = button_get(false);
132 if (button != BUTTON_NONE) {
133 break;
134 }
135 peak_meter_peek();
136 sleep(0); /* Sleep until end of current tick. */
137
138 if (TIME_AFTER(current_tick, next_refresh)) {
139 FOR_NB_SCREENS(i)
140 gui_wps_refresh(&gui_syncwps.gui_wps[i], 0, WPS_REFRESH_PEAK_METER);
141 next_refresh += HZ / PEAK_METER_FPS;
142 }
143 }
144
145 }
146
147 /* The peak meter is disabled
148 -> no additional screen updates needed */
149 else {
150 button = button_get_w_tmo(HZ/5);
151 }
152#else
153 button = button_get_w_tmo(HZ/5);
154#endif
155
156 /* discard first event if it's a button release */
157 if (button && ignore_keyup)
158 {
159 ignore_keyup = false;
160 /* Negative events are system events */
161 if (button >= 0 && button & BUTTON_REL )
162 continue;
163 }
164
165#ifdef WPS_KEYLOCK
166 /* ignore non-remote buttons when keys are locked */
167 if (keys_locked &&
168 ! ((button < 0) ||
169 (button == BUTTON_NONE) ||
170 ((button & WPS_KEYLOCK) == WPS_KEYLOCK) ||
171 (button & BUTTON_REMOTE)
172 ))
173 {
174 if (!(button & BUTTON_REL))
175 display_keylock_text(true);
176 restore = true;
177 button = BUTTON_NONE;
178 }
179#endif
180
181 /* Exit if audio has stopped playing. This can happen if using the
182 sleep timer with the charger plugged or if starting a recording
183 from F1 */
184 if (!audio_status())
185 exit = true;
186
187 switch(button)
188 {
189#ifdef WPS_CONTEXT
190 case WPS_CONTEXT:
191#ifdef WPS_RC_CONTEXT
192 case WPS_RC_CONTEXT:
193#endif
194 onplay(wps_state.id3->path, TREE_ATTR_MPA, CONTEXT_WPS);
195 restore = true;
196 break;
197#endif
198
199#ifdef WPS_RC_BROWSE
200 case WPS_RC_BROWSE:
201#endif
202 case WPS_BROWSE:
203#ifdef WPS_BROWSE_PRE
204 if ((lastbutton != WPS_BROWSE_PRE)
205#ifdef WPS_RC_BROWSE_PRE
206 && (lastbutton != WPS_RC_BROWSE_PRE)
207#endif
208 )
209 break;
210#endif
211#ifdef HAVE_LCD_CHARCELLS
212 status_set_record(false);
213 status_set_audio(false);
214#endif
215 FOR_NB_SCREENS(i)
216 gui_syncwps.gui_wps[i].display->stop_scroll();
217
218 /* set dir browser to current playing song */
219 if (global_settings.browse_current &&
220 wps_state.current_track_path[0] != '\0')
221 set_current_file(wps_state.current_track_path);
222
223 return 0;
224 break;
225
226 /* play/pause */
227 case WPS_PAUSE:
228#ifdef WPS_PAUSE_PRE
229 if (lastbutton != WPS_PAUSE_PRE)
230 break;
231#endif
232#ifdef WPS_RC_PAUSE
233 case WPS_RC_PAUSE:
234#ifdef WPS_RC_PAUSE_PRE
235 if ((button == WPS_RC_PAUSE) && (lastbutton != WPS_RC_PAUSE_PRE))
236 break;
237#endif
238#endif
239 if ( wps_state.paused )
240 {
241 wps_state.paused = false;
242 if ( global_settings.fade_on_stop )
243 fade(1);
244 else
245 audio_resume();
246 }
247 else
248 {
249 wps_state.paused = true;
250 if ( global_settings.fade_on_stop )
251 fade(0);
252 else
253 audio_pause();
254 settings_save();
255#if !defined(HAVE_RTC) && !defined(HAVE_SW_POWEROFF)
256 ata_flush(); /* make sure resume info is saved */
257#endif
258 }
259 break;
260
261 /* volume up */
262 case WPS_INCVOL:
263 case WPS_INCVOL | BUTTON_REPEAT:
264#ifdef WPS_RC_INCVOL
265 case WPS_RC_INCVOL:
266 case WPS_RC_INCVOL | BUTTON_REPEAT:
267#endif
268 global_settings.volume++;
269 if (setvol()) {
270 restore = true;
271 restoretimer = current_tick + HZ;
272 }
273 break;
274
275 /* volume down */
276 case WPS_DECVOL:
277 case WPS_DECVOL | BUTTON_REPEAT:
278#ifdef WPS_RC_DECVOL
279 case WPS_RC_DECVOL:
280 case WPS_RC_DECVOL | BUTTON_REPEAT:
281#endif
282 global_settings.volume--;
283 if (setvol()) {
284 restore = true;
285 restoretimer = current_tick + HZ;
286 }
287 break;
288
289 /* fast forward / rewind */
290#ifdef WPS_RC_FFWD
291 case WPS_RC_FFWD:
292#endif
293 case WPS_FFWD:
294#ifdef WPS_NEXT_DIR
295 if (current_tick - right_lastclick < HZ)
296 {
297 audio_next_dir();
298 right_lastclick = 0;
299 break;
300 }
301#endif
302#ifdef WPS_RC_REW
303 case WPS_RC_REW:
304#endif
305 case WPS_REW:
306#ifdef WPS_PREV_DIR
307 if (current_tick - left_lastclick < HZ)
308 {
309 audio_prev_dir();
310 left_lastclick = 0;
311 break;
312 }
313#endif
314 ffwd_rew(button);
315 break;
316
317 /* prev / restart */
318 case WPS_PREV:
319#ifdef WPS_PREV_PRE
320 if (lastbutton != WPS_PREV_PRE)
321 break;
322#endif
323#ifdef WPS_RC_PREV
324 case WPS_RC_PREV:
325#ifdef WPS_RC_PREV_PRE
326 if ((button == WPS_RC_PREV) && (lastbutton != WPS_RC_PREV_PRE))
327 break;
328#endif
329#endif
330 left_lastclick = current_tick;
331 update_track = true;
332
333#ifdef AB_REPEAT_ENABLE
334 /* if we're in A/B repeat mode and the current position
335 is past the A marker, jump back to the A marker... */
336 if ( ab_repeat_mode_enabled() && ab_after_A_marker(wps_state.id3->elapsed) )
337 {
338 ab_jump_to_A_marker();
339 break;
340 }
341 /* ...otherwise, do it normally */
342#endif
343
344 if (!wps_state.id3 || (wps_state.id3->elapsed < 3*1000)) {
345 audio_prev();
346 }
347 else {
348 if (!wps_state.paused)
349 audio_pause();
350
351 audio_ff_rewind(0);
352
353 if (!wps_state.paused)
354 audio_resume();
355 }
356 break;
357
358#ifdef WPS_NEXT_DIR
359#ifdef WPS_RC_NEXT_DIR
360 case WPS_RC_NEXT_DIR:
361#endif
362 case WPS_NEXT_DIR:
363 audio_next_dir();
364 break;
365#endif
366#ifdef WPS_PREV_DIR
367#ifdef WPS_RC_PREV_DIR
368 case WPS_RC_PREV_DIR:
369#endif
370 case WPS_PREV_DIR:
371 audio_prev_dir();
372 break;
373#endif
374
375 /* next */
376 case WPS_NEXT:
377#ifdef WPS_NEXT_PRE
378 if (lastbutton != WPS_NEXT_PRE)
379 break;
380#endif
381#ifdef WPS_RC_NEXT
382 case WPS_RC_NEXT:
383#ifdef WPS_RC_NEXT_PRE
384 if ((button == WPS_RC_NEXT) && (lastbutton != WPS_RC_NEXT_PRE))
385 break;
386#endif
387#endif
388 right_lastclick = current_tick;
389 update_track = true;
390
391#ifdef AB_REPEAT_ENABLE
392 /* if we're in A/B repeat mode and the current position is
393 before the A marker, jump to the A marker... */
394 if ( ab_repeat_mode_enabled() && ab_before_A_marker(wps_state.id3->elapsed) )
395 {
396 ab_jump_to_A_marker();
397 break;
398 }
399 /* ...otherwise, do it normally */
400#endif
401
402 audio_next();
403 break;
404
405#ifdef WPS_MENU
406 /* menu key functions */
407 case WPS_MENU:
408#ifdef WPS_MENU_PRE
409 if (lastbutton != WPS_MENU_PRE)
410 break;
411#endif
412#ifdef WPS_RC_MENU
413 case WPS_RC_MENU:
414#ifdef WPS_RC_MENU_PRE
415 if ((button == WPS_RC_MENU) && (lastbutton != WPS_RC_MENU_PRE))
416 break;
417#endif
418#endif
419 FOR_NB_SCREENS(i)
420 gui_syncwps.gui_wps[i].display->stop_scroll();
421
422 if (main_menu())
423 return true;
424#ifdef HAVE_LCD_BITMAP
425 FOR_NB_SCREENS(i)
426 {
427 if(global_settings.statusbar)
428 gui_syncwps.gui_wps[i].display->setmargins(0, STATUSBAR_HEIGHT);
429 else
430 gui_syncwps.gui_wps[i].display->setmargins(0, 0);
431 }
432#endif
433 restore = true;
434 break;
435#endif /* WPS_MENU */
436
437#ifdef WPS_KEYLOCK
438 /* key lock */
439 case WPS_KEYLOCK:
440 case WPS_KEYLOCK | BUTTON_REPEAT:
441 keys_locked = !keys_locked;
442 display_keylock_text(keys_locked);
443 restore = true;
444 waitfor_nokey();
445 break;
446#endif
447
448#if (CONFIG_KEYPAD == RECORDER_PAD) || (CONFIG_KEYPAD == IRIVER_H100_PAD)
449 /* play settings */
450 case WPS_QUICK:
451#ifdef WPS_RC_QUICK
452 case WPS_RC_QUICK:
453#endif
454 if (quick_screen(CONTEXT_WPS, WPS_QUICK))
455 return SYS_USB_CONNECTED;
456 restore = true;
457 lastbutton = 0;
458 break;
459
460 /* screen settings */
461#ifdef BUTTON_F3
462 case BUTTON_F3:
463 if (quick_screen(CONTEXT_WPS, BUTTON_F3))
464 return SYS_USB_CONNECTED;
465 restore = true;
466 break;
467#endif
468
469 /* pitch screen */
470#if CONFIG_KEYPAD == RECORDER_PAD
471 case BUTTON_ON | BUTTON_UP:
472 case BUTTON_ON | BUTTON_DOWN:
473 if (2 == pitch_screen())
474 return SYS_USB_CONNECTED;
475 restore = true;
476 break;
477#endif
478#endif
479
480#ifdef AB_REPEAT_ENABLE
481
482#ifdef WPS_AB_SET_A_MARKER
483 /* set A marker for A-B repeat */
484 case WPS_AB_SET_A_MARKER:
485 if (ab_repeat_mode_enabled())
486 ab_set_A_marker(wps_state.id3->elapsed);
487 break;
488#endif
489
490#ifdef WPS_AB_SET_B_MARKER
491 /* set B marker for A-B repeat and jump to A */
492 case WPS_AB_SET_B_MARKER:
493 if (ab_repeat_mode_enabled())
494 {
495 ab_set_B_marker(wps_state.id3->elapsed);
496 ab_jump_to_A_marker();
497 update_track = true;
498 }
499 break;
500#endif
501
502#ifdef WPS_AB_RESET_AB_MARKERS
503 /* reset A&B markers */
504 case WPS_AB_RESET_AB_MARKERS:
505 if (ab_repeat_mode_enabled())
506 {
507 ab_reset_markers();
508 update_track = true;
509 }
510 break;
511#endif
512
513#endif /* AB_REPEAT_ENABLE */
514
515 /* stop and exit wps */
516#ifdef WPS_EXIT
517 case WPS_EXIT:
518# ifdef WPS_EXIT_PRE
519 if (lastbutton != WPS_EXIT_PRE)
520 break;
521# endif
522 exit = true;
523#ifdef WPS_RC_EXIT
524 case WPS_RC_EXIT:
525#ifdef WPS_RC_EXIT_PRE
526 if (lastbutton != WPS_RC_EXIT_PRE)
527 break;
528#endif
529 exit = true;
530#endif
531 break;
532#endif
533
534#ifdef WPS_ID3
535 case WPS_ID3:
536 browse_id3();
537 restore = true;
538 break;
539#endif
540
541 case BUTTON_NONE: /* Timeout */
542 update_track = true;
543 break;
544
545 default:
546 if(default_event_handler(button) == SYS_USB_CONNECTED)
547 return SYS_USB_CONNECTED;
548 update_track = true;
549 break;
550 }
551
552 if (update_track)
553 {
554 bool upt = false;
555 FOR_NB_SCREENS(i){
556 if(update(&gui_syncwps.gui_wps[i]))
557 upt = true;
558 }
559 if (upt)
560 {
561 /* set dir browser to current playing song */
562 if (global_settings.browse_current &&
563 wps_state.current_track_path[0] != '\0')
564 set_current_file(wps_state.current_track_path);
565
566 return 0;
567 }
568 update_track = false;
569 }
570
571 if (exit) {
572#ifdef HAVE_LCD_CHARCELLS
573 status_set_record(false);
574 status_set_audio(false);
575#endif
576 if (global_settings.fade_on_stop)
577 fade(0);
578
579 FOR_NB_SCREENS(i)
580 gui_syncwps.gui_wps[i].display->stop_scroll();
581 bookmark_autobookmark();
582 audio_stop();
583#ifdef AB_REPEAT_ENABLE
584 ab_reset_markers();
585#endif
586
587 /* Keys can be locked when exiting, so either unlock here
588 or implement key locking in tree.c too */
589 keys_locked=false;
590
591 /* set dir browser to current playing song */
592 if (global_settings.browse_current &&
593 wps_state.current_track_path[0] != '\0')
594 set_current_file(wps_state.current_track_path);
595
596 return 0;
597 }
598
599 if ( button )
600 ata_spin();
601
602 if (restore &&
603 ((restoretimer == 0) ||
604 (restoretimer < current_tick)))
605 {
606 restore = false;
607 restoretimer = 0;
608 if (gui_wps_display())
609 {
610 /* set dir browser to current playing song */
611 if (global_settings.browse_current &&
612 wps_state.current_track_path[0] != '\0')
613 set_current_file(wps_state.current_track_path);
614
615 return 0;
616 }
617
618 if (wps_state.id3){
619 FOR_NB_SCREENS(i)
620 gui_wps_refresh(&gui_syncwps.gui_wps[i], 0, WPS_REFRESH_NON_STATIC);
621 }
622 }
623 if (button != BUTTON_NONE)
624 lastbutton = button;
625 }
626 return 0; /* unreachable - just to reduce compiler warnings */
627}
628
629/* needs checking if needed end*/
630
631/* wps_data*/
632/* initial setup of wps_data */
633void wps_data_init(struct wps_data *wps_data)
634{
635 int i;
636#ifdef HAVE_LCD_BITMAP
637 for (i = 0; i < MAX_IMAGES; i++) {
638 wps_data->img[i].loaded = false;
639 wps_data->img[i].display = false;
640 wps_data->img[i].always_display = false;
641 }
642#else /* HAVE_LCD_CHARCELLS */
643 for(i = 0; i < 8; i++)
644 wps_data->wps_progress_pat[i] = 0;
645 wps_data->full_line_progressbar = 0;
646#endif
647 wps_data->format_buffer[0] = '\0';
648 wps_data->wps_loaded = false;
649 wps_data->peak_meter_enabled = false;
650}
651
652#ifdef HAVE_LCD_BITMAP
653/* Clear the WPS image cache */
654static void wps_clear(struct wps_data *data )
655{
656 int i;
657 /* set images to unloaded and not displayed */
658 for (i = 0; i < MAX_IMAGES; i++) {
659 data->img[i].loaded = false;
660 data->img[i].display = false;
661 data->img[i].always_display = false;
662 }
663}
664#else
665#define wps_clear(a)
666#endif
667
668static void wps_reset(struct wps_data *data)
669{
670 data->wps_loaded = false;
671 memset(&data->format_buffer, 0, sizeof data->format_buffer);
672 wps_clear(data);
673}
674
675/* to setup up the wps-data from a format-buffer (isfile = false)
676 from a (wps-)file (isfile = true)*/
677bool wps_data_load(struct wps_data *wps_data, const char *buf, bool isfile, bool display)
678{
679 int i, s;
680 int fd;
681 if(!wps_data || !buf) return false;
682 if(!isfile)
683 {
684 wps_clear(wps_data);
685 strncpy(wps_data->format_buffer, buf, sizeof(wps_data->format_buffer));
686 wps_data->format_buffer[sizeof(wps_data->format_buffer) - 1] = 0;
687 gui_wps_format(wps_data, NULL, 0);
688 return true;
689 }
690 else
691 {
692 /*
693 * Hardcode loading WPS_DEFAULTCFG to cause a reset ideally this
694 * wants to be a virtual file. Feel free to modify dirbrowse()
695 * if you're feeling brave.
696 */
697 if (! strcmp(buf, WPS_DEFAULTCFG) || !strcmp(buf, RWPS_DEFAULTCFG) ) {
698 wps_reset(wps_data);
699 return false;
700 }
701 size_t bmpdirlen;
702 char *bmpdir = strrchr(buf, '.');
703 bmpdirlen = bmpdir - buf;
704
705 fd = open(buf, O_RDONLY);
706
707 if (fd >= 0)
708 {
709 int numread = read(fd, wps_data->format_buffer, sizeof(wps_data->format_buffer) - 1);
710
711 if (numread > 0)
712 {
713#ifdef HAVE_LCD_BITMAP
714 wps_clear(wps_data);
715#endif
716 wps_data->format_buffer[numread] = 0;
717 gui_wps_format(wps_data, buf, bmpdirlen);
718 }
719
720 close(fd);
721
722 if ( display ) {
723 bool any_defined_line;
724 int z;
725 FOR_NB_SCREENS(z)
726 screens[z].clear_display();
727#ifdef HAVE_LCD_BITMAP
728 FOR_NB_SCREENS(z)
729 screens[z].setmargins(0,0);
730#endif
731 for (s=0; s<WPS_MAX_SUBLINES; s++)
732 {
733 any_defined_line = false;
734 for (i=0; i<WPS_MAX_LINES; i++)
735 {
736 if (wps_data->format_lines[i][s])
737 {
738 if (*(wps_data->format_lines[i][s]) == 0)
739 {
740 FOR_NB_SCREENS(z)
741 screens[z].puts(0,i," ");
742 }
743 else
744 {
745 FOR_NB_SCREENS(z)
746 screens[z].puts(0,i,wps_data->format_lines[i][s]);
747 }
748 any_defined_line = true;
749 }
750 else
751 {
752 FOR_NB_SCREENS(z)
753 screens[z].puts(0,i," ");
754 }
755 }
756 if (any_defined_line)
757 {
758#ifdef HAVE_LCD_BITMAP
759 FOR_NB_SCREENS(z)
760 screens[z].update();
761#endif
762 sleep(HZ/2);
763 }
764 }
765 }
766 wps_data->wps_loaded = true;
767
768 return numread > 0;
769 }
770 }
771
772 return false;
773}
774
775/* wps_data end */
776
777/* wps_state */
778struct wps_state wps_state;
779
780void wps_state_init(void)
781{
782 wps_state.ff_rewind = false;
783 wps_state.paused = false;
784 wps_state.id3 = NULL;
785 wps_state.nid3 = NULL;
786 wps_state.current_track_path[0] = '\0';
787}
788void wps_state_update_ff_rew(bool ff_rew)
789{
790 wps_state.ff_rewind = ff_rew;
791}
792void wps_state_update_paused(bool paused)
793{
794 wps_state.paused = paused;
795}
796void wps_state_update_ctp(const char *path)
797{
798 memcpy(wps_state.current_track_path, path, sizeof(wps_state.current_track_path));
799}
800void wps_state_update_id3_nid3(struct mp3entry *id3, struct mp3entry *nid3)
801{
802 wps_state.id3 = id3;
803 wps_state.nid3 = nid3;
804}
805/* wps_state end*/
806
807/* initial setup of a wps */
808void gui_wps_init(struct gui_wps *gui_wps)
809{
810 gui_wps->data = NULL;
811 gui_wps->display = NULL;
812 /* Currently no seperate wps_state needed/possible
813 so use the only aviable ( "global" ) one */
814 gui_wps->state = &wps_state;
815}
816
817/* connects a wps with a format-description of the displayed content */
818void gui_wps_set_data(struct gui_wps *gui_wps, struct wps_data *data)
819{
820 gui_wps->data = data;
821}
822
823/* connects a wps with a screen */
824void gui_wps_set_disp(struct gui_wps *gui_wps, struct screen *display)
825{
826 gui_wps->display = display;
827}
828/* gui_wps end */
829
830void gui_sync_data_wps_init(void)
831{
832 int i;
833 FOR_NB_SCREENS(i)
834 wps_data_init(&wps_datas[i]);
835}
836
837void gui_sync_wps_screen_init(void)
838{
839 int i;
840 FOR_NB_SCREENS(i)
841 gui_wps_set_disp(&gui_syncwps.gui_wps[i], &screens[i]);
842}
843
844void gui_sync_wps_init(void)
845{
846 int i;
847 FOR_NB_SCREENS(i)
848 {
849 gui_wps_init(&gui_syncwps.gui_wps[i]);
850 gui_wps_set_data(&gui_syncwps.gui_wps[i], &wps_datas[i]);
851 }
852}
853
diff --git a/apps/gui/gwps.h b/apps/gui/gwps.h
new file mode 100644
index 0000000000..b5fddd556f
--- /dev/null
+++ b/apps/gui/gwps.h
@@ -0,0 +1,351 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2002 Jerome Kuptz
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#ifndef _WPS_H
20#define _WPS_H
21
22#include "screen_access.h"
23#include "id3.h"
24#include "playlist.h"
25
26
27/* button definitions */
28#if (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
29 (CONFIG_KEYPAD == IRIVER_H300_PAD)
30#define WPS_NEXT (BUTTON_RIGHT | BUTTON_REL)
31#define WPS_NEXT_PRE BUTTON_RIGHT
32#define WPS_PREV (BUTTON_LEFT | BUTTON_REL)
33#define WPS_PREV_PRE BUTTON_LEFT
34#define WPS_FFWD (BUTTON_RIGHT | BUTTON_REPEAT)
35#define WPS_REW (BUTTON_LEFT | BUTTON_REPEAT)
36#define WPS_INCVOL BUTTON_UP
37#define WPS_DECVOL BUTTON_DOWN
38#define WPS_PAUSE (BUTTON_ON | BUTTON_REL)
39#define WPS_PAUSE_PRE BUTTON_ON
40#define WPS_MENU (BUTTON_MODE | BUTTON_REL)
41#define WPS_MENU_PRE BUTTON_MODE
42#define WPS_BROWSE (BUTTON_SELECT | BUTTON_REL)
43#define WPS_BROWSE_PRE BUTTON_SELECT
44#define WPS_EXIT (BUTTON_OFF | BUTTON_REL)
45#define WPS_EXIT_PRE BUTTON_OFF
46#define WPS_ID3 (BUTTON_MODE | BUTTON_ON)
47#define WPS_CONTEXT (BUTTON_SELECT | BUTTON_REPEAT)
48#define WPS_QUICK (BUTTON_MODE | BUTTON_REPEAT)
49#define WPS_NEXT_DIR (BUTTON_RIGHT | BUTTON_ON)
50#define WPS_PREV_DIR (BUTTON_LEFT | BUTTON_ON)
51
52#define WPS_RC_NEXT_DIR (BUTTON_RC_BITRATE | BUTTON_REL)
53#define WPS_RC_PREV_DIR (BUTTON_RC_SOURCE | BUTTON_REL)
54#define WPS_RC_NEXT (BUTTON_RC_FF | BUTTON_REL)
55#define WPS_RC_NEXT_PRE BUTTON_RC_FF
56#define WPS_RC_PREV (BUTTON_RC_REW | BUTTON_REL)
57#define WPS_RC_PREV_PRE BUTTON_RC_REW
58#define WPS_RC_FFWD (BUTTON_RC_FF | BUTTON_REPEAT)
59#define WPS_RC_REW (BUTTON_RC_REW | BUTTON_REPEAT)
60#define WPS_RC_PAUSE BUTTON_RC_ON
61#define WPS_RC_INCVOL BUTTON_RC_VOL_UP
62#define WPS_RC_DECVOL BUTTON_RC_VOL_DOWN
63#define WPS_RC_EXIT (BUTTON_RC_STOP | BUTTON_REL)
64#define WPS_RC_EXIT_PRE BUTTON_RC_STOP
65#define WPS_RC_MENU (BUTTON_RC_MODE | BUTTON_REL)
66#define WPS_RC_MENU_PRE BUTTON_RC_MODE
67#define WPS_RC_BROWSE (BUTTON_RC_MENU | BUTTON_REL)
68#define WPS_RC_BROWSE_PRE BUTTON_RC_MENU
69#define WPS_RC_CONTEXT (BUTTON_RC_MENU | BUTTON_REPEAT)
70
71#elif CONFIG_KEYPAD == RECORDER_PAD
72#define WPS_NEXT (BUTTON_RIGHT | BUTTON_REL)
73#define WPS_NEXT_PRE BUTTON_RIGHT
74#define WPS_PREV (BUTTON_LEFT | BUTTON_REL)
75#define WPS_PREV_PRE BUTTON_LEFT
76#define WPS_FFWD (BUTTON_RIGHT | BUTTON_REPEAT)
77#define WPS_REW (BUTTON_LEFT | BUTTON_REPEAT)
78#define WPS_INCVOL BUTTON_UP
79#define WPS_DECVOL BUTTON_DOWN
80#define WPS_PAUSE_PRE BUTTON_PLAY
81#define WPS_PAUSE (BUTTON_PLAY | BUTTON_REL)
82#define WPS_MENU (BUTTON_F1 | BUTTON_REL)
83#define WPS_MENU_PRE BUTTON_F1
84#define WPS_BROWSE (BUTTON_ON | BUTTON_REL)
85#define WPS_BROWSE_PRE BUTTON_ON
86#define WPS_EXIT BUTTON_OFF
87#define WPS_KEYLOCK (BUTTON_F1 | BUTTON_DOWN)
88#define WPS_ID3 (BUTTON_F1 | BUTTON_ON)
89#define WPS_CONTEXT (BUTTON_PLAY | BUTTON_REPEAT)
90#define WPS_QUICK BUTTON_F2
91
92#ifdef AB_REPEAT_ENABLE
93#define WPS_AB_SET_A_MARKER (BUTTON_ON | BUTTON_LEFT)
94#define WPS_AB_SET_B_MARKER (BUTTON_ON | BUTTON_RIGHT)
95#define WPS_AB_RESET_AB_MARKERS (BUTTON_ON | BUTTON_OFF)
96#endif
97
98#define WPS_RC_NEXT BUTTON_RC_RIGHT
99#define WPS_RC_PREV BUTTON_RC_LEFT
100#define WPS_RC_PAUSE BUTTON_RC_PLAY
101#define WPS_RC_INCVOL BUTTON_RC_VOL_UP
102#define WPS_RC_DECVOL BUTTON_RC_VOL_DOWN
103#define WPS_RC_EXIT BUTTON_RC_STOP
104
105#elif CONFIG_KEYPAD == PLAYER_PAD
106#define WPS_NEXT (BUTTON_RIGHT | BUTTON_REL)
107#define WPS_NEXT_PRE BUTTON_RIGHT
108#define WPS_PREV (BUTTON_LEFT | BUTTON_REL)
109#define WPS_PREV_PRE BUTTON_LEFT
110#define WPS_FFWD (BUTTON_RIGHT | BUTTON_REPEAT)
111#define WPS_REW (BUTTON_LEFT | BUTTON_REPEAT)
112#define WPS_INCVOL (BUTTON_MENU | BUTTON_RIGHT)
113#define WPS_DECVOL (BUTTON_MENU | BUTTON_LEFT)
114#define WPS_PAUSE_PRE BUTTON_PLAY
115#define WPS_PAUSE (BUTTON_PLAY | BUTTON_REL)
116#define WPS_MENU (BUTTON_MENU | BUTTON_REL)
117#define WPS_MENU_PRE BUTTON_MENU
118#define WPS_BROWSE (BUTTON_ON | BUTTON_REL)
119#define WPS_BROWSE_PRE BUTTON_ON
120#define WPS_EXIT BUTTON_STOP
121#define WPS_KEYLOCK (BUTTON_MENU | BUTTON_STOP)
122#define WPS_ID3 (BUTTON_MENU | BUTTON_ON)
123#define WPS_CONTEXT (BUTTON_PLAY | BUTTON_REPEAT)
124
125#ifdef AB_REPEAT_ENABLE
126#define WPS_AB_SET_A_MARKER (BUTTON_ON | BUTTON_LEFT)
127#define WPS_AB_SET_B_MARKER (BUTTON_ON | BUTTON_RIGHT)
128#define WPS_AB_RESET_AB_MARKERS (BUTTON_ON | BUTTON_STOP)
129#endif
130
131#define WPS_RC_NEXT BUTTON_RC_RIGHT
132#define WPS_RC_PREV BUTTON_RC_LEFT
133#define WPS_RC_PAUSE BUTTON_RC_PLAY
134#define WPS_RC_INCVOL BUTTON_RC_VOL_UP
135#define WPS_RC_DECVOL BUTTON_RC_VOL_DOWN
136#define WPS_RC_EXIT BUTTON_RC_STOP
137
138#elif CONFIG_KEYPAD == ONDIO_PAD
139#define WPS_NEXT (BUTTON_RIGHT | BUTTON_REL)
140#define WPS_NEXT_PRE BUTTON_RIGHT
141#define WPS_PREV (BUTTON_LEFT | BUTTON_REL)
142#define WPS_PREV_PRE BUTTON_LEFT
143#define WPS_FFWD (BUTTON_RIGHT | BUTTON_REPEAT)
144#define WPS_REW (BUTTON_LEFT | BUTTON_REPEAT)
145#define WPS_INCVOL BUTTON_UP
146#define WPS_DECVOL BUTTON_DOWN
147#define WPS_PAUSE BUTTON_OFF
148/* #define WPS_MENU Ondio can't have both main menu and context menu in wps */
149#define WPS_BROWSE (BUTTON_MENU | BUTTON_REL)
150#define WPS_BROWSE_PRE BUTTON_MENU
151#define WPS_KEYLOCK (BUTTON_MENU | BUTTON_DOWN)
152#define WPS_EXIT (BUTTON_OFF | BUTTON_REPEAT)
153#define WPS_CONTEXT (BUTTON_MENU | BUTTON_REPEAT)
154
155#elif CONFIG_KEYPAD == GMINI100_PAD
156#define WPS_NEXT (BUTTON_RIGHT | BUTTON_REL)
157#define WPS_NEXT_PRE BUTTON_RIGHT
158#define WPS_PREV (BUTTON_LEFT | BUTTON_REL)
159#define WPS_PREV_PRE BUTTON_LEFT
160#define WPS_FFWD (BUTTON_RIGHT | BUTTON_REPEAT)
161#define WPS_REW (BUTTON_LEFT | BUTTON_REPEAT)
162#define WPS_INCVOL BUTTON_UP
163#define WPS_DECVOL BUTTON_DOWN
164#define WPS_PAUSE BUTTON_PLAY
165#define WPS_MENU (BUTTON_MENU | BUTTON_REL)
166#define WPS_MENU_PRE BUTTON_MENU
167#define WPS_BROWSE (BUTTON_ON | BUTTON_REL)
168#define WPS_BROWSE_PRE BUTTON_ON
169#define WPS_EXIT BUTTON_OFF
170#define WPS_KEYLOCK (BUTTON_MENU | BUTTON_DOWN)
171#define WPS_ID3 (BUTTON_MENU | BUTTON_ON)
172
173#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_NANO_PAD)
174
175/* TODO: Check WPS button assignments */
176
177#define WPS_NEXT (BUTTON_RIGHT | BUTTON_REL)
178#define WPS_NEXT_PRE BUTTON_RIGHT
179#define WPS_PREV (BUTTON_LEFT | BUTTON_REL)
180#define WPS_PREV_PRE BUTTON_LEFT
181#define WPS_FFWD (BUTTON_RIGHT | BUTTON_REPEAT)
182#define WPS_REW (BUTTON_LEFT | BUTTON_REPEAT)
183#define WPS_INCVOL BUTTON_UP
184#define WPS_DECVOL BUTTON_DOWN
185#define WPS_PAUSE BUTTON_OFF
186/* #define WPS_MENU iPod can't have both main menu and context menu in wps */
187#define WPS_BROWSE (BUTTON_MENU | BUTTON_REL)
188#define WPS_BROWSE_PRE BUTTON_MENU
189#define WPS_KEYLOCK (BUTTON_MENU | BUTTON_DOWN)
190#define WPS_EXIT (BUTTON_OFF | BUTTON_REPEAT)
191#define WPS_CONTEXT (BUTTON_MENU | BUTTON_REPEAT)
192
193#endif
194
195/* constants used in line_type and as refresh_mode for wps_refresh */
196#define WPS_REFRESH_STATIC 1 /* line doesn't change over time */
197#define WPS_REFRESH_DYNAMIC 2 /* line may change (e.g. time flag) */
198#define WPS_REFRESH_SCROLL 4 /* line scrolls */
199#define WPS_REFRESH_PLAYER_PROGRESS 8 /* line contains a progress bar */
200#define WPS_REFRESH_PEAK_METER 16 /* line contains a peak meter */
201#define WPS_REFRESH_ALL 0xff /* to refresh all line types */
202/* to refresh only those lines that change over time */
203#define WPS_REFRESH_NON_STATIC (WPS_REFRESH_ALL & ~WPS_REFRESH_STATIC & ~WPS_REFRESH_SCROLL)
204
205/* alignments */
206#define WPS_ALIGN_RIGHT 32
207#define WPS_ALIGN_CENTER 64
208#define WPS_ALIGN_LEFT 128
209
210
211extern bool keys_locked;
212/* wps_data*/
213
214#ifdef HAVE_LCD_BITMAP
215struct gui_img{
216 unsigned char* ptr; /* pointer */
217 int x; /* x-pos */
218 int y; /* y-pos */
219 int w; /* width */
220 int h; /* height */
221 bool loaded; /* load state */
222 bool display; /* is to be displayed */
223 bool always_display; /* not using the preload/display mechanism */
224};
225#endif
226
227struct align_pos {
228 char* left;
229 char* center;
230 char* right;
231};
232
233#ifdef HAVE_LCD_BITMAP
234#define MAX_IMAGES (26*2) /* a-z and A-Z */
235#define IMG_BUFSIZE (LCD_HEIGHT * LCD_WIDTH * MAX_IMAGES/25) / 8
236#define WPS_MAX_LINES (LCD_HEIGHT/5+1)
237#define FORMAT_BUFFER_SIZE 3072
238#else
239#define WPS_MAX_LINES 2
240#define FORMAT_BUFFER_SIZE 400
241#endif
242#define WPS_MAX_SUBLINES 12
243#define DEFAULT_SUBLINE_TIME_MULTIPLIER 20 /* (10ths of sec) */
244#define BASE_SUBLINE_TIME 10 /* base time that multiplier is applied to
245 (1/HZ sec, or 100ths of sec) */
246#define SUBLINE_RESET -1
247
248/* wps_data
249 this struct old all necessary data which describes the
250 viewable content of a wps */
251struct wps_data
252{
253#ifdef HAVE_LCD_BITMAP
254 struct gui_img img[MAX_IMAGES];
255 unsigned char img_buf[IMG_BUFSIZE];
256#endif
257#ifdef HAVE_LCD_CHARCELLS
258 unsigned char wps_progress_pat[8];
259 bool full_line_progressbar;
260#endif
261 char format_buffer[FORMAT_BUFFER_SIZE];
262 char* format_lines[WPS_MAX_LINES][WPS_MAX_SUBLINES];
263 struct align_pos format_align[WPS_MAX_LINES][WPS_MAX_SUBLINES];
264 unsigned char line_type[WPS_MAX_LINES][WPS_MAX_SUBLINES];
265 unsigned short time_mult[WPS_MAX_LINES][WPS_MAX_SUBLINES];
266 long subline_expire_time[WPS_MAX_LINES];
267 int curr_subline[WPS_MAX_LINES];
268 bool wps_loaded;
269 bool peak_meter_enabled;
270};
271
272/* initial setup of wps_data */
273void wps_data_init(struct wps_data *wps_data);
274
275/* to setup up the wps-data from a format-buffer (isfile = false)
276 from a (wps-)file (isfile = true)*/
277bool wps_data_load(struct wps_data *wps_data, const char *buf, bool isfile, bool display);
278
279/* wps_data end */
280
281/* wps_state
282 holds the data which belongs to the current played track,
283 the track which will be played afterwards, current path to the track
284 and some status infos */
285struct wps_state
286{
287 bool ff_rewind;
288 bool paused;
289 int ff_rewind_count;
290 bool wps_time_countup;
291 struct mp3entry* id3;
292 struct mp3entry* nid3;
293 char current_track_path[MAX_PATH+1];
294};
295
296/* initial setup of wps_data */
297void wps_state_init(void);
298
299/* change the ff/rew-status
300 if ff_rew = true then we are in skipping mode
301 else we are in normal mode */
302void wps_state_update_ff_rew(bool ff_rew);
303
304/* change the paused-status
305 to indicate if playback is currently paused or not */
306void wps_state_update_paused(bool paused);
307
308/* change the path to the current played track */
309void wps_state_update_ctp(const char *path);
310
311/* change the tag-information of the current played track
312 and the following track */
313void wps_state_update_id3_nid3(struct mp3entry *id3, struct mp3entry *nid3);
314/* wps_state end*/
315
316/* gui_wps
317 defines a wps with it's data, state,
318 and the screen on which the wps-content should be drawn */
319struct gui_wps
320{
321 struct screen * display;
322 struct wps_data *data;
323 struct wps_state *state;
324};
325
326/* initial setup of a wps */
327void gui_wps_init(struct gui_wps *gui_wps);
328
329/* connects a wps with a format-description of the displayed content */
330void gui_wps_set_data(struct gui_wps *gui_wps, struct wps_data *data);
331
332/* connects a wps with a screen */
333void gui_wps_set_disp(struct gui_wps *gui_wps, struct screen *display);
334/* gui_wps end */
335
336struct gui_syncwps
337{
338 struct gui_wps gui_wps[NB_SCREENS];
339};
340long gui_wps_show(void);
341
342/* currently only on wps_state is needed */
343extern struct wps_state wps_state;
344extern struct gui_syncwps gui_syncwps;
345extern struct wps_data wps_datas[NB_SCREENS];
346
347void gui_sync_wps_init(void);
348void gui_sync_data_wps_init(void);
349void gui_sync_wps_screen_init(void);
350
351#endif