Update Beast installation instructions to use beastpatcher and remove instructions...
[kugel-rb.git] / apps / gui / gwps.c
blob7ba9862895bce3e68184357bc589ee27bdde46fd
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 Jerome Kuptz
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include <stdio.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include "config.h"
26 #include "system.h"
27 #include "file.h"
28 #include "lcd.h"
29 #include "font.h"
30 #include "backlight.h"
31 #include "action.h"
32 #include "kernel.h"
33 #include "filetypes.h"
34 #include "debug.h"
35 #include "sprintf.h"
36 #include "settings.h"
37 #include "gwps.h"
38 #include "gwps-common.h"
39 #include "audio.h"
40 #include "usb.h"
41 #include "status.h"
42 #include "storage.h"
43 #include "screens.h"
44 #include "playlist.h"
45 #ifdef HAVE_LCD_BITMAP
46 #include "icons.h"
47 #include "peakmeter.h"
48 #endif
49 #include "action.h"
50 #include "lang.h"
51 #include "bookmark.h"
52 #include "misc.h"
53 #include "sound.h"
54 #include "onplay.h"
55 #include "abrepeat.h"
56 #include "playback.h"
57 #include "splash.h"
58 #include "cuesheet.h"
59 #include "ata_idle_notify.h"
60 #include "root_menu.h"
61 #include "backdrop.h"
62 #include "quickscreen.h"
63 #include "pitchscreen.h"
64 #include "appevents.h"
65 #include "viewport.h"
67 /* currently only one wps_state is needed */
68 struct wps_state wps_state;
69 struct gui_wps gui_wps[NB_SCREENS];
70 static struct wps_data wps_datas[NB_SCREENS];
72 /* initial setup of wps_data */
73 static void wps_state_init(void);
75 static void prev_track(unsigned skip_thresh)
77 if (!wps_state.id3 || (wps_state.id3->elapsed < skip_thresh*1000)) {
78 audio_prev();
80 else {
81 if (cuesheet_is_enabled() && wps_state.id3->cuesheet_type)
83 curr_cuesheet_skip(-1, wps_state.id3->elapsed);
84 return;
87 if (!wps_state.paused)
88 #if (CONFIG_CODEC == SWCODEC)
89 audio_pre_ff_rewind();
90 #else
91 audio_pause();
92 #endif
94 audio_ff_rewind(0);
96 #if (CONFIG_CODEC != SWCODEC)
97 if (!wps_state.paused)
98 audio_resume();
99 #endif
103 static void next_track(void)
105 /* take care of if we're playing a cuesheet */
106 if (cuesheet_is_enabled() && wps_state.id3->cuesheet_type)
108 if (curr_cuesheet_skip(1, wps_state.id3->elapsed))
110 /* if the result was false, then we really want
111 to skip to the next track */
112 return;
116 audio_next();
118 static int fix_wps_bars(void)
120 #ifdef HAVE_LCD_BITMAP
121 int i;
122 int wpsbars = VP_SB_HIDE_ALL;
123 FOR_NB_SCREENS(i)
125 bool draw = global_settings.statusbar;
126 if (gui_wps[i].data->wps_sb_tag)
127 draw = gui_wps[i].data->show_sb_on_wps;
128 if (draw)
129 wpsbars |= (VP_SB_ONSCREEN(i) | VP_SB_IGNORE_SETTING(i));
131 return wpsbars;
132 #else
133 return VP_SB_ALLSCREENS;
134 #endif
137 long gui_wps_show(void)
139 long button = 0;
140 bool restore = false;
141 long restoretimer = 0; /* timer to delay screen redraw temporarily */
142 bool exit = false;
143 bool bookmark = false;
144 bool update_track = false;
145 int i;
146 long last_left = 0, last_right = 0;
147 int wpsbars, oldbars;
149 wps_state_init();
151 #ifdef HAVE_LCD_CHARCELLS
152 status_set_audio(true);
153 status_set_param(false);
154 #else
155 #if LCD_DEPTH > 1
156 show_wps_backdrop();
157 #endif /* LCD_DEPTH > 1 */
158 #endif
160 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
161 show_remote_wps_backdrop();
162 #endif
164 #ifdef AB_REPEAT_ENABLE
165 ab_repeat_init();
166 ab_reset_markers();
167 #endif
169 oldbars = viewportmanager_set_statusbar(VP_SB_HIDE_ALL);
170 if(audio_status() & AUDIO_STATUS_PLAY)
172 wps_state.id3 = audio_current_track();
173 wps_state.nid3 = audio_next_track();
174 if (wps_state.id3) {
175 if (gui_wps_display())
176 return 0;
179 restore = true;
181 wpsbars = fix_wps_bars();
182 viewportmanager_set_statusbar(wpsbars);
184 while ( 1 )
186 bool audio_paused = (audio_status() & AUDIO_STATUS_PAUSE)?true:false;
188 /* did someone else (i.e power thread) change audio pause mode? */
189 if (wps_state.paused != audio_paused) {
190 wps_state.paused = audio_paused;
192 /* if another thread paused audio, we are probably in car mode,
193 about to shut down. lets save the settings. */
194 if (wps_state.paused) {
195 settings_save();
196 #if !defined(HAVE_RTC_RAM) && !defined(HAVE_SW_POWEROFF)
197 call_storage_idle_notifys(true);
198 #endif
202 #ifdef HAVE_LCD_BITMAP
203 /* when the peak meter is enabled we want to have a
204 few extra updates to make it look smooth. On the
205 other hand we don't want to waste energy if it
206 isn't displayed */
207 bool pm=false;
208 FOR_NB_SCREENS(i)
210 if(gui_wps[i].data->peak_meter_enabled)
211 pm = true;
214 if (pm) {
215 long next_refresh = current_tick;
216 long next_big_refresh = current_tick + HZ / 5;
217 button = BUTTON_NONE;
218 while (TIME_BEFORE(current_tick, next_big_refresh)) {
219 button = get_action(CONTEXT_WPS|ALLOW_SOFTLOCK,TIMEOUT_NOBLOCK);
220 if (button != ACTION_NONE) {
221 break;
223 peak_meter_peek();
224 sleep(0); /* Sleep until end of current tick. */
226 if (TIME_AFTER(current_tick, next_refresh)) {
227 FOR_NB_SCREENS(i)
229 if(gui_wps[i].data->peak_meter_enabled)
230 gui_wps_refresh(&gui_wps[i], 0,
231 WPS_REFRESH_PEAK_METER);
232 next_refresh += HZ / PEAK_METER_FPS;
239 /* The peak meter is disabled
240 -> no additional screen updates needed */
241 else {
242 button = get_action(CONTEXT_WPS|ALLOW_SOFTLOCK,HZ/5);
244 #else
245 button = get_action(CONTEXT_WPS|ALLOW_SOFTLOCK,HZ/5);
246 #endif
248 /* Exit if audio has stopped playing. This can happen if using the
249 sleep timer with the charger plugged or if starting a recording
250 from F1 */
251 if (!audio_status())
252 exit = true;
253 /* The iPods/X5/M5 use a single button for the A-B mode markers,
254 defined as ACTION_WPSAB_SINGLE in their config files. */
255 #ifdef ACTION_WPSAB_SINGLE
256 if (!global_settings.party_mode && ab_repeat_mode_enabled())
258 static int wps_ab_state = 0;
259 if (button == ACTION_WPSAB_SINGLE)
261 switch (wps_ab_state)
263 case 0: /* set the A spot */
264 button = ACTION_WPS_ABSETA_PREVDIR;
265 break;
266 case 1: /* set the B spot */
267 button = ACTION_WPS_ABSETB_NEXTDIR;
268 break;
269 case 2:
270 button = ACTION_WPS_ABRESET;
271 break;
273 wps_ab_state = (wps_ab_state+1) % 3;
276 #endif
277 switch(button)
279 case ACTION_WPS_CONTEXT:
281 #if LCD_DEPTH > 1
282 show_main_backdrop();
283 #endif
284 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
285 show_remote_main_backdrop();
286 #endif
287 viewportmanager_set_statusbar(oldbars);
288 /* if music is stopped in the context menu we want to exit the wps */
289 if (onplay(wps_state.id3->path,
290 FILE_ATTR_AUDIO, CONTEXT_WPS) == ONPLAY_MAINMENU
291 || !audio_status())
292 return GO_TO_ROOT;
293 viewportmanager_set_statusbar(wpsbars);
294 /* track might have changed */
295 update_track = true;
297 #if LCD_DEPTH > 1
298 show_wps_backdrop();
299 #endif
300 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
301 show_remote_wps_backdrop();
302 #endif
303 restore = true;
305 break;
307 case ACTION_WPS_BROWSE:
308 #ifdef HAVE_LCD_CHARCELLS
309 status_set_record(false);
310 status_set_audio(false);
311 #endif
312 FOR_NB_SCREENS(i)
313 gui_wps[i].display->stop_scroll();
314 return GO_TO_PREVIOUS_BROWSER;
315 break;
317 /* play/pause */
318 case ACTION_WPS_PLAY:
319 if (global_settings.party_mode)
320 break;
321 if ( wps_state.paused )
323 wps_state.paused = false;
324 if ( global_settings.fade_on_stop )
325 fade(true, true);
326 else
327 audio_resume();
329 else
331 wps_state.paused = true;
332 if ( global_settings.fade_on_stop )
333 fade(false, true);
334 else
335 audio_pause();
336 settings_save();
337 #if !defined(HAVE_RTC_RAM) && !defined(HAVE_SW_POWEROFF)
338 call_storage_idle_notifys(true); /* make sure resume info is saved */
339 #endif
341 break;
343 /* volume up */
344 case ACTION_WPS_VOLUP:
346 FOR_NB_SCREENS(i)
347 gui_wps[i].data->button_time_volume = current_tick;
348 global_settings.volume++;
349 bool res = false;
350 setvol();
351 FOR_NB_SCREENS(i)
353 if(update_onvol_change(&gui_wps[i]))
354 res = true;
356 if (res) {
357 restore = true;
358 restoretimer = current_tick + HZ;
361 break;
363 /* volume down */
364 case ACTION_WPS_VOLDOWN:
366 FOR_NB_SCREENS(i)
367 gui_wps[i].data->button_time_volume = current_tick;
368 global_settings.volume--;
369 setvol();
370 bool res = false;
371 FOR_NB_SCREENS(i)
373 if(update_onvol_change(&gui_wps[i]))
374 res = true;
376 if (res) {
377 restore = true;
378 restoretimer = current_tick + HZ;
381 break;
382 /* fast forward
383 OR next dir if this is straight after ACTION_WPS_SKIPNEXT
384 OR if skip length set, next track if straight after SKIPPREV. */
385 case ACTION_WPS_SEEKFWD:
386 if (global_settings.party_mode)
387 break;
388 if (global_settings.skip_length == 0
389 && current_tick -last_right < HZ)
391 if (cuesheet_is_enabled() && wps_state.id3->cuesheet_type)
393 audio_next();
395 else
397 audio_next_dir();
400 else if (global_settings.skip_length > 0
401 && current_tick -last_left < HZ) {
402 next_track();
403 update_track = true;
405 else ffwd_rew(ACTION_WPS_SEEKFWD);
406 last_right = last_left = 0;
407 break;
408 /* fast rewind
409 OR prev dir if this is straight after ACTION_WPS_SKIPPREV,
410 OR if skip length set, beg of track or prev track if this is
411 straight after SKIPPREV */
412 case ACTION_WPS_SEEKBACK:
413 if (global_settings.party_mode)
414 break;
415 if (global_settings.skip_length == 0
416 && current_tick -last_left < HZ)
418 if (cuesheet_is_enabled() && wps_state.id3->cuesheet_type)
420 if (!wps_state.paused)
421 #if (CONFIG_CODEC == SWCODEC)
422 audio_pre_ff_rewind();
423 #else
424 audio_pause();
425 #endif
426 audio_ff_rewind(0);
428 else
430 audio_prev_dir();
433 else if (global_settings.skip_length > 0
434 && current_tick -last_right < HZ)
436 prev_track(3+global_settings.skip_length);
437 update_track = true;
439 else ffwd_rew(ACTION_WPS_SEEKBACK);
440 last_left = last_right = 0;
441 break;
443 /* prev / restart */
444 case ACTION_WPS_SKIPPREV:
445 if (global_settings.party_mode)
446 break;
447 last_left = current_tick;
448 update_track = true;
450 #ifdef AB_REPEAT_ENABLE
451 /* if we're in A/B repeat mode and the current position
452 is past the A marker, jump back to the A marker... */
453 if ( ab_repeat_mode_enabled() )
455 if ( ab_after_A_marker(wps_state.id3->elapsed) )
457 ab_jump_to_A_marker();
458 break;
459 #if (AB_REPEAT_ENABLE == 2)
460 } else {
461 ab_reset_markers();
462 #endif
465 /* ...otherwise, do it normally */
466 #endif
468 if (global_settings.skip_length > 0)
469 play_hop(-1);
470 else prev_track(3);
471 break;
473 /* next
474 OR if skip length set, hop by predetermined amount. */
475 case ACTION_WPS_SKIPNEXT:
476 if (global_settings.party_mode)
477 break;
478 last_right = current_tick;
479 update_track = true;
481 #ifdef AB_REPEAT_ENABLE
482 /* if we're in A/B repeat mode and the current position is
483 before the A marker, jump to the A marker... */
484 if ( ab_repeat_mode_enabled() )
486 if ( ab_before_A_marker(wps_state.id3->elapsed) )
488 ab_jump_to_A_marker();
489 break;
490 #if (AB_REPEAT_ENABLE == 2)
491 } else {
492 ab_reset_markers();
493 #endif
496 /* ...otherwise, do it normally */
497 #endif
499 if (global_settings.skip_length > 0)
500 play_hop(1);
501 else next_track();
502 break;
503 /* next / prev directories */
504 /* and set A-B markers if in a-b mode */
505 case ACTION_WPS_ABSETB_NEXTDIR:
506 if (global_settings.party_mode)
507 break;
508 #if defined(AB_REPEAT_ENABLE)
509 if (ab_repeat_mode_enabled())
511 ab_set_B_marker(wps_state.id3->elapsed);
512 ab_jump_to_A_marker();
513 update_track = true;
515 else
516 #endif
518 if (global_settings.skip_length > 0)
519 next_track();
520 else audio_next_dir();
522 break;
523 case ACTION_WPS_ABSETA_PREVDIR:
524 if (global_settings.party_mode)
525 break;
526 #if defined(AB_REPEAT_ENABLE)
527 if (ab_repeat_mode_enabled())
528 ab_set_A_marker(wps_state.id3->elapsed);
529 else
530 #endif
532 if (global_settings.skip_length > 0)
533 prev_track(3);
534 else audio_prev_dir();
536 break;
537 /* menu key functions */
538 case ACTION_WPS_MENU:
539 FOR_NB_SCREENS(i)
540 gui_wps[i].display->stop_scroll();
541 return GO_TO_ROOT;
542 break;
545 #ifdef HAVE_QUICKSCREEN
546 case ACTION_WPS_QUICKSCREEN:
548 viewportmanager_set_statusbar(oldbars);
549 #if LCD_DEPTH > 1
550 show_main_backdrop();
551 #endif
552 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
553 show_remote_main_backdrop();
554 #endif
555 if (quick_screen_quick(button))
556 return SYS_USB_CONNECTED;
557 wpsbars = fix_wps_bars();
558 viewportmanager_set_statusbar(wpsbars);
559 #if LCD_DEPTH > 1
560 show_wps_backdrop();
561 #endif
562 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
563 show_remote_wps_backdrop();
564 #endif
565 restore = true;
567 break;
568 #endif /* HAVE_QUICKSCREEN */
570 /* screen settings */
571 #ifdef BUTTON_F3
572 case ACTION_F3:
574 viewportmanager_set_statusbar(oldbars);
575 #if LCD_DEPTH > 1
576 show_main_backdrop();
577 #endif
578 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
579 show_remote_main_backdrop();
580 #endif
581 if (quick_screen_f3(BUTTON_F3))
582 return SYS_USB_CONNECTED;
583 restore = true;
584 wpsbars = fix_wps_bars();
585 viewportmanager_set_statusbar(wpsbars);
587 break;
588 #endif /* BUTTON_F3 */
590 /* pitch screen */
591 #ifdef HAVE_PITCHSCREEN
592 case ACTION_WPS_PITCHSCREEN:
594 viewportmanager_set_statusbar(oldbars);
595 #if LCD_DEPTH > 1
596 show_main_backdrop();
597 #endif
598 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
599 show_remote_main_backdrop();
600 #endif
601 if (1 == gui_syncpitchscreen_run())
602 return SYS_USB_CONNECTED;
603 #if LCD_DEPTH > 1
604 show_wps_backdrop();
605 #endif
606 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
607 show_remote_wps_backdrop();
608 #endif
609 restore = true;
610 viewportmanager_set_statusbar(wpsbars);
612 break;
613 #endif /* HAVE_PITCHSCREEN */
615 #ifdef AB_REPEAT_ENABLE
616 /* reset A&B markers */
617 case ACTION_WPS_ABRESET:
618 if (ab_repeat_mode_enabled())
620 ab_reset_markers();
621 update_track = true;
623 break;
624 #endif /* AB_REPEAT_ENABLE */
626 /* stop and exit wps */
627 case ACTION_WPS_STOP:
628 if (global_settings.party_mode)
629 break;
630 bookmark = true;
631 exit = true;
632 break;
634 case ACTION_WPS_ID3SCREEN:
636 viewportmanager_set_statusbar(oldbars);
637 #if LCD_DEPTH > 1
638 show_main_backdrop();
639 #endif
640 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
641 show_remote_main_backdrop();
642 #endif
643 browse_id3();
644 #if LCD_DEPTH > 1
645 show_wps_backdrop();
646 #endif
647 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
648 show_remote_wps_backdrop();
649 #endif
650 restore = true;
651 viewportmanager_set_statusbar(wpsbars);
653 break;
655 case ACTION_REDRAW: /* yes are locked, just redraw */
656 restore = true;
657 break;
658 case ACTION_NONE: /* Timeout */
659 update_track = true;
660 ffwd_rew(button); /* hopefully fix the ffw/rwd bug */
661 break;
662 #ifdef HAVE_RECORDING
663 case ACTION_WPS_REC:
664 exit = true;
665 break;
666 #endif
667 case SYS_POWEROFF:
668 viewportmanager_set_statusbar(oldbars);
669 #if LCD_DEPTH > 1
670 show_main_backdrop();
671 #endif
672 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
673 show_remote_main_backdrop();
674 #endif
675 default_event_handler(SYS_POWEROFF);
676 break;
678 default:
679 if(default_event_handler(button) == SYS_USB_CONNECTED)
680 return GO_TO_ROOT;
681 update_track = true;
682 break;
685 if (update_track)
687 FOR_NB_SCREENS(i)
689 if(update(&gui_wps[i]))
690 exit = true;
692 update_track = false;
695 if (restore &&
696 ((restoretimer == 0) ||
697 (restoretimer < current_tick)))
699 restore = false;
700 restoretimer = 0;
701 if (gui_wps_display()) {
702 exit = true;
706 if (exit) {
707 viewportmanager_set_statusbar(oldbars);
708 #ifdef HAVE_LCD_CHARCELLS
709 status_set_record(false);
710 status_set_audio(false);
711 #endif
712 if (global_settings.fade_on_stop)
713 fade(false, true);
715 FOR_NB_SCREENS(i)
716 gui_wps[i].display->stop_scroll();
717 if (bookmark)
718 bookmark_autobookmark();
719 audio_stop();
720 #ifdef AB_REPEAT_ENABLE
721 ab_reset_markers();
722 #endif
723 #ifdef HAVE_RECORDING
724 if (button == ACTION_WPS_REC)
725 return GO_TO_RECSCREEN;
726 #endif
727 if (global_settings.browse_current)
728 return GO_TO_PREVIOUS_BROWSER;
729 return GO_TO_PREVIOUS;
732 if (button && !IS_SYSEVENT(button) )
733 storage_spin();
735 return GO_TO_ROOT; /* unreachable - just to reduce compiler warnings */
738 /* needs checking if needed end*/
740 /* wps_state */
742 static void wps_state_init(void)
744 wps_state.ff_rewind = false;
745 wps_state.paused = false;
746 wps_state.id3 = NULL;
747 wps_state.nid3 = NULL;
750 /* wps_state end*/
752 #ifdef HAVE_LCD_BITMAP
753 static void statusbar_toggle_handler(void *data)
755 (void)data;
756 int i;
757 bool draw = global_settings.statusbar;
759 FOR_NB_SCREENS(i)
761 struct wps_viewport *vp = &gui_wps[i].data->viewports[0];
762 if (gui_wps[i].data->wps_sb_tag)
763 draw = gui_wps[i].data->show_sb_on_wps;
764 if (!draw)
766 vp->vp.y = 0;
767 vp->vp.height = screens[i].lcdheight;
769 else
771 vp->vp.y = STATUSBAR_HEIGHT;
772 vp->vp.height = screens[i].lcdheight - STATUSBAR_HEIGHT;
776 #endif
778 void gui_sync_wps_init(void)
780 int i;
781 FOR_NB_SCREENS(i)
783 wps_data_init(&wps_datas[i]);
784 #ifdef HAVE_ALBUMART
785 wps_datas[i].wps_uses_albumart = 0;
786 #endif
787 #ifdef HAVE_REMOTE_LCD
788 wps_datas[i].remote_wps = (i != 0);
789 #endif
790 gui_wps[i].data = &wps_datas[i];
791 gui_wps[i].display = &screens[i];
792 /* Currently no seperate wps_state needed/possible
793 so use the only aviable ( "global" ) one */
794 gui_wps[i].state = &wps_state;
796 #ifdef HAVE_LCD_BITMAP
797 add_event(GUI_EVENT_STATUSBAR_TOGGLE, false, statusbar_toggle_handler);
798 #endif
799 #if LCD_DEPTH > 1
800 unload_wps_backdrop();
801 #endif
802 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
803 unload_remote_wps_backdrop();
804 #endif
807 #ifdef HAVE_ALBUMART
808 /* Returns true if at least one of the gui_wps screens has an album art
809 tag in its wps structure */
810 bool gui_sync_wps_uses_albumart(void)
812 int i;
813 FOR_NB_SCREENS(i) {
814 struct gui_wps *gwps = &gui_wps[i];
815 if (gwps->data && (gwps->data->wps_uses_albumart != WPS_ALBUMART_NONE))
816 return true;
818 return false;
820 #endif