First commit:
[kugel-rb.git] / apps / gui / gwps.c
blob0520357002dac925f5d23b4b7c7918b78ed6d0a4
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 Jerome Kuptz
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.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
19 #include <stdio.h>
20 #include <string.h>
21 #include <stdlib.h>
22 #include "config.h"
24 #include "system.h"
25 #include "file.h"
26 #include "lcd.h"
27 #include "font.h"
28 #include "backlight.h"
29 #include "action.h"
30 #include "kernel.h"
31 #include "filetypes.h"
32 #include "debug.h"
33 #include "sprintf.h"
34 #include "settings.h"
35 #include "gwps.h"
36 #include "gwps-common.h"
37 #include "audio.h"
38 #include "usb.h"
39 #include "status.h"
40 #include "ata.h"
41 #include "screens.h"
42 #include "playlist.h"
43 #ifdef HAVE_LCD_BITMAP
44 #include "icons.h"
45 #include "peakmeter.h"
46 #endif
47 #include "action.h"
48 #include "lang.h"
49 #include "bookmark.h"
50 #include "misc.h"
51 #include "sound.h"
52 #include "onplay.h"
53 #include "abrepeat.h"
54 #include "playback.h"
55 #include "splash.h"
56 #include "cuesheet.h"
57 #include "ata_idle_notify.h"
58 #include "root_menu.h"
59 #include "backdrop.h"
61 /* currently only on wps_state is needed */
62 struct wps_state wps_state;
63 struct gui_wps gui_wps[NB_SCREENS];
64 static struct wps_data wps_datas[NB_SCREENS];
66 /* change the path to the current played track */
67 static void wps_state_update_ctp(const char *path);
68 /* initial setup of wps_data */
69 static void wps_state_init(void);
70 /* initial setup of a wps */
71 static void gui_wps_init(struct gui_wps *gui_wps);
72 /* connects a wps with a format-description of the displayed content */
73 static void gui_wps_set_data(struct gui_wps *gui_wps, struct wps_data *data);
74 /* connects a wps with a screen */
75 static void gui_wps_set_disp(struct gui_wps *gui_wps, struct screen *display);
76 /* connects a wps with a statusbar*/
77 static void gui_wps_set_statusbar(struct gui_wps *gui_wps, struct gui_statusbar *statusbar);
79 #ifdef HAVE_LCD_BITMAP
80 static void gui_wps_set_margin(struct gui_wps *gwps)
82 int offset = 0;
83 struct wps_data *data = gwps->data;
84 if(data->wps_sb_tag && data->show_sb_on_wps)
85 offset = STATUSBAR_HEIGHT;
86 else if ( global_settings.statusbar && !data->wps_sb_tag)
87 offset = STATUSBAR_HEIGHT;
88 gwps->display->setmargins(0, offset);
90 #endif
92 long gui_wps_show(void)
94 long button = 0;
95 bool restore = false;
96 long restoretimer = 0; /* timer to delay screen redraw temporarily */
97 bool exit = false;
98 bool bookmark = false;
99 bool update_track = false;
100 int i;
101 long last_left = 0, last_right = 0;
103 wps_state_init();
105 #ifdef HAVE_LCD_CHARCELLS
106 status_set_audio(true);
107 status_set_param(false);
108 #else
109 FOR_NB_SCREENS(i)
111 gui_wps_set_margin(&gui_wps[i]);
113 #if LCD_DEPTH > 1
114 show_wps_backdrop();
115 #endif /* LCD_DEPTH > 1 */
116 #endif
118 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
119 show_remote_wps_backdrop();
120 #endif
122 #ifdef AB_REPEAT_ENABLE
123 ab_repeat_init();
124 ab_reset_markers();
125 #endif
127 if(audio_status() & AUDIO_STATUS_PLAY)
129 wps_state.id3 = audio_current_track();
130 wps_state.nid3 = audio_next_track();
131 if (wps_state.id3) {
132 if (gui_wps_display())
133 return 0;
134 FOR_NB_SCREENS(i)
135 gui_wps_refresh(&gui_wps[i], 0, WPS_REFRESH_ALL);
136 wps_state_update_ctp(wps_state.id3->path);
139 restore = true;
141 while ( 1 )
143 bool audio_paused = (audio_status() & AUDIO_STATUS_PAUSE)?true:false;
145 /* did someone else (i.e power thread) change audio pause mode? */
146 if (wps_state.paused != audio_paused) {
147 wps_state.paused = audio_paused;
149 /* if another thread paused audio, we are probably in car mode,
150 about to shut down. lets save the settings. */
151 if (wps_state.paused) {
152 settings_save();
153 #if !defined(HAVE_RTC_RAM) && !defined(HAVE_SW_POWEROFF)
154 call_ata_idle_notifys(true);
155 #endif
159 #ifdef HAVE_LCD_BITMAP
160 /* when the peak meter is enabled we want to have a
161 few extra updates to make it look smooth. On the
162 other hand we don't want to waste energy if it
163 isn't displayed */
164 bool pm=false;
165 FOR_NB_SCREENS(i)
167 if(gui_wps[i].data->peak_meter_enabled)
168 pm = true;
171 if (pm) {
172 long next_refresh = current_tick;
173 long next_big_refresh = current_tick + HZ / 5;
174 button = BUTTON_NONE;
175 while (TIME_BEFORE(current_tick, next_big_refresh)) {
176 button = get_action(CONTEXT_WPS|ALLOW_SOFTLOCK,TIMEOUT_NOBLOCK);
177 if (button != ACTION_NONE) {
178 break;
180 peak_meter_peek();
181 sleep(0); /* Sleep until end of current tick. */
183 if (TIME_AFTER(current_tick, next_refresh)) {
184 FOR_NB_SCREENS(i)
186 if(gui_wps[i].data->peak_meter_enabled)
187 gui_wps_refresh(&gui_wps[i], 0,
188 WPS_REFRESH_PEAK_METER);
189 next_refresh += HZ / PEAK_METER_FPS;
196 /* The peak meter is disabled
197 -> no additional screen updates needed */
198 else {
199 button = get_action(CONTEXT_WPS|ALLOW_SOFTLOCK,HZ/5);
201 #else
202 button = get_action(CONTEXT_WPS|ALLOW_SOFTLOCK,HZ/5);
203 #endif
205 /* Exit if audio has stopped playing. This can happen if using the
206 sleep timer with the charger plugged or if starting a recording
207 from F1 */
208 if (!audio_status())
209 exit = true;
210 #ifdef ACTION_WPSAB_SINGLE
211 if (!global_settings.party_mode && ab_repeat_mode_enabled())
213 static int wps_ab_state = 0;
214 if (button == ACTION_WPSAB_SINGLE)
216 switch (wps_ab_state)
218 case 0: /* set the A spot */
219 button = ACTION_WPS_ABSETA_PREVDIR;
220 break;
221 case 1: /* set the B spot */
222 button = ACTION_WPS_ABSETB_NEXTDIR;
223 break;
224 case 2:
225 button = ACTION_WPS_ABRESET;
226 break;
228 wps_ab_state = (wps_ab_state+1) % 3;
231 #endif
232 switch(button)
234 case ACTION_WPS_CONTEXT:
235 #if LCD_DEPTH > 1
236 show_main_backdrop();
237 #endif
238 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
239 show_remote_main_backdrop();
240 #endif
241 /* if music is stopped in the context menu we want to exit the wps */
242 if (onplay(wps_state.id3->path,
243 FILE_ATTR_AUDIO, CONTEXT_WPS) == ONPLAY_MAINMENU
244 || !audio_status())
245 return GO_TO_ROOT;
247 /* track might have changed */
248 update_track = true;
250 #if LCD_DEPTH > 1
251 show_wps_backdrop();
252 #endif
253 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
254 show_remote_wps_backdrop();
255 #endif
256 #ifdef HAVE_LCD_BITMAP
257 FOR_NB_SCREENS(i)
259 gui_wps_set_margin(&gui_wps[i]);
261 #endif
262 restore = true;
263 break;
265 case ACTION_WPS_BROWSE:
266 #ifdef HAVE_LCD_CHARCELLS
267 status_set_record(false);
268 status_set_audio(false);
269 #endif
270 FOR_NB_SCREENS(i)
271 gui_wps[i].display->stop_scroll();
272 return GO_TO_PREVIOUS_BROWSER;
273 break;
275 /* play/pause */
276 case ACTION_WPS_PLAY:
277 if (global_settings.party_mode)
278 break;
279 if ( wps_state.paused )
281 wps_state.paused = false;
282 if ( global_settings.fade_on_stop )
283 fade(1);
284 else
285 audio_resume();
287 else
289 wps_state.paused = true;
290 if ( global_settings.fade_on_stop )
291 fade(0);
292 else
293 audio_pause();
294 settings_save();
295 #if !defined(HAVE_RTC_RAM) && !defined(HAVE_SW_POWEROFF)
296 call_ata_idle_notifys(true); /* make sure resume info is saved */
297 #endif
299 break;
301 /* volume up */
302 case ACTION_WPS_VOLUP:
304 global_settings.volume++;
305 bool res = false;
306 setvol();
307 FOR_NB_SCREENS(i)
309 if(update_onvol_change(&gui_wps[i]))
310 res = true;
312 if (res) {
313 restore = true;
314 restoretimer = current_tick + HZ;
317 break;
319 /* volume down */
320 case ACTION_WPS_VOLDOWN:
322 global_settings.volume--;
323 setvol();
324 bool res = false;
325 FOR_NB_SCREENS(i)
327 if(update_onvol_change(&gui_wps[i]))
328 res = true;
330 if (res) {
331 restore = true;
332 restoretimer = current_tick + HZ;
335 break;
336 /* fast forward
337 OR next dir if this is straight after ACTION_WPS_SKIPNEXT */
338 case ACTION_WPS_SEEKFWD:
339 if (global_settings.party_mode)
340 break;
341 if (current_tick -last_right < HZ)
343 if (cuesheet_is_enabled() && wps_state.id3->cuesheet_type)
345 audio_next();
347 else
349 audio_next_dir();
352 else ffwd_rew(ACTION_WPS_SEEKFWD);
353 last_right = 0;
354 break;
355 /* fast rewind
356 OR prev dir if this is straight after ACTION_WPS_SKIPPREV */
357 case ACTION_WPS_SEEKBACK:
358 if (global_settings.party_mode)
359 break;
360 if (current_tick -last_left < HZ)
362 if (cuesheet_is_enabled() && wps_state.id3->cuesheet_type)
364 if (!wps_state.paused)
365 #if (CONFIG_CODEC == SWCODEC)
366 audio_pre_ff_rewind();
367 #else
368 audio_pause();
369 #endif
370 audio_ff_rewind(0);
372 else
374 audio_prev_dir();
377 else ffwd_rew(ACTION_WPS_SEEKBACK);
378 last_left = 0;
379 break;
381 /* prev / restart */
382 case ACTION_WPS_SKIPPREV:
383 if (global_settings.party_mode)
384 break;
385 last_left = current_tick;
386 update_track = true;
388 #ifdef AB_REPEAT_ENABLE
389 /* if we're in A/B repeat mode and the current position
390 is past the A marker, jump back to the A marker... */
391 if ( ab_repeat_mode_enabled() )
393 if ( ab_after_A_marker(wps_state.id3->elapsed) )
395 ab_jump_to_A_marker();
396 break;
397 #if (AB_REPEAT_ENABLE == 2)
398 } else {
399 ab_reset_markers();
400 #endif
403 /* ...otherwise, do it normally */
404 #endif
406 if (!wps_state.id3 || (wps_state.id3->elapsed < 3*1000)) {
407 audio_prev();
409 else {
411 if (cuesheet_is_enabled() && wps_state.id3->cuesheet_type)
413 curr_cuesheet_skip(-1, wps_state.id3->elapsed);
414 break;
417 if (!wps_state.paused)
418 #if (CONFIG_CODEC == SWCODEC)
419 audio_pre_ff_rewind();
420 #else
421 audio_pause();
422 #endif
424 audio_ff_rewind(0);
426 #if (CONFIG_CODEC != SWCODEC)
427 if (!wps_state.paused)
428 audio_resume();
429 #endif
431 break;
433 /* next */
434 case ACTION_WPS_SKIPNEXT:
435 if (global_settings.party_mode)
436 break;
437 last_right = current_tick;
438 update_track = true;
440 #ifdef AB_REPEAT_ENABLE
441 /* if we're in A/B repeat mode and the current position is
442 before the A marker, jump to the A marker... */
443 if ( ab_repeat_mode_enabled() )
445 if ( ab_before_A_marker(wps_state.id3->elapsed) )
447 ab_jump_to_A_marker();
448 break;
449 #if (AB_REPEAT_ENABLE == 2)
450 } else {
451 ab_reset_markers();
452 #endif
455 /* ...otherwise, do it normally */
456 #endif
458 /* take care of if we're playing a cuesheet */
459 if (cuesheet_is_enabled() && wps_state.id3->cuesheet_type)
461 if (curr_cuesheet_skip(1, wps_state.id3->elapsed))
463 /* if the result was false, then we really want
464 to skip to the next track */
465 break;
469 audio_next();
470 break;
471 /* next / prev directories */
472 /* and set A-B markers if in a-b mode */
473 case ACTION_WPS_ABSETB_NEXTDIR:
474 if (global_settings.party_mode)
475 break;
476 #if defined(AB_REPEAT_ENABLE)
477 if (ab_repeat_mode_enabled())
479 ab_set_B_marker(wps_state.id3->elapsed);
480 ab_jump_to_A_marker();
481 update_track = true;
483 else
484 #endif
486 audio_next_dir();
488 break;
489 case ACTION_WPS_ABSETA_PREVDIR:
490 if (global_settings.party_mode)
491 break;
492 #if defined(AB_REPEAT_ENABLE)
493 if (ab_repeat_mode_enabled())
494 ab_set_A_marker(wps_state.id3->elapsed);
495 else
496 #endif
498 audio_prev_dir();
500 break;
501 /* menu key functions */
502 case ACTION_WPS_MENU:
503 FOR_NB_SCREENS(i)
504 gui_wps[i].display->stop_scroll();
505 return GO_TO_ROOT;
506 break;
509 #ifdef HAVE_QUICKSCREEN
510 case ACTION_WPS_QUICKSCREEN:
511 #if LCD_DEPTH > 1
512 show_main_backdrop();
513 #endif
514 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
515 show_remote_main_backdrop();
516 #endif
517 if (quick_screen_quick(button))
518 return SYS_USB_CONNECTED;
519 #if LCD_DEPTH > 1
520 show_wps_backdrop();
521 #endif
522 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
523 show_remote_wps_backdrop();
524 #endif
525 #ifdef HAVE_LCD_BITMAP
526 FOR_NB_SCREENS(i)
528 gui_wps_set_margin(&gui_wps[i]);
530 #endif
531 restore = true;
532 break;
533 #endif /* HAVE_QUICKSCREEN */
535 /* screen settings */
536 #ifdef BUTTON_F3
537 case ACTION_F3:
538 #if LCD_DEPTH > 1
539 show_main_backdrop();
540 #endif
541 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
542 show_remote_main_backdrop();
543 #endif
544 if (quick_screen_f3(BUTTON_F3))
545 return SYS_USB_CONNECTED;
546 #ifdef HAVE_LCD_BITMAP
547 FOR_NB_SCREENS(i)
549 gui_wps_set_margin(&gui_wps[i]);
551 #endif
552 restore = true;
553 break;
554 #endif /* BUTTON_F3 */
556 /* pitch screen */
557 #ifdef HAVE_PITCHSCREEN
558 case ACTION_WPS_PITCHSCREEN:
559 #if LCD_DEPTH > 1
560 show_main_backdrop();
561 #endif
562 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
563 show_remote_main_backdrop();
564 #endif
565 if (1 == pitch_screen())
566 return SYS_USB_CONNECTED;
567 #if LCD_DEPTH > 1
568 show_wps_backdrop();
569 #endif
570 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
571 show_remote_wps_backdrop();
572 #endif
573 restore = true;
574 break;
575 #endif /* HAVE_PITCHSCREEN */
577 #ifdef AB_REPEAT_ENABLE
578 /* reset A&B markers */
579 case ACTION_WPS_ABRESET:
580 if (ab_repeat_mode_enabled())
582 ab_reset_markers();
583 update_track = true;
585 break;
586 #endif /* AB_REPEAT_ENABLE */
588 /* stop and exit wps */
589 case ACTION_WPS_STOP:
590 if (global_settings.party_mode)
591 break;
592 bookmark = true;
593 exit = true;
594 break;
596 case ACTION_WPS_ID3SCREEN:
597 #if LCD_DEPTH > 1
598 show_main_backdrop();
599 #endif
600 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
601 show_remote_main_backdrop();
602 #endif
603 browse_id3();
604 #if LCD_DEPTH > 1
605 show_wps_backdrop();
606 #endif
607 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
608 show_remote_wps_backdrop();
609 #endif
610 #ifdef HAVE_LCD_BITMAP
611 FOR_NB_SCREENS(i)
613 gui_wps_set_margin(&gui_wps[i]);
615 #endif
616 restore = true;
617 break;
619 case ACTION_REDRAW: /* yes are locked, just redraw */
620 restore = true;
621 break;
622 case ACTION_NONE: /* Timeout */
623 update_track = true;
624 ffwd_rew(button); /* hopefully fix the ffw/rwd bug */
625 break;
626 #ifdef HAVE_RECORDING
627 case ACTION_WPS_REC:
628 exit = true;
629 break;
630 #endif
631 case SYS_POWEROFF:
632 #if LCD_DEPTH > 1
633 show_main_backdrop();
634 #endif
635 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
636 show_remote_main_backdrop();
637 #endif
638 default_event_handler(SYS_POWEROFF);
639 break;
641 default:
642 if(default_event_handler(button) == SYS_USB_CONNECTED)
643 return GO_TO_ROOT;
644 update_track = true;
645 break;
648 if (update_track)
650 FOR_NB_SCREENS(i)
652 if(update(&gui_wps[i]))
653 exit = true;
655 update_track = false;
658 if (restore &&
659 ((restoretimer == 0) ||
660 (restoretimer < current_tick)))
662 restore = false;
663 restoretimer = 0;
664 if (gui_wps_display()) {
665 exit = true;
667 else if (wps_state.id3){
668 FOR_NB_SCREENS(i)
669 gui_wps_refresh(&gui_wps[i], 0, WPS_REFRESH_NON_STATIC);
673 if (exit) {
674 #ifdef HAVE_LCD_CHARCELLS
675 status_set_record(false);
676 status_set_audio(false);
677 #endif
678 if (global_settings.fade_on_stop)
679 fade(0);
681 FOR_NB_SCREENS(i)
682 gui_wps[i].display->stop_scroll();
683 if (bookmark)
684 bookmark_autobookmark();
685 audio_stop();
686 #ifdef AB_REPEAT_ENABLE
687 ab_reset_markers();
688 #endif
689 #ifdef HAVE_RECORDING
690 if (button == ACTION_WPS_REC)
691 return GO_TO_RECSCREEN;
692 #endif
693 if (global_settings.browse_current)
694 return GO_TO_PREVIOUS_BROWSER;
695 return GO_TO_PREVIOUS;
698 if ( button )
699 ata_spin();
701 return GO_TO_ROOT; /* unreachable - just to reduce compiler warnings */
704 /* needs checking if needed end*/
706 /* wps_state */
708 static void wps_state_init(void)
710 wps_state.ff_rewind = false;
711 wps_state.paused = false;
712 wps_state.id3 = NULL;
713 wps_state.nid3 = NULL;
714 wps_state.current_track_path[0] = '\0';
717 #if 0
718 /* these are obviously not used? */
720 void wps_state_update_ff_rew(bool ff_rew)
722 wps_state.ff_rewind = ff_rew;
725 void wps_state_update_paused(bool paused)
727 wps_state.paused = paused;
729 void wps_state_update_id3_nid3(struct mp3entry *id3, struct mp3entry *nid3)
731 wps_state.id3 = id3;
732 wps_state.nid3 = nid3;
734 #endif
736 static void wps_state_update_ctp(const char *path)
738 strncpy(wps_state.current_track_path, path,
739 sizeof(wps_state.current_track_path));
740 wps_state.current_track_path[sizeof(wps_state.current_track_path)-1] = '\0';
742 /* wps_state end*/
744 /* initial setup of a wps */
745 static void gui_wps_init(struct gui_wps *gui_wps)
747 gui_wps->data = NULL;
748 gui_wps->display = NULL;
749 gui_wps->statusbar = NULL;
750 /* Currently no seperate wps_state needed/possible
751 so use the only aviable ( "global" ) one */
752 gui_wps->state = &wps_state;
755 /* connects a wps with a format-description of the displayed content */
756 static void gui_wps_set_data(struct gui_wps *gui_wps, struct wps_data *data)
758 gui_wps->data = data;
761 /* connects a wps with a screen */
762 static void gui_wps_set_disp(struct gui_wps *gui_wps, struct screen *display)
764 gui_wps->display = display;
767 static void gui_wps_set_statusbar(struct gui_wps *gui_wps, struct gui_statusbar *statusbar)
769 gui_wps->statusbar = statusbar;
771 /* gui_wps end */
773 void gui_sync_wps_screen_init(void)
775 int i;
776 FOR_NB_SCREENS(i)
777 gui_wps_set_disp(&gui_wps[i], &screens[i]);
780 void gui_sync_wps_init(void)
782 int i;
783 FOR_NB_SCREENS(i)
785 wps_data_init(&wps_datas[i]);
786 #ifdef HAVE_REMOTE_LCD
787 wps_datas[i].remote_wps = (i != 0);
788 #endif
789 gui_wps_init(&gui_wps[i]);
790 gui_wps_set_data(&gui_wps[i], &wps_datas[i]);
791 gui_wps_set_statusbar(&gui_wps[i], &statusbars.statusbars[i]);
793 #if LCD_DEPTH > 1
794 unload_wps_backdrop();
795 #endif
796 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
797 unload_remote_wps_backdrop();
798 #endif
801 #ifdef HAVE_ALBUMART
802 /* Returns true if at least one of the gui_wps screens has an album art
803 tag in its wps structure */
804 bool gui_sync_wps_uses_albumart(void)
806 int i;
807 FOR_NB_SCREENS(i) {
808 struct gui_wps *gwps = &gui_wps[i];
809 if (gwps->data && (gwps->data->wps_uses_albumart != WPS_ALBUMART_NONE))
810 return true;
812 return false;
814 #endif