Drop the unnecessary checks since they are made by the caller
[kugel-rb/myfork.git] / apps / gui / gwps.c
blob32a27212a46b0102fa7470aab2356c9c3e668d96
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"
66 #include "pcmbuf.h"
68 #define RESTORE_WPS_INSTANTLY 0l
69 #define RESTORE_WPS_NEXT_SECOND ((long)(HZ+current_tick))
70 /* in milliseconds */
71 #define DEFAULT_SKIP_TRESH 3000ul
73 static int wpsbars;
74 /* currently only one wps_state is needed */
75 struct wps_state wps_state;
76 struct gui_wps gui_wps[NB_SCREENS];
77 static struct wps_data wps_datas[NB_SCREENS];
79 /* initial setup of wps_data */
80 static void wps_state_init(void);
82 static void change_dir(int direction)
84 if (global_settings.prevent_skip)
85 return;
87 if (direction < 0)
88 audio_prev_dir();
89 else if (direction > 0)
90 audio_next_dir();
93 static void prev_track(unsigned long skip_thresh)
95 if (wps_state.id3->elapsed < skip_thresh)
97 audio_prev();
98 return;
100 else
102 if (cuesheet_is_enabled() && wps_state.id3->cuesheet_type)
104 curr_cuesheet_skip(-1, wps_state.id3->elapsed);
105 return;
108 if (!wps_state.paused)
109 #if (CONFIG_CODEC == SWCODEC)
110 audio_pre_ff_rewind();
111 #else
112 audio_pause();
113 #endif
115 audio_ff_rewind(0);
117 #if (CONFIG_CODEC != SWCODEC)
118 if (!wps_state.paused)
119 audio_resume();
120 #endif
124 static void next_track(void)
126 /* take care of if we're playing a cuesheet */
127 if (cuesheet_is_enabled() && wps_state.id3->cuesheet_type)
129 if (curr_cuesheet_skip(1, wps_state.id3->elapsed))
131 /* if the result was false, then we really want
132 to skip to the next track */
133 return;
137 audio_next();
140 static void play_hop(int direction)
142 unsigned long step = ((unsigned long)global_settings.skip_length)*1000;
143 unsigned long elapsed = wps_state.id3->elapsed;
144 unsigned long remaining = wps_state.id3->length - elapsed;
146 if (!global_settings.prevent_skip &&
147 (!step ||
148 (direction > 0 && step >= remaining) ||
149 (direction < 0 && elapsed < DEFAULT_SKIP_TRESH)))
150 { /* Do normal track skipping */
151 if (direction > 0)
152 next_track();
153 else if (direction < 0)
154 prev_track(DEFAULT_SKIP_TRESH);
155 return;
158 if (direction == 1 && step >= remaining)
160 #if CONFIG_CODEC == SWCODEC
161 if(global_settings.beep)
162 pcmbuf_beep(1000, 150, 1500*global_settings.beep);
163 #endif
164 return;
166 else if ((direction == -1 && elapsed < step))
168 elapsed = 0;
170 else
172 elapsed += step * direction;
174 if((audio_status() & AUDIO_STATUS_PLAY) && !wps_state.paused)
176 #if (CONFIG_CODEC == SWCODEC)
177 audio_pre_ff_rewind();
178 #else
179 audio_pause();
180 #endif
182 audio_ff_rewind(wps_state.id3->elapsed = elapsed);
183 #if (CONFIG_CODEC != SWCODEC)
184 if (!wps_state.paused)
185 audio_resume();
186 #endif
189 static void gwps_fix_statusbars(void)
191 #ifdef HAVE_LCD_BITMAP
192 int i;
193 wpsbars = VP_SB_HIDE_ALL;
194 FOR_NB_SCREENS(i)
196 bool draw = false;
197 if (gui_wps[i].data->wps_sb_tag)
198 draw = gui_wps[i].data->show_sb_on_wps;
199 else if (global_settings.statusbar)
200 wpsbars |= VP_SB_ONSCREEN(i);
201 if (draw)
202 wpsbars |= (VP_SB_ONSCREEN(i) | VP_SB_IGNORE_SETTING(i));
204 #else
205 wpsbars = VP_SB_ALLSCREENS;
206 #endif
210 static void gwps_leave_wps(void)
212 int i, oldbars = VP_SB_HIDE_ALL;
214 FOR_NB_SCREENS(i)
215 gui_wps[i].display->stop_scroll();
216 if (global_settings.statusbar)
217 oldbars = VP_SB_ALLSCREENS;
219 #if LCD_DEPTH > 1
220 show_main_backdrop();
221 #endif
222 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
223 show_remote_main_backdrop();
224 #endif
225 viewportmanager_set_statusbar(oldbars);
228 void gwps_draw_statusbars(void)
230 viewportmanager_set_statusbar(wpsbars);
233 /* The WPS can be left in two ways:
234 * a) call a function, which draws over the wps. In this case, the wps
235 * will be still active (i.e. the below function didn't return)
236 * b) return with a value evaluated by root_menu.c, in this case the wps
237 * is really left, and root_menu will handle the next screen
239 * In either way, call gwps_leave_wps(), in order to restore the correct
240 * "main screen" backdrops and statusbars
242 long gui_wps_show(void)
244 long button = 0;
245 bool restore = false;
246 long restoretimer = RESTORE_WPS_INSTANTLY; /* timer to delay screen redraw temporarily */
247 bool exit = false;
248 bool bookmark = false;
249 bool update_track = false;
250 int i;
251 long last_left = 0, last_right = 0;
252 wps_state_init();
254 #ifdef HAVE_LCD_CHARCELLS
255 status_set_audio(true);
256 status_set_param(false);
257 #endif
259 gwps_fix_statusbars();
261 #ifdef AB_REPEAT_ENABLE
262 ab_repeat_init();
263 ab_reset_markers();
264 #endif
265 if(audio_status() & AUDIO_STATUS_PLAY)
267 wps_state.id3 = audio_current_track();
268 wps_state.nid3 = audio_next_track();
269 restore = true; /* force initial full redraw */
272 while ( 1 )
274 bool audio_paused = (audio_status() & AUDIO_STATUS_PAUSE)?true:false;
276 /* did someone else (i.e power thread) change audio pause mode? */
277 if (wps_state.paused != audio_paused) {
278 wps_state.paused = audio_paused;
280 /* if another thread paused audio, we are probably in car mode,
281 about to shut down. lets save the settings. */
282 if (wps_state.paused) {
283 settings_save();
284 #if !defined(HAVE_RTC_RAM) && !defined(HAVE_SW_POWEROFF)
285 call_storage_idle_notifys(true);
286 #endif
290 #ifdef HAVE_LCD_BITMAP
291 /* when the peak meter is enabled we want to have a
292 few extra updates to make it look smooth. On the
293 other hand we don't want to waste energy if it
294 isn't displayed */
295 bool pm=false;
296 FOR_NB_SCREENS(i)
298 if(gui_wps[i].data->peak_meter_enabled)
299 pm = true;
302 if (pm) {
303 long next_refresh = current_tick;
304 long next_big_refresh = current_tick + HZ / 5;
305 button = BUTTON_NONE;
306 while (TIME_BEFORE(current_tick, next_big_refresh)) {
307 button = get_action(CONTEXT_WPS|ALLOW_SOFTLOCK,TIMEOUT_NOBLOCK);
308 if (button != ACTION_NONE) {
309 break;
311 peak_meter_peek();
312 sleep(0); /* Sleep until end of current tick. */
314 if (TIME_AFTER(current_tick, next_refresh)) {
315 FOR_NB_SCREENS(i)
317 if(gui_wps[i].data->peak_meter_enabled)
318 gui_wps_redraw(&gui_wps[i], 0,
319 WPS_REFRESH_PEAK_METER);
320 next_refresh += HZ / PEAK_METER_FPS;
327 /* The peak meter is disabled
328 -> no additional screen updates needed */
329 else
330 #endif
332 button = get_action(CONTEXT_WPS|ALLOW_SOFTLOCK,HZ/5);
335 /* Exit if audio has stopped playing. This happens e.g. at end of
336 playlist or if using the sleep timer. */
337 if (!(audio_status() & AUDIO_STATUS_PLAY))
338 exit = true;
339 /* The iPods/X5/M5 use a single button for the A-B mode markers,
340 defined as ACTION_WPSAB_SINGLE in their config files. */
341 #ifdef ACTION_WPSAB_SINGLE
342 if (!global_settings.party_mode && ab_repeat_mode_enabled())
344 static int wps_ab_state = 0;
345 if (button == ACTION_WPSAB_SINGLE)
347 switch (wps_ab_state)
349 case 0: /* set the A spot */
350 button = ACTION_WPS_ABSETA_PREVDIR;
351 break;
352 case 1: /* set the B spot */
353 button = ACTION_WPS_ABSETB_NEXTDIR;
354 break;
355 case 2:
356 button = ACTION_WPS_ABRESET;
357 break;
359 wps_ab_state = (wps_ab_state+1) % 3;
362 #endif
363 switch(button)
365 case ACTION_WPS_CONTEXT:
367 gwps_leave_wps();
368 /* if music is stopped in the context menu we want to exit the wps */
369 if (onplay(wps_state.id3->path,
370 FILE_ATTR_AUDIO, CONTEXT_WPS) == ONPLAY_MAINMENU
371 || !audio_status())
372 return GO_TO_ROOT;
373 /* track might have changed */
374 update_track = true;
375 restore = true;
377 break;
379 case ACTION_WPS_BROWSE:
380 #ifdef HAVE_LCD_CHARCELLS
381 status_set_record(false);
382 status_set_audio(false);
383 #endif
384 gwps_leave_wps();
385 return GO_TO_PREVIOUS_BROWSER;
386 break;
388 /* play/pause */
389 case ACTION_WPS_PLAY:
390 if (global_settings.party_mode)
391 break;
392 if ( wps_state.paused )
394 wps_state.paused = false;
395 if ( global_settings.fade_on_stop )
396 fade(true, true);
397 else
398 audio_resume();
400 else
402 wps_state.paused = true;
403 if ( global_settings.fade_on_stop )
404 fade(false, true);
405 else
406 audio_pause();
407 settings_save();
408 #if !defined(HAVE_RTC_RAM) && !defined(HAVE_SW_POWEROFF)
409 call_storage_idle_notifys(true); /* make sure resume info is saved */
410 #endif
412 break;
414 case ACTION_WPS_VOLUP:
416 FOR_NB_SCREENS(i)
417 gui_wps[i].data->button_time_volume = current_tick;
418 global_settings.volume++;
419 bool res = false;
420 setvol();
421 FOR_NB_SCREENS(i)
423 if(update_onvol_change(&gui_wps[i]))
424 res = true;
426 if (res) {
427 restore = true;
428 restoretimer = RESTORE_WPS_NEXT_SECOND;
431 break;
432 case ACTION_WPS_VOLDOWN:
434 FOR_NB_SCREENS(i)
435 gui_wps[i].data->button_time_volume = current_tick;
436 global_settings.volume--;
437 setvol();
438 bool res = false;
439 FOR_NB_SCREENS(i)
441 if(update_onvol_change(&gui_wps[i]))
442 res = true;
444 if (res) {
445 restore = true;
446 restoretimer = RESTORE_WPS_NEXT_SECOND;
449 break;
450 /* fast forward
451 OR next dir if this is straight after ACTION_WPS_SKIPNEXT */
452 case ACTION_WPS_SEEKFWD:
453 if (global_settings.party_mode)
454 break;
455 if (current_tick -last_right < HZ)
457 if (cuesheet_is_enabled() && wps_state.id3->cuesheet_type)
459 audio_next();
461 else
463 change_dir(1);
466 else
467 ffwd_rew(ACTION_WPS_SEEKFWD);
468 last_right = last_left = 0;
469 break;
470 /* fast rewind
471 OR prev dir if this is straight after ACTION_WPS_SKIPPREV,*/
472 case ACTION_WPS_SEEKBACK:
473 if (global_settings.party_mode)
474 break;
475 if (current_tick -last_left < HZ)
477 if (cuesheet_is_enabled() && wps_state.id3->cuesheet_type)
479 if (!wps_state.paused)
480 #if (CONFIG_CODEC == SWCODEC)
481 audio_pre_ff_rewind();
482 #else
483 audio_pause();
484 #endif
485 audio_ff_rewind(0);
487 else
489 change_dir(-1);
492 else
493 ffwd_rew(ACTION_WPS_SEEKBACK);
494 last_left = last_right = 0;
495 break;
497 /* prev / restart */
498 case ACTION_WPS_SKIPPREV:
499 if (global_settings.party_mode)
500 break;
501 last_left = current_tick;
502 update_track = true;
503 #ifdef AB_REPEAT_ENABLE
504 /* if we're in A/B repeat mode and the current position
505 is past the A marker, jump back to the A marker... */
506 if ( ab_repeat_mode_enabled() )
508 if ( ab_after_A_marker(wps_state.id3->elapsed) )
510 ab_jump_to_A_marker();
511 break;
512 #if (AB_REPEAT_ENABLE == 2)
513 } else {
514 ab_reset_markers();
515 #endif
518 else
519 /* ...otherwise, do it normally */
520 #endif
521 play_hop(-1);
522 break;
524 /* next
525 OR if skip length set, hop by predetermined amount. */
526 case ACTION_WPS_SKIPNEXT:
527 if (global_settings.party_mode)
528 break;
529 last_right = current_tick;
530 update_track = true;
531 #ifdef AB_REPEAT_ENABLE
532 /* if we're in A/B repeat mode and the current position is
533 before the A marker, jump to the A marker... */
534 if ( ab_repeat_mode_enabled() )
536 if ( ab_before_A_marker(wps_state.id3->elapsed) )
538 ab_jump_to_A_marker();
539 break;
540 #if (AB_REPEAT_ENABLE == 2)
541 } else {
542 ab_reset_markers();
543 #endif
546 else
547 /* ...otherwise, do it normally */
548 #endif
549 play_hop(1);
550 break;
551 /* next / prev directories */
552 /* and set A-B markers if in a-b mode */
553 case ACTION_WPS_ABSETB_NEXTDIR:
554 if (global_settings.party_mode)
555 break;
556 #if defined(AB_REPEAT_ENABLE)
557 if (ab_repeat_mode_enabled())
559 ab_set_B_marker(wps_state.id3->elapsed);
560 ab_jump_to_A_marker();
561 update_track = true;
563 else
564 #endif
566 change_dir(1);
568 break;
569 case ACTION_WPS_ABSETA_PREVDIR:
570 if (global_settings.party_mode)
571 break;
572 #if defined(AB_REPEAT_ENABLE)
573 if (ab_repeat_mode_enabled())
574 ab_set_A_marker(wps_state.id3->elapsed);
575 else
576 #endif
578 change_dir(-1);
580 break;
581 /* menu key functions */
582 case ACTION_WPS_MENU:
583 gwps_leave_wps();
584 return GO_TO_ROOT;
585 break;
588 #ifdef HAVE_QUICKSCREEN
589 case ACTION_WPS_QUICKSCREEN:
591 gwps_leave_wps();
592 if (quick_screen_quick(button))
593 return SYS_USB_CONNECTED;
594 restore = true;
596 break;
597 #endif /* HAVE_QUICKSCREEN */
599 /* screen settings */
600 #ifdef BUTTON_F3
601 case ACTION_F3:
603 gwps_leave_wps();
604 if (quick_screen_f3(BUTTON_F3))
605 return SYS_USB_CONNECTED;
606 restore = true;
608 break;
609 #endif /* BUTTON_F3 */
611 /* pitch screen */
612 #ifdef HAVE_PITCHSCREEN
613 case ACTION_WPS_PITCHSCREEN:
615 gwps_leave_wps();
616 if (1 == gui_syncpitchscreen_run())
617 return SYS_USB_CONNECTED;
618 restore = true;
620 break;
621 #endif /* HAVE_PITCHSCREEN */
623 #ifdef AB_REPEAT_ENABLE
624 /* reset A&B markers */
625 case ACTION_WPS_ABRESET:
626 if (ab_repeat_mode_enabled())
628 ab_reset_markers();
629 update_track = true;
631 break;
632 #endif /* AB_REPEAT_ENABLE */
634 /* stop and exit wps */
635 case ACTION_WPS_STOP:
636 if (global_settings.party_mode)
637 break;
638 bookmark = true;
639 exit = true;
640 break;
642 case ACTION_WPS_ID3SCREEN:
644 gwps_leave_wps();
645 browse_id3();
646 restore = true;
648 break;
650 case ACTION_REDRAW: /* yes are locked, just redraw */
651 restore = true;
652 break;
653 case ACTION_NONE: /* Timeout */
654 update_track = true;
655 ffwd_rew(button); /* hopefully fix the ffw/rwd bug */
656 break;
657 #ifdef HAVE_RECORDING
658 case ACTION_WPS_REC:
659 exit = true;
660 break;
661 #endif
662 case SYS_POWEROFF:
663 gwps_leave_wps();
664 default_event_handler(SYS_POWEROFF);
665 break;
667 default:
668 if(default_event_handler(button) == SYS_USB_CONNECTED)
669 return GO_TO_ROOT;
670 update_track = true;
671 break;
674 if (update_track)
676 FOR_NB_SCREENS(i)
678 gui_wps_update(&gui_wps[i]);
680 update_track = false;
683 if (restore && wps_state.id3 &&
684 ((restoretimer == RESTORE_WPS_INSTANTLY) ||
685 TIME_AFTER(current_tick, restoretimer)))
687 restore = false;
688 restoretimer = RESTORE_WPS_INSTANTLY;
689 FOR_NB_SCREENS(i)
691 screens[i].stop_scroll();
692 gui_wps_display(&gui_wps[i]);
696 if (exit) {
697 #ifdef HAVE_LCD_CHARCELLS
698 status_set_record(false);
699 status_set_audio(false);
700 #endif
701 if (global_settings.fade_on_stop)
702 fade(false, true);
704 if (bookmark)
705 bookmark_autobookmark();
706 audio_stop();
707 #ifdef AB_REPEAT_ENABLE
708 ab_reset_markers();
709 #endif
710 gwps_leave_wps();
711 #ifdef HAVE_RECORDING
712 if (button == ACTION_WPS_REC)
713 return GO_TO_RECSCREEN;
714 #endif
715 if (global_settings.browse_current)
716 return GO_TO_PREVIOUS_BROWSER;
717 return GO_TO_PREVIOUS;
720 if (button && !IS_SYSEVENT(button) )
721 storage_spin();
723 return GO_TO_ROOT; /* unreachable - just to reduce compiler warnings */
726 /* needs checking if needed end*/
728 /* wps_state */
730 static void wps_state_init(void)
732 wps_state.ff_rewind = false;
733 wps_state.paused = false;
734 wps_state.id3 = NULL;
735 wps_state.nid3 = NULL;
738 /* wps_state end*/
740 #ifdef HAVE_LCD_BITMAP
741 static void statusbar_toggle_handler(void *data)
743 (void)data;
744 int i;
745 gwps_fix_statusbars();
747 FOR_NB_SCREENS(i)
749 struct viewport *vp = &gui_wps[i].data->viewports[0].vp;
750 bool draw = wpsbars & (VP_SB_ONSCREEN(i) | VP_SB_IGNORE_SETTING(i));
751 if (!draw)
753 vp->y = 0;
754 vp->height = screens[i].lcdheight;
756 else
758 vp->y = STATUSBAR_HEIGHT;
759 vp->height = screens[i].lcdheight - STATUSBAR_HEIGHT;
763 #endif
765 void gui_sync_wps_init(void)
767 int i;
768 FOR_NB_SCREENS(i)
770 wps_data_init(&wps_datas[i]);
771 #ifdef HAVE_ALBUMART
772 wps_datas[i].wps_uses_albumart = 0;
773 #endif
774 #ifdef HAVE_REMOTE_LCD
775 wps_datas[i].remote_wps = (i != 0);
776 #endif
777 gui_wps[i].data = &wps_datas[i];
778 gui_wps[i].display = &screens[i];
779 /* Currently no seperate wps_state needed/possible
780 so use the only aviable ( "global" ) one */
781 gui_wps[i].state = &wps_state;
783 #ifdef HAVE_LCD_BITMAP
784 add_event(GUI_EVENT_STATUSBAR_TOGGLE, false, statusbar_toggle_handler);
785 #endif
786 #if LCD_DEPTH > 1
787 unload_wps_backdrop();
788 #endif
789 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
790 unload_remote_wps_backdrop();
791 #endif
794 #ifdef HAVE_ALBUMART
795 /* Returns true if at least one of the gui_wps screens has an album art
796 tag in its wps structure */
797 bool gui_sync_wps_uses_albumart(void)
799 int i;
800 FOR_NB_SCREENS(i) {
801 struct gui_wps *gwps = &gui_wps[i];
802 if (gwps->data && (gwps->data->wps_uses_albumart != WPS_ALBUMART_NONE))
803 return true;
805 return false;
807 #endif