FS#9051 - remove LCD margins... use viewports if you need them...
[Rockbox.git] / apps / gui / gwps.c
blob31b19897863db08d159a2f894f4bebd3fbac17f0
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"
60 #include "quickscreen.h"
62 /* currently only on wps_state is needed */
63 struct wps_state wps_state;
64 struct gui_wps gui_wps[NB_SCREENS];
65 static struct wps_data wps_datas[NB_SCREENS];
67 /* change the path to the current played track */
68 static void wps_state_update_ctp(const char *path);
69 /* initial setup of wps_data */
70 static void wps_state_init(void);
71 /* initial setup of a wps */
72 static void gui_wps_init(struct gui_wps *gui_wps);
73 /* connects a wps with a format-description of the displayed content */
74 static void gui_wps_set_data(struct gui_wps *gui_wps, struct wps_data *data);
75 /* connects a wps with a screen */
76 static void gui_wps_set_disp(struct gui_wps *gui_wps, struct screen *display);
77 /* connects a wps with a statusbar*/
78 static void gui_wps_set_statusbar(struct gui_wps *gui_wps, struct gui_statusbar *statusbar);
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();
125 long gui_wps_show(void)
127 long button = 0;
128 bool restore = false;
129 long restoretimer = 0; /* timer to delay screen redraw temporarily */
130 bool exit = false;
131 bool bookmark = false;
132 bool update_track = false;
133 int i;
134 long last_left = 0, last_right = 0;
136 wps_state_init();
138 #ifdef HAVE_LCD_CHARCELLS
139 status_set_audio(true);
140 status_set_param(false);
141 #else
142 #if LCD_DEPTH > 1
143 show_wps_backdrop();
144 #endif /* LCD_DEPTH > 1 */
145 #endif
147 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
148 show_remote_wps_backdrop();
149 #endif
151 #ifdef AB_REPEAT_ENABLE
152 ab_repeat_init();
153 ab_reset_markers();
154 #endif
156 if(audio_status() & AUDIO_STATUS_PLAY)
158 wps_state.id3 = audio_current_track();
159 wps_state.nid3 = audio_next_track();
160 if (wps_state.id3) {
161 if (gui_wps_display())
162 return 0;
163 FOR_NB_SCREENS(i)
164 gui_wps_refresh(&gui_wps[i], 0, WPS_REFRESH_ALL);
165 wps_state_update_ctp(wps_state.id3->path);
168 restore = true;
170 while ( 1 )
172 bool audio_paused = (audio_status() & AUDIO_STATUS_PAUSE)?true:false;
174 /* did someone else (i.e power thread) change audio pause mode? */
175 if (wps_state.paused != audio_paused) {
176 wps_state.paused = audio_paused;
178 /* if another thread paused audio, we are probably in car mode,
179 about to shut down. lets save the settings. */
180 if (wps_state.paused) {
181 settings_save();
182 #if !defined(HAVE_RTC_RAM) && !defined(HAVE_SW_POWEROFF)
183 call_ata_idle_notifys(true);
184 #endif
188 #ifdef HAVE_LCD_BITMAP
189 /* when the peak meter is enabled we want to have a
190 few extra updates to make it look smooth. On the
191 other hand we don't want to waste energy if it
192 isn't displayed */
193 bool pm=false;
194 FOR_NB_SCREENS(i)
196 if(gui_wps[i].data->peak_meter_enabled)
197 pm = true;
200 if (pm) {
201 long next_refresh = current_tick;
202 long next_big_refresh = current_tick + HZ / 5;
203 button = BUTTON_NONE;
204 while (TIME_BEFORE(current_tick, next_big_refresh)) {
205 button = get_action(CONTEXT_WPS|ALLOW_SOFTLOCK,TIMEOUT_NOBLOCK);
206 if (button != ACTION_NONE) {
207 break;
209 peak_meter_peek();
210 sleep(0); /* Sleep until end of current tick. */
212 if (TIME_AFTER(current_tick, next_refresh)) {
213 FOR_NB_SCREENS(i)
215 if(gui_wps[i].data->peak_meter_enabled)
216 gui_wps_refresh(&gui_wps[i], 0,
217 WPS_REFRESH_PEAK_METER);
218 next_refresh += HZ / PEAK_METER_FPS;
225 /* The peak meter is disabled
226 -> no additional screen updates needed */
227 else {
228 button = get_action(CONTEXT_WPS|ALLOW_SOFTLOCK,HZ/5);
230 #else
231 button = get_action(CONTEXT_WPS|ALLOW_SOFTLOCK,HZ/5);
232 #endif
234 /* Exit if audio has stopped playing. This can happen if using the
235 sleep timer with the charger plugged or if starting a recording
236 from F1 */
237 if (!audio_status())
238 exit = true;
239 #ifdef ACTION_WPSAB_SINGLE
240 if (!global_settings.party_mode && ab_repeat_mode_enabled())
242 static int wps_ab_state = 0;
243 if (button == ACTION_WPSAB_SINGLE)
245 switch (wps_ab_state)
247 case 0: /* set the A spot */
248 button = ACTION_WPS_ABSETA_PREVDIR;
249 break;
250 case 1: /* set the B spot */
251 button = ACTION_WPS_ABSETB_NEXTDIR;
252 break;
253 case 2:
254 button = ACTION_WPS_ABRESET;
255 break;
257 wps_ab_state = (wps_ab_state+1) % 3;
260 #endif
261 switch(button)
263 case ACTION_WPS_CONTEXT:
264 #if LCD_DEPTH > 1
265 show_main_backdrop();
266 #endif
267 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
268 show_remote_main_backdrop();
269 #endif
270 /* if music is stopped in the context menu we want to exit the wps */
271 if (onplay(wps_state.id3->path,
272 FILE_ATTR_AUDIO, CONTEXT_WPS) == ONPLAY_MAINMENU
273 || !audio_status())
274 return GO_TO_ROOT;
276 /* track might have changed */
277 update_track = true;
279 #if LCD_DEPTH > 1
280 show_wps_backdrop();
281 #endif
282 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
283 show_remote_wps_backdrop();
284 #endif
285 restore = true;
286 break;
288 case ACTION_WPS_BROWSE:
289 #ifdef HAVE_LCD_CHARCELLS
290 status_set_record(false);
291 status_set_audio(false);
292 #endif
293 FOR_NB_SCREENS(i)
294 gui_wps[i].display->stop_scroll();
295 return GO_TO_PREVIOUS_BROWSER;
296 break;
298 /* play/pause */
299 case ACTION_WPS_PLAY:
300 if (global_settings.party_mode)
301 break;
302 if ( wps_state.paused )
304 wps_state.paused = false;
305 if ( global_settings.fade_on_stop )
306 fade(true, true);
307 else
308 audio_resume();
310 else
312 wps_state.paused = true;
313 if ( global_settings.fade_on_stop )
314 fade(false, true);
315 else
316 audio_pause();
317 settings_save();
318 #if !defined(HAVE_RTC_RAM) && !defined(HAVE_SW_POWEROFF)
319 call_ata_idle_notifys(true); /* make sure resume info is saved */
320 #endif
322 break;
324 /* volume up */
325 case ACTION_WPS_VOLUP:
327 global_settings.volume++;
328 bool res = false;
329 setvol();
330 FOR_NB_SCREENS(i)
332 if(update_onvol_change(&gui_wps[i]))
333 res = true;
335 if (res) {
336 restore = true;
337 restoretimer = current_tick + HZ;
340 break;
342 /* volume down */
343 case ACTION_WPS_VOLDOWN:
345 global_settings.volume--;
346 setvol();
347 bool res = false;
348 FOR_NB_SCREENS(i)
350 if(update_onvol_change(&gui_wps[i]))
351 res = true;
353 if (res) {
354 restore = true;
355 restoretimer = current_tick + HZ;
358 break;
359 /* fast forward
360 OR next dir if this is straight after ACTION_WPS_SKIPNEXT
361 OR in study mode, next track if straight after SKIPPREV. */
362 case ACTION_WPS_SEEKFWD:
363 if (global_settings.party_mode)
364 break;
365 if (!global_settings.study_mode
366 && current_tick -last_right < HZ)
368 if (cuesheet_is_enabled() && wps_state.id3->cuesheet_type)
370 audio_next();
372 else
374 audio_next_dir();
377 else if(global_settings.study_mode
378 && current_tick -last_left < HZ) {
379 next_track();
380 update_track = true;
382 else ffwd_rew(ACTION_WPS_SEEKFWD);
383 last_right = last_left = 0;
384 break;
385 /* fast rewind
386 OR prev dir if this is straight after ACTION_WPS_SKIPPREV,
387 OR in study mode, beg of track or prev track if this is
388 straight after SKIPPREV */
389 case ACTION_WPS_SEEKBACK:
390 if (global_settings.party_mode)
391 break;
392 if (!global_settings.study_mode
393 && current_tick -last_left < HZ)
395 if (cuesheet_is_enabled() && wps_state.id3->cuesheet_type)
397 if (!wps_state.paused)
398 #if (CONFIG_CODEC == SWCODEC)
399 audio_pre_ff_rewind();
400 #else
401 audio_pause();
402 #endif
403 audio_ff_rewind(0);
405 else
407 audio_prev_dir();
410 else if(global_settings.study_mode
411 && current_tick -last_right < HZ)
413 prev_track(3+global_settings.study_hop_step);
414 update_track = true;
416 else ffwd_rew(ACTION_WPS_SEEKBACK);
417 last_left = last_right = 0;
418 break;
420 /* prev / restart */
421 case ACTION_WPS_SKIPPREV:
422 if (global_settings.party_mode)
423 break;
424 last_left = current_tick;
425 update_track = true;
427 #ifdef AB_REPEAT_ENABLE
428 /* if we're in A/B repeat mode and the current position
429 is past the A marker, jump back to the A marker... */
430 if ( ab_repeat_mode_enabled() )
432 if ( ab_after_A_marker(wps_state.id3->elapsed) )
434 ab_jump_to_A_marker();
435 break;
436 #if (AB_REPEAT_ENABLE == 2)
437 } else {
438 ab_reset_markers();
439 #endif
442 /* ...otherwise, do it normally */
443 #endif
445 if(global_settings.study_mode)
446 play_hop(-1);
447 else prev_track(3);
448 break;
450 /* next
451 OR in study mode, hop by predetermined amount. */
452 case ACTION_WPS_SKIPNEXT:
453 if (global_settings.party_mode)
454 break;
455 last_right = current_tick;
456 update_track = true;
458 #ifdef AB_REPEAT_ENABLE
459 /* if we're in A/B repeat mode and the current position is
460 before the A marker, jump to the A marker... */
461 if ( ab_repeat_mode_enabled() )
463 if ( ab_before_A_marker(wps_state.id3->elapsed) )
465 ab_jump_to_A_marker();
466 break;
467 #if (AB_REPEAT_ENABLE == 2)
468 } else {
469 ab_reset_markers();
470 #endif
473 /* ...otherwise, do it normally */
474 #endif
476 if(global_settings.study_mode)
477 play_hop(1);
478 else next_track();
479 break;
480 /* next / prev directories */
481 /* and set A-B markers if in a-b mode */
482 case ACTION_WPS_ABSETB_NEXTDIR:
483 if (global_settings.party_mode)
484 break;
485 #if defined(AB_REPEAT_ENABLE)
486 if (ab_repeat_mode_enabled())
488 ab_set_B_marker(wps_state.id3->elapsed);
489 ab_jump_to_A_marker();
490 update_track = true;
492 else
493 #endif
495 if(global_settings.study_mode)
496 next_track();
497 else audio_next_dir();
499 break;
500 case ACTION_WPS_ABSETA_PREVDIR:
501 if (global_settings.party_mode)
502 break;
503 #if defined(AB_REPEAT_ENABLE)
504 if (ab_repeat_mode_enabled())
505 ab_set_A_marker(wps_state.id3->elapsed);
506 else
507 #endif
509 if(global_settings.study_mode)
510 prev_track(3);
511 else audio_prev_dir();
513 break;
514 /* menu key functions */
515 case ACTION_WPS_MENU:
516 FOR_NB_SCREENS(i)
517 gui_wps[i].display->stop_scroll();
518 return GO_TO_ROOT;
519 break;
522 #ifdef HAVE_QUICKSCREEN
523 case ACTION_WPS_QUICKSCREEN:
524 #if LCD_DEPTH > 1
525 show_main_backdrop();
526 #endif
527 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
528 show_remote_main_backdrop();
529 #endif
530 if (quick_screen_quick(button))
531 return SYS_USB_CONNECTED;
532 #if LCD_DEPTH > 1
533 show_wps_backdrop();
534 #endif
535 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
536 show_remote_wps_backdrop();
537 #endif
538 restore = true;
539 break;
540 #endif /* HAVE_QUICKSCREEN */
542 /* screen settings */
543 #ifdef BUTTON_F3
544 case ACTION_F3:
545 #if LCD_DEPTH > 1
546 show_main_backdrop();
547 #endif
548 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
549 show_remote_main_backdrop();
550 #endif
551 if (quick_screen_f3(BUTTON_F3))
552 return SYS_USB_CONNECTED;
553 restore = true;
554 break;
555 #endif /* BUTTON_F3 */
557 /* pitch screen */
558 #ifdef HAVE_PITCHSCREEN
559 case ACTION_WPS_PITCHSCREEN:
560 #if LCD_DEPTH > 1
561 show_main_backdrop();
562 #endif
563 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
564 show_remote_main_backdrop();
565 #endif
566 if (1 == pitch_screen())
567 return SYS_USB_CONNECTED;
568 #if LCD_DEPTH > 1
569 show_wps_backdrop();
570 #endif
571 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
572 show_remote_wps_backdrop();
573 #endif
574 restore = true;
575 break;
576 #endif /* HAVE_PITCHSCREEN */
578 #ifdef AB_REPEAT_ENABLE
579 /* reset A&B markers */
580 case ACTION_WPS_ABRESET:
581 if (ab_repeat_mode_enabled())
583 ab_reset_markers();
584 update_track = true;
586 break;
587 #endif /* AB_REPEAT_ENABLE */
589 /* stop and exit wps */
590 case ACTION_WPS_STOP:
591 if (global_settings.party_mode)
592 break;
593 bookmark = true;
594 exit = true;
595 break;
597 case ACTION_WPS_ID3SCREEN:
598 #if LCD_DEPTH > 1
599 show_main_backdrop();
600 #endif
601 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
602 show_remote_main_backdrop();
603 #endif
604 browse_id3();
605 #if LCD_DEPTH > 1
606 show_wps_backdrop();
607 #endif
608 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
609 show_remote_wps_backdrop();
610 #endif
611 restore = true;
612 break;
614 case ACTION_REDRAW: /* yes are locked, just redraw */
615 restore = true;
616 break;
617 case ACTION_NONE: /* Timeout */
618 update_track = true;
619 ffwd_rew(button); /* hopefully fix the ffw/rwd bug */
620 break;
621 #ifdef HAVE_RECORDING
622 case ACTION_WPS_REC:
623 exit = true;
624 break;
625 #endif
626 case SYS_POWEROFF:
627 #if LCD_DEPTH > 1
628 show_main_backdrop();
629 #endif
630 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
631 show_remote_main_backdrop();
632 #endif
633 default_event_handler(SYS_POWEROFF);
634 break;
636 default:
637 if(default_event_handler(button) == SYS_USB_CONNECTED)
638 return GO_TO_ROOT;
639 update_track = true;
640 break;
643 if (update_track)
645 FOR_NB_SCREENS(i)
647 if(update(&gui_wps[i]))
648 exit = true;
650 update_track = false;
653 if (restore &&
654 ((restoretimer == 0) ||
655 (restoretimer < current_tick)))
657 restore = false;
658 restoretimer = 0;
659 if (gui_wps_display()) {
660 exit = true;
662 else if (wps_state.id3){
663 FOR_NB_SCREENS(i)
664 gui_wps_refresh(&gui_wps[i], 0, WPS_REFRESH_NON_STATIC);
668 if (exit) {
669 #ifdef HAVE_LCD_CHARCELLS
670 status_set_record(false);
671 status_set_audio(false);
672 #endif
673 if (global_settings.fade_on_stop)
674 fade(false, true);
676 FOR_NB_SCREENS(i)
677 gui_wps[i].display->stop_scroll();
678 if (bookmark)
679 bookmark_autobookmark();
680 audio_stop();
681 #ifdef AB_REPEAT_ENABLE
682 ab_reset_markers();
683 #endif
684 #ifdef HAVE_RECORDING
685 if (button == ACTION_WPS_REC)
686 return GO_TO_RECSCREEN;
687 #endif
688 if (global_settings.browse_current)
689 return GO_TO_PREVIOUS_BROWSER;
690 return GO_TO_PREVIOUS;
693 if ( button )
694 ata_spin();
696 return GO_TO_ROOT; /* unreachable - just to reduce compiler warnings */
699 /* needs checking if needed end*/
701 /* wps_state */
703 static void wps_state_init(void)
705 wps_state.ff_rewind = false;
706 wps_state.paused = false;
707 wps_state.id3 = NULL;
708 wps_state.nid3 = NULL;
709 wps_state.current_track_path[0] = '\0';
712 #if 0
713 /* these are obviously not used? */
715 void wps_state_update_ff_rew(bool ff_rew)
717 wps_state.ff_rewind = ff_rew;
720 void wps_state_update_paused(bool paused)
722 wps_state.paused = paused;
724 void wps_state_update_id3_nid3(struct mp3entry *id3, struct mp3entry *nid3)
726 wps_state.id3 = id3;
727 wps_state.nid3 = nid3;
729 #endif
731 static void wps_state_update_ctp(const char *path)
733 strncpy(wps_state.current_track_path, path,
734 sizeof(wps_state.current_track_path));
735 wps_state.current_track_path[sizeof(wps_state.current_track_path)-1] = '\0';
737 /* wps_state end*/
739 /* initial setup of a wps */
740 static void gui_wps_init(struct gui_wps *gui_wps)
742 gui_wps->data = NULL;
743 gui_wps->display = NULL;
744 gui_wps->statusbar = NULL;
745 /* Currently no seperate wps_state needed/possible
746 so use the only aviable ( "global" ) one */
747 gui_wps->state = &wps_state;
750 /* connects a wps with a format-description of the displayed content */
751 static void gui_wps_set_data(struct gui_wps *gui_wps, struct wps_data *data)
753 gui_wps->data = data;
756 /* connects a wps with a screen */
757 static void gui_wps_set_disp(struct gui_wps *gui_wps, struct screen *display)
759 gui_wps->display = display;
762 static void gui_wps_set_statusbar(struct gui_wps *gui_wps, struct gui_statusbar *statusbar)
764 gui_wps->statusbar = statusbar;
766 /* gui_wps end */
768 void gui_sync_wps_screen_init(void)
770 int i;
771 FOR_NB_SCREENS(i)
772 gui_wps_set_disp(&gui_wps[i], &screens[i]);
774 #ifdef HAVE_LCD_BITMAP
775 static void statusbar_toggle_handler(void *data)
777 (void)data;
778 int i;
779 bool draw = global_settings.statusbar;
781 FOR_NB_SCREENS(i)
783 struct wps_viewport *vp = &gui_wps[i].data->viewports[0];
784 if (gui_wps[i].data->wps_sb_tag)
785 draw = gui_wps[i].data->show_sb_on_wps;
786 if (!global_settings.statusbar && !draw)
788 vp->vp.y = 0;
789 vp->vp.height = screens[i].height;
791 else
793 vp->vp.y = STATUSBAR_HEIGHT;
794 vp->vp.height = screens[i].height - STATUSBAR_HEIGHT;
798 #endif
800 void gui_sync_wps_init(void)
802 int i;
803 FOR_NB_SCREENS(i)
805 wps_data_init(&wps_datas[i]);
806 #ifdef HAVE_REMOTE_LCD
807 wps_datas[i].remote_wps = (i != 0);
808 #endif
809 gui_wps_init(&gui_wps[i]);
810 gui_wps_set_data(&gui_wps[i], &wps_datas[i]);
811 gui_wps_set_statusbar(&gui_wps[i], &statusbars.statusbars[i]);
813 #ifdef HAVE_LCD_BITMAP
814 add_event(STATUSBAR_TOGGLE_EVENT, false, statusbar_toggle_handler);
815 #endif
816 #if LCD_DEPTH > 1
817 unload_wps_backdrop();
818 #endif
819 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
820 unload_remote_wps_backdrop();
821 #endif
824 #ifdef HAVE_ALBUMART
825 /* Returns true if at least one of the gui_wps screens has an album art
826 tag in its wps structure */
827 bool gui_sync_wps_uses_albumart(void)
829 int i;
830 FOR_NB_SCREENS(i) {
831 struct gui_wps *gwps = &gui_wps[i];
832 if (gwps->data && (gwps->data->wps_uses_albumart != WPS_ALBUMART_NONE))
833 return true;
835 return false;
837 #endif