Fix up statusbar drawing in the wps a bit, fixing most, if not all, (re-)draw issues.
[kugel-rb/myfork.git] / apps / gui / gwps.c
blobcbce0f59738f434a0f147ba1e14e5998ff69bd50
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))
71 static int wpsbars;
72 /* currently only one wps_state is needed */
73 struct wps_state wps_state;
74 struct gui_wps gui_wps[NB_SCREENS];
75 static struct wps_data wps_datas[NB_SCREENS];
77 /* initial setup of wps_data */
78 static void wps_state_init(void);
80 static void prev_track(unsigned skip_thresh)
82 if (!wps_state.id3 || (wps_state.id3->elapsed < skip_thresh*1000)) {
83 audio_prev();
85 else {
86 if (cuesheet_is_enabled() && wps_state.id3->cuesheet_type)
88 curr_cuesheet_skip(-1, wps_state.id3->elapsed);
89 return;
92 if (!wps_state.paused)
93 #if (CONFIG_CODEC == SWCODEC)
94 audio_pre_ff_rewind();
95 #else
96 audio_pause();
97 #endif
99 audio_ff_rewind(0);
101 #if (CONFIG_CODEC != SWCODEC)
102 if (!wps_state.paused)
103 audio_resume();
104 #endif
108 static void next_track(void)
110 /* take care of if we're playing a cuesheet */
111 if (cuesheet_is_enabled() && wps_state.id3->cuesheet_type)
113 if (curr_cuesheet_skip(1, wps_state.id3->elapsed))
115 /* if the result was false, then we really want
116 to skip to the next track */
117 return;
121 audio_next();
124 static void play_hop(int direction)
126 unsigned step = ((unsigned)global_settings.skip_length*1000);
127 unsigned long *elapsed = &(wps_state.id3->elapsed);
129 if (direction == 1 && wps_state.id3->length - *elapsed < step+1000) {
130 #if CONFIG_CODEC == SWCODEC
131 if(global_settings.beep)
132 pcmbuf_beep(1000, 150, 1500*global_settings.beep);
133 #endif
134 return;
135 } else if ((direction == -1 && *elapsed < step)) {
136 *elapsed = 0;
137 } else {
138 *elapsed += step * direction;
140 if((audio_status() & AUDIO_STATUS_PLAY) && !wps_state.paused) {
141 #if (CONFIG_CODEC == SWCODEC)
142 audio_pre_ff_rewind();
143 #else
144 audio_pause();
145 #endif
147 audio_ff_rewind(*elapsed);
148 #if (CONFIG_CODEC != SWCODEC)
149 if (!wps_state.paused)
150 audio_resume();
151 #endif
154 static void gwps_fix_statusbars(void)
156 #ifdef HAVE_LCD_BITMAP
157 int i;
158 wpsbars = VP_SB_HIDE_ALL;
159 FOR_NB_SCREENS(i)
161 bool draw = false;
162 if (gui_wps[i].data->wps_sb_tag)
163 draw = gui_wps[i].data->show_sb_on_wps;
164 else if (global_settings.statusbar)
165 wpsbars |= VP_SB_ONSCREEN(i);
166 if (draw)
167 wpsbars |= (VP_SB_ONSCREEN(i) | VP_SB_IGNORE_SETTING(i));
169 #else
170 wpsbars = VP_SB_ALLSCREENS;
171 #endif
175 static void gwps_leave_wps(void)
177 int i, oldbars = VP_SB_HIDE_ALL;
179 FOR_NB_SCREENS(i)
180 gui_wps[i].display->stop_scroll();
181 if (global_settings.statusbar)
182 oldbars = VP_SB_ALLSCREENS;
184 #if LCD_DEPTH > 1
185 show_main_backdrop();
186 #endif
187 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
188 show_remote_main_backdrop();
189 #endif
190 viewportmanager_set_statusbar(oldbars);
193 void gwps_draw_statusbars(void)
195 viewportmanager_set_statusbar(wpsbars);
198 /* The WPS can be left in two ways:
199 * a) call a function, which draws over the wps. In this case, the wps
200 * will be still active (i.e. the below function didn't return)
201 * b) return with a value evaluated by root_menu.c, in this case the wps
202 * is really left, and root_menu will handle the next screen
204 * In either way, call gwps_leave_wps(), in order to restore the correct
205 * "main screen" backdrops and statusbars
207 long gui_wps_show(void)
209 long button = 0;
210 bool restore = false;
211 long restoretimer = RESTORE_WPS_INSTANTLY; /* timer to delay screen redraw temporarily */
212 bool exit = false;
213 bool bookmark = false;
214 bool update_track = false;
215 int i;
216 long last_left = 0, last_right = 0;
217 wps_state_init();
219 #ifdef HAVE_LCD_CHARCELLS
220 status_set_audio(true);
221 status_set_param(false);
222 #else
223 #if LCD_DEPTH > 1
224 show_wps_backdrop();
225 #endif /* LCD_DEPTH > 1 */
226 #endif
228 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
229 show_remote_wps_backdrop();
230 #endif
231 gwps_fix_statusbars();
233 #ifdef AB_REPEAT_ENABLE
234 ab_repeat_init();
235 ab_reset_markers();
236 #endif
237 if(audio_status() & AUDIO_STATUS_PLAY)
239 wps_state.id3 = audio_current_track();
240 wps_state.nid3 = audio_next_track();
241 if (wps_state.id3) {
242 if (gui_wps_display())
244 gwps_leave_wps();
245 return 0;
249 restore = true;
252 while ( 1 )
254 bool audio_paused = (audio_status() & AUDIO_STATUS_PAUSE)?true:false;
256 /* did someone else (i.e power thread) change audio pause mode? */
257 if (wps_state.paused != audio_paused) {
258 wps_state.paused = audio_paused;
260 /* if another thread paused audio, we are probably in car mode,
261 about to shut down. lets save the settings. */
262 if (wps_state.paused) {
263 settings_save();
264 #if !defined(HAVE_RTC_RAM) && !defined(HAVE_SW_POWEROFF)
265 call_storage_idle_notifys(true);
266 #endif
270 #ifdef HAVE_LCD_BITMAP
271 /* when the peak meter is enabled we want to have a
272 few extra updates to make it look smooth. On the
273 other hand we don't want to waste energy if it
274 isn't displayed */
275 bool pm=false;
276 FOR_NB_SCREENS(i)
278 if(gui_wps[i].data->peak_meter_enabled)
279 pm = true;
282 if (pm) {
283 long next_refresh = current_tick;
284 long next_big_refresh = current_tick + HZ / 5;
285 button = BUTTON_NONE;
286 while (TIME_BEFORE(current_tick, next_big_refresh)) {
287 button = get_action(CONTEXT_WPS|ALLOW_SOFTLOCK,TIMEOUT_NOBLOCK);
288 if (button != ACTION_NONE) {
289 break;
291 peak_meter_peek();
292 sleep(0); /* Sleep until end of current tick. */
294 if (TIME_AFTER(current_tick, next_refresh)) {
295 FOR_NB_SCREENS(i)
297 if(gui_wps[i].data->peak_meter_enabled)
298 gui_wps_refresh(&gui_wps[i], 0,
299 WPS_REFRESH_PEAK_METER);
300 next_refresh += HZ / PEAK_METER_FPS;
307 /* The peak meter is disabled
308 -> no additional screen updates needed */
309 else
310 #endif
312 button = get_action(CONTEXT_WPS|ALLOW_SOFTLOCK,HZ/5);
315 /* Exit if audio has stopped playing. This can happen if using the
316 sleep timer with the charger plugged or if starting a recording
317 from F1 */
318 if (!audio_status())
319 exit = true;
320 /* The iPods/X5/M5 use a single button for the A-B mode markers,
321 defined as ACTION_WPSAB_SINGLE in their config files. */
322 #ifdef ACTION_WPSAB_SINGLE
323 if (!global_settings.party_mode && ab_repeat_mode_enabled())
325 static int wps_ab_state = 0;
326 if (button == ACTION_WPSAB_SINGLE)
328 switch (wps_ab_state)
330 case 0: /* set the A spot */
331 button = ACTION_WPS_ABSETA_PREVDIR;
332 break;
333 case 1: /* set the B spot */
334 button = ACTION_WPS_ABSETB_NEXTDIR;
335 break;
336 case 2:
337 button = ACTION_WPS_ABRESET;
338 break;
340 wps_ab_state = (wps_ab_state+1) % 3;
343 #endif
344 switch(button)
346 case ACTION_WPS_CONTEXT:
348 gwps_leave_wps();
349 /* if music is stopped in the context menu we want to exit the wps */
350 if (onplay(wps_state.id3->path,
351 FILE_ATTR_AUDIO, CONTEXT_WPS) == ONPLAY_MAINMENU
352 || !audio_status())
353 return GO_TO_ROOT;
354 /* track might have changed */
355 update_track = true;
356 restore = true;
358 break;
360 case ACTION_WPS_BROWSE:
361 #ifdef HAVE_LCD_CHARCELLS
362 status_set_record(false);
363 status_set_audio(false);
364 #endif
365 gwps_leave_wps();
366 return GO_TO_PREVIOUS_BROWSER;
367 break;
369 /* play/pause */
370 case ACTION_WPS_PLAY:
371 if (global_settings.party_mode)
372 break;
373 if ( wps_state.paused )
375 wps_state.paused = false;
376 if ( global_settings.fade_on_stop )
377 fade(true, true);
378 else
379 audio_resume();
381 else
383 wps_state.paused = true;
384 if ( global_settings.fade_on_stop )
385 fade(false, true);
386 else
387 audio_pause();
388 settings_save();
389 #if !defined(HAVE_RTC_RAM) && !defined(HAVE_SW_POWEROFF)
390 call_storage_idle_notifys(true); /* make sure resume info is saved */
391 #endif
393 break;
395 /* volume up */
396 case ACTION_WPS_VOLUP:
398 FOR_NB_SCREENS(i)
399 gui_wps[i].data->button_time_volume = current_tick;
400 global_settings.volume++;
401 bool res = false;
402 setvol();
403 FOR_NB_SCREENS(i)
405 if(update_onvol_change(&gui_wps[i]))
406 res = true;
408 if (res) {
409 restore = true;
410 restoretimer = RESTORE_WPS_NEXT_SECOND;
413 break;
415 /* volume down */
416 case ACTION_WPS_VOLDOWN:
418 FOR_NB_SCREENS(i)
419 gui_wps[i].data->button_time_volume = current_tick;
420 global_settings.volume--;
421 setvol();
422 bool res = false;
423 FOR_NB_SCREENS(i)
425 if(update_onvol_change(&gui_wps[i]))
426 res = true;
428 if (res) {
429 restore = true;
430 restoretimer = RESTORE_WPS_NEXT_SECOND;
433 break;
434 /* fast forward
435 OR next dir if this is straight after ACTION_WPS_SKIPNEXT
436 OR if skip length set, next track if straight after SKIPPREV. */
437 case ACTION_WPS_SEEKFWD:
438 if (global_settings.party_mode)
439 break;
440 if (global_settings.skip_length == 0
441 && current_tick -last_right < HZ)
443 if (cuesheet_is_enabled() && wps_state.id3->cuesheet_type)
445 audio_next();
447 else
449 audio_next_dir();
452 else if (global_settings.skip_length > 0
453 && current_tick -last_left < HZ) {
454 next_track();
455 update_track = true;
457 else ffwd_rew(ACTION_WPS_SEEKFWD);
458 last_right = last_left = 0;
459 break;
460 /* fast rewind
461 OR prev dir if this is straight after ACTION_WPS_SKIPPREV,
462 OR if skip length set, beg of track or prev track if this is
463 straight after SKIPPREV */
464 case ACTION_WPS_SEEKBACK:
465 if (global_settings.party_mode)
466 break;
467 if (global_settings.skip_length == 0
468 && current_tick -last_left < HZ)
470 if (cuesheet_is_enabled() && wps_state.id3->cuesheet_type)
472 if (!wps_state.paused)
473 #if (CONFIG_CODEC == SWCODEC)
474 audio_pre_ff_rewind();
475 #else
476 audio_pause();
477 #endif
478 audio_ff_rewind(0);
480 else
482 audio_prev_dir();
485 else if (global_settings.skip_length > 0
486 && current_tick -last_right < HZ)
488 prev_track(3+global_settings.skip_length);
489 update_track = true;
491 else ffwd_rew(ACTION_WPS_SEEKBACK);
492 last_left = last_right = 0;
493 break;
495 /* prev / restart */
496 case ACTION_WPS_SKIPPREV:
497 if (global_settings.party_mode)
498 break;
499 last_left = current_tick;
500 update_track = true;
502 #ifdef AB_REPEAT_ENABLE
503 /* if we're in A/B repeat mode and the current position
504 is past the A marker, jump back to the A marker... */
505 if ( ab_repeat_mode_enabled() )
507 if ( ab_after_A_marker(wps_state.id3->elapsed) )
509 ab_jump_to_A_marker();
510 break;
511 #if (AB_REPEAT_ENABLE == 2)
512 } else {
513 ab_reset_markers();
514 #endif
517 /* ...otherwise, do it normally */
518 #endif
520 if (global_settings.skip_length > 0)
521 play_hop(-1);
522 else prev_track(3);
523 break;
525 /* next
526 OR if skip length set, hop by predetermined amount. */
527 case ACTION_WPS_SKIPNEXT:
528 if (global_settings.party_mode)
529 break;
530 last_right = current_tick;
531 update_track = true;
533 #ifdef AB_REPEAT_ENABLE
534 /* if we're in A/B repeat mode and the current position is
535 before the A marker, jump to the A marker... */
536 if ( ab_repeat_mode_enabled() )
538 if ( ab_before_A_marker(wps_state.id3->elapsed) )
540 ab_jump_to_A_marker();
541 break;
542 #if (AB_REPEAT_ENABLE == 2)
543 } else {
544 ab_reset_markers();
545 #endif
548 /* ...otherwise, do it normally */
549 #endif
551 if (global_settings.skip_length > 0)
552 play_hop(1);
553 else next_track();
554 break;
555 /* next / prev directories */
556 /* and set A-B markers if in a-b mode */
557 case ACTION_WPS_ABSETB_NEXTDIR:
558 if (global_settings.party_mode)
559 break;
560 #if defined(AB_REPEAT_ENABLE)
561 if (ab_repeat_mode_enabled())
563 ab_set_B_marker(wps_state.id3->elapsed);
564 ab_jump_to_A_marker();
565 update_track = true;
567 else
568 #endif
570 if (global_settings.skip_length > 0)
571 next_track();
572 else audio_next_dir();
574 break;
575 case ACTION_WPS_ABSETA_PREVDIR:
576 if (global_settings.party_mode)
577 break;
578 #if defined(AB_REPEAT_ENABLE)
579 if (ab_repeat_mode_enabled())
580 ab_set_A_marker(wps_state.id3->elapsed);
581 else
582 #endif
584 if (global_settings.skip_length > 0)
585 prev_track(3);
586 else audio_prev_dir();
588 break;
589 /* menu key functions */
590 case ACTION_WPS_MENU:
591 gwps_leave_wps();
592 return GO_TO_ROOT;
593 break;
596 #ifdef HAVE_QUICKSCREEN
597 case ACTION_WPS_QUICKSCREEN:
599 gwps_leave_wps();
600 if (quick_screen_quick(button))
601 return SYS_USB_CONNECTED;
602 restore = true;
604 break;
605 #endif /* HAVE_QUICKSCREEN */
607 /* screen settings */
608 #ifdef BUTTON_F3
609 case ACTION_F3:
611 gwps_leave_wps();
612 if (quick_screen_f3(BUTTON_F3))
613 return SYS_USB_CONNECTED;
614 restore = true;
616 break;
617 #endif /* BUTTON_F3 */
619 /* pitch screen */
620 #ifdef HAVE_PITCHSCREEN
621 case ACTION_WPS_PITCHSCREEN:
623 gwps_leave_wps();
624 if (1 == gui_syncpitchscreen_run())
625 return SYS_USB_CONNECTED;
626 restore = true;
628 break;
629 #endif /* HAVE_PITCHSCREEN */
631 #ifdef AB_REPEAT_ENABLE
632 /* reset A&B markers */
633 case ACTION_WPS_ABRESET:
634 if (ab_repeat_mode_enabled())
636 ab_reset_markers();
637 update_track = true;
639 break;
640 #endif /* AB_REPEAT_ENABLE */
642 /* stop and exit wps */
643 case ACTION_WPS_STOP:
644 if (global_settings.party_mode)
645 break;
646 bookmark = true;
647 exit = true;
648 break;
650 case ACTION_WPS_ID3SCREEN:
652 gwps_leave_wps();
653 browse_id3();
654 restore = true;
656 break;
658 case ACTION_REDRAW: /* yes are locked, just redraw */
659 restore = true;
660 break;
661 case ACTION_NONE: /* Timeout */
662 update_track = true;
663 ffwd_rew(button); /* hopefully fix the ffw/rwd bug */
664 break;
665 #ifdef HAVE_RECORDING
666 case ACTION_WPS_REC:
667 exit = true;
668 break;
669 #endif
670 case SYS_POWEROFF:
671 gwps_leave_wps();
672 default_event_handler(SYS_POWEROFF);
673 break;
675 default:
676 if(default_event_handler(button) == SYS_USB_CONNECTED)
677 return GO_TO_ROOT;
678 update_track = true;
679 break;
682 if (update_track)
684 FOR_NB_SCREENS(i)
686 if(update(&gui_wps[i]))
687 exit = true;
689 update_track = false;
692 if (restore &&
693 ((restoretimer == RESTORE_WPS_INSTANTLY) ||
694 TIME_AFTER(current_tick, restoretimer)))
696 /* restore wps backrops and statusbars */
697 #if LCD_DEPTH > 1
698 show_wps_backdrop();
699 #endif
700 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
701 show_remote_wps_backdrop();
702 #endif
703 restore = false;
704 restoretimer = RESTORE_WPS_INSTANTLY;
705 if (gui_wps_display()) {
706 exit = true;
710 if (exit) {
711 #ifdef HAVE_LCD_CHARCELLS
712 status_set_record(false);
713 status_set_audio(false);
714 #endif
715 if (global_settings.fade_on_stop)
716 fade(false, true);
718 if (bookmark)
719 bookmark_autobookmark();
720 audio_stop();
721 #ifdef AB_REPEAT_ENABLE
722 ab_reset_markers();
723 #endif
724 gwps_leave_wps();
725 #ifdef HAVE_RECORDING
726 if (button == ACTION_WPS_REC)
727 return GO_TO_RECSCREEN;
728 #endif
729 if (global_settings.browse_current)
730 return GO_TO_PREVIOUS_BROWSER;
731 return GO_TO_PREVIOUS;
734 if (button && !IS_SYSEVENT(button) )
735 storage_spin();
737 return GO_TO_ROOT; /* unreachable - just to reduce compiler warnings */
740 /* needs checking if needed end*/
742 /* wps_state */
744 static void wps_state_init(void)
746 wps_state.ff_rewind = false;
747 wps_state.paused = false;
748 wps_state.id3 = NULL;
749 wps_state.nid3 = NULL;
752 /* wps_state end*/
754 #ifdef HAVE_LCD_BITMAP
755 static void statusbar_toggle_handler(void *data)
757 (void)data;
758 int i;
759 gwps_fix_statusbars();
761 FOR_NB_SCREENS(i)
763 struct viewport *vp = &gui_wps[i].data->viewports[0].vp;
764 bool draw = wpsbars & (VP_SB_ONSCREEN(i) | VP_SB_IGNORE_SETTING(i));
765 if (!draw)
767 vp->y = 0;
768 vp->height = screens[i].lcdheight;
770 else
772 vp->y = STATUSBAR_HEIGHT;
773 vp->height = screens[i].lcdheight - STATUSBAR_HEIGHT;
777 #endif
779 void gui_sync_wps_init(void)
781 int i;
782 FOR_NB_SCREENS(i)
784 wps_data_init(&wps_datas[i]);
785 #ifdef HAVE_ALBUMART
786 wps_datas[i].wps_uses_albumart = 0;
787 #endif
788 #ifdef HAVE_REMOTE_LCD
789 wps_datas[i].remote_wps = (i != 0);
790 #endif
791 gui_wps[i].data = &wps_datas[i];
792 gui_wps[i].display = &screens[i];
793 /* Currently no seperate wps_state needed/possible
794 so use the only aviable ( "global" ) one */
795 gui_wps[i].state = &wps_state;
797 #ifdef HAVE_LCD_BITMAP
798 add_event(GUI_EVENT_STATUSBAR_TOGGLE, false, statusbar_toggle_handler);
799 #endif
800 #if LCD_DEPTH > 1
801 unload_wps_backdrop();
802 #endif
803 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
804 unload_remote_wps_backdrop();
805 #endif
808 #ifdef HAVE_ALBUMART
809 /* Returns true if at least one of the gui_wps screens has an album art
810 tag in its wps structure */
811 bool gui_sync_wps_uses_albumart(void)
813 int i;
814 FOR_NB_SCREENS(i) {
815 struct gui_wps *gwps = &gui_wps[i];
816 if (gwps->data && (gwps->data->wps_uses_albumart != WPS_ALBUMART_NONE))
817 return true;
819 return false;
821 #endif