Index: apps/recorder/recording.c =================================================================== --- apps/recorder/recording.c (revision 29159) +++ apps/recorder/recording.c (working copy) @@ -1591,6 +1591,14 @@ /* Only act if the mpeg is stopped */ if(!(audio_stat & AUDIO_STATUS_RECORD)) { + /* + If recording kicks off again, check and start the sleep timer + */ + if (global_settings.sleeptimer_on_startup == true) + { + set_sleep_timer(global_settings.sleeptimer_duration * 60); + } + /* is this manual or triggered recording? */ if ((global_settings.rec_trigger_mode == TRIG_MODE_OFF) || (peak_meter_trigger_status() != TRIG_OFF)) Index: apps/lang/english.lang =================================================================== --- apps/lang/english.lang (revision 29159) +++ apps/lang/english.lang (working copy) @@ -4425,7 +4425,7 @@ id: LANG_SLEEP_TIMER - desc: sleep timer setting + desc: sleep timer menu user: core *: "Sleep Timer" @@ -12713,3 +12713,73 @@ *: "Yes (requires initialized database)" + + id: LANG_SLEEP_TIMER_START + desc: start sleep timer + user: core + + *: "Start Timer" + + + *: "Start Timer" + + + *: "Start Timer" + + + + id: LANG_SLEEP_TIMER_STOP + desc: stop sleep timer + user: core + + *: "Stop Timer" + + + *: "Stop Timer" + + + *: "Stop Timer" + + + + id: LANG_SLEEP_TIMER_DURATION + desc: sleep timer duration in minutes + user: core + + *: "Timer Duration" + + + *: "Timer Duration" + + + *: "Timer Duration" + + + + id: LANG_SLEEP_TIMER_ON_POWER_UP + desc: whether sleep timer starts on power up + user: core + + *: "Start Timer On Power Up" + + + *: "Start Timer On Power Up" + + + *: "Start Timer On Power Up" + + + + id: LANG_SLEEP_TIMER_PAUSE_MODE + desc: sleep timer pauses rather than powers down + user: core + + *: "Timer Pause Mode" + + + *: "Timer Pause Mode" + + + *: "Timer Pause Mode" + + Index: apps/gui/wps.c =================================================================== --- apps/gui/wps.c (revision 29159) +++ apps/gui/wps.c (working copy) @@ -685,6 +685,7 @@ * In either way, call gwps_leave_wps(), in order to restore the correct * "main screen" backdrops and statusbars */ +extern void set_sleep_timer(int seconds); long gui_wps_show(void) { long button = 0; @@ -809,6 +810,12 @@ fade(true, true); else audio_resume(); + /* restart sleep timer if it is currently enabled for startup */ + if (global_settings.sleeptimer_on_startup == true) + { + set_sleep_timer(global_settings.sleeptimer_duration * 60); + } + } else { @@ -817,6 +824,9 @@ fade(false, true); else audio_pause(); + /* stopping sleep timer at any pause - sleeptimer_on_startup can be ignored here */ + set_sleep_timer(0); + settings_save(); #if !defined(HAVE_RTC_RAM) && !defined(HAVE_SW_POWEROFF) call_storage_idle_notifys(true); /* make sure resume info is saved */ Index: apps/settings.h =================================================================== --- apps/settings.h (revision 29159) +++ apps/settings.h (working copy) @@ -778,6 +778,10 @@ int compressor_release_time; #endif + int sleeptimer_duration; + bool sleeptimer_on_startup; + bool sleeptimer_pause_mode; + #ifdef HAVE_MORSE_INPUT bool morse_input; /* text input method setting */ #endif Index: apps/menus/exported_menus.h =================================================================== --- apps/menus/exported_menus.h (revision 29159) +++ apps/menus/exported_menus.h (working copy) @@ -41,7 +41,8 @@ #ifdef AUDIOHW_HAVE_EQ audiohw_eq_tone_controls, /* audiohw_eq_menu.c */ #endif - theme_menu; /* theme_menu.c */ + theme_menu, /* theme_menu.c */ + sleeptimer_menu; /* sleeptimer_menu.c */ struct browse_folder_info { const char* dir; Index: apps/menus/main_menu.c =================================================================== --- apps/menus/main_menu.c (revision 29159) +++ apps/menus/main_menu.c (working copy) @@ -392,46 +392,11 @@ MENUITEM_FUNCTION(show_info_item, 0, ID2P(LANG_ROCKBOX_INFO), (menu_function)show_info, NULL, NULL, Icon_NOICON); - -/* sleep Menu */ -static const char* sleep_timer_formatter(char* buffer, size_t buffer_size, - int value, const char* unit) -{ - (void) unit; - int minutes, hours; - - if (value) { - hours = value / 60; - minutes = value - (hours * 60); - snprintf(buffer, buffer_size, "%d:%02d", hours, minutes); - return buffer; - } else { - return str(LANG_OFF); - } -} - -static void sleep_timer_set(int minutes) -{ - set_sleep_timer(minutes * 60); -} - -static int sleep_timer(void) -{ - int minutes = (get_sleep_timer() + 59) / 60; /* round up */ - return (int)set_int(str(LANG_SLEEP_TIMER), "", UNIT_MIN, &minutes, - &sleep_timer_set, -5, 300, 0, sleep_timer_formatter); -} - - #if CONFIG_RTC int time_screen(void* ignored); MENUITEM_FUNCTION(timedate_item, MENU_FUNC_CHECK_RETVAL, ID2P(LANG_TIME_MENU), time_screen, NULL, NULL, Icon_Menu_setting ); #endif -/* This item is in the time/date screen if there is a RTC */ -MENUITEM_FUNCTION(sleep_timer_call, 0, ID2P(LANG_SLEEP_TIMER), sleep_timer, - NULL, NULL, Icon_Menu_setting); /* make it look like a - setting to the user */ MENUITEM_FUNCTION(show_credits_item, 0, ID2P(LANG_CREDITS), (menu_function)show_credits, NULL, NULL, Icon_NOICON); @@ -445,9 +410,6 @@ &timedate_item, #endif &show_info_item, &show_credits_item, &show_runtime_item, -#if CONFIG_RTC == 0 - &sleep_timer_call, -#endif &debug_menu_item); /* INFO MENU */ /***********************************/ Index: apps/menus/time_menu.c =================================================================== --- apps/menus/time_menu.c (revision 29159) +++ apps/menus/time_menu.c (working copy) @@ -81,8 +81,6 @@ timedate_set, NULL, NULL, Icon_NOICON); MENUITEM_SETTING(timeformat, &global_settings.timeformat, NULL); -/* in main_menu.c */ -extern const struct menu_item_ex sleep_timer_call; #ifdef HAVE_RTC_ALARM MENUITEM_FUNCTION(alarm_screen_call, 0, ID2P(LANG_ALARM_MOD_ALARM_MENU), @@ -239,7 +237,7 @@ MAKE_MENU(time_menu, ID2P(LANG_TIME_MENU), time_menu_callback, Icon_NOICON, - &time_set, &sleep_timer_call, + &time_set, #ifdef HAVE_RTC_ALARM &alarm_screen_call, #if defined(HAVE_RECORDING) || CONFIG_TUNER Index: apps/menus/settings_menu.c =================================================================== --- apps/menus/settings_menu.c (revision 29159) +++ apps/menus/settings_menu.c (working copy) @@ -330,6 +330,7 @@ &usb_hid, &usb_keypad_mode, #endif + &sleeptimer_menu, ); /* SYSTEM MENU */ Index: apps/menus/sleeptimer_menu.c =================================================================== --- apps/menus/sleeptimer_menu.c (revision 0) +++ apps/menus/sleeptimer_menu.c (revision 0) @@ -0,0 +1,96 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: sleep_timer_menu.c 23867 2009-12-05 11:24:50Z nickp $ + * + * Copyright (C) 2007 Jonathan Gordon + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include +#include +#include "config.h" +#include "string.h" +#include "lang.h" +#include "settings.h" +#include "menu.h" +#include "keyboard.h" +#include "exported_menus.h" +#include "powermgmt.h" +#include "action.h" + +static char* get_current_status(int selected_item, void * data, char *buffer) +{ + (void)selected_item; + (void)data; + int remain = get_sleep_timer(); + if (remain) + { + remain += 59; /* round up minutes */ + snprintf(buffer, MAX_PATH, "%s (%d:%02d)", + str(LANG_SLEEP_TIMER_STOP), + remain / 3600, + ( remain % 3600 ) / 60 + ); + } + else + { + remain = global_settings.sleeptimer_duration; + snprintf(buffer, MAX_PATH, "%s (%d:%02d)", + str(LANG_SLEEP_TIMER_START), + remain / 60, + remain % 60 + ); + } + return buffer; +} + +static int toggle_sleeptimer(void) +{ + if (get_sleep_timer()) + set_sleep_timer(0); + else + set_sleep_timer(global_settings.sleeptimer_duration * 60); + return 0; +} + +static int initial_duration; +static int sleeptimer_duration_cb(int action,const struct menu_item_ex *this_item) +{ + (void)this_item; + switch (action) + { + case ACTION_ENTER_MENUITEM: + initial_duration = global_settings.sleeptimer_duration; + break; + case ACTION_EXIT_MENUITEM: + if (initial_duration != global_settings.sleeptimer_duration && get_sleep_timer()) + set_sleep_timer(global_settings.sleeptimer_duration * 60); + } + return action; +} + +MENUITEM_FUNCTION_DYNTEXT(menu_toggle_sleeptimer, 0, toggle_sleeptimer, NULL, + get_current_status, NULL, NULL, NULL, Icon_NOICON); + +MENUITEM_SETTING(menu_sleeptimer_duration, &global_settings.sleeptimer_duration, + sleeptimer_duration_cb); + +MENUITEM_SETTING(menu_sleeptimer_on_startup, &global_settings.sleeptimer_on_startup, NULL); +MENUITEM_SETTING(menu_sleeptimer_pause_mode, &global_settings.sleeptimer_pause_mode, NULL); + +MAKE_MENU(sleeptimer_menu, ID2P(LANG_SLEEP_TIMER), NULL, Icon_NOICON, + &menu_toggle_sleeptimer, &menu_sleeptimer_duration, &menu_sleeptimer_on_startup, + &menu_sleeptimer_pause_mode); Index: apps/settings_list.c =================================================================== --- apps/settings_list.c (revision 29159) +++ apps/settings_list.c (working copy) @@ -532,6 +532,14 @@ return get_hotkey_lang_id(value); } #endif /* HAVE_HOTKEY */ +static const char* sleeptimer_formatter(char* buffer, size_t buffer_size, + int value, const char* unit) +{ + (void) unit; + snprintf(buffer, buffer_size, "%d:%02d", + value / 60, value % 60); + return buffer; +} const struct settings_list settings[] = { /* sound settings */ SOUND_SETTING(F_NO_WRAP,volume, LANG_VOLUME, "volume", SOUND_VOLUME), @@ -1702,6 +1710,10 @@ #endif /* CONFIG_CODEC == SWCODEC */ TEXT_SETTING(0, playlist_catalog_dir, "playlist catalog directory", PLAYLIST_CATALOG_DEFAULT_DIR, NULL, NULL), + INT_SETTING(0, sleeptimer_duration, LANG_SLEEP_TIMER_DURATION, 30, "sleeptimer duration", + UNIT_MIN, 300, 5, -5, sleeptimer_formatter, NULL, NULL), + OFFON_SETTING(0, sleeptimer_on_startup, LANG_SLEEP_TIMER_ON_POWER_UP, false, "sleeptimer on startup", NULL), + OFFON_SETTING(0, sleeptimer_pause_mode, LANG_SLEEP_TIMER_PAUSE_MODE, false, "sleeptimer pause mode", NULL), #ifdef HAVE_TOUCHPAD_SENSITIVITY_SETTING CHOICE_SETTING(0, touchpad_sensitivity, LANG_TOUCHPAD_SENSITIVITY, 0, "touchpad sensitivity", "normal,high", touchpad_set_sensitivity, 2, Index: apps/SOURCES =================================================================== --- apps/SOURCES (revision 29159) +++ apps/SOURCES (working copy) @@ -37,6 +37,7 @@ #if CONFIG_RTC menus/time_menu.c #endif +menus/sleeptimer_menu.c misc.c mp3data.c onplay.c Index: apps/root_menu.c =================================================================== --- apps/root_menu.c (revision 29159) +++ apps/root_menu.c (working copy) @@ -653,6 +653,11 @@ (global_settings.unplug_autoresume && !headphones_inserted() )) next_screen = GO_TO_ROOT; #endif + /* + * Restore sleeptimer if required + */ + if (global_settings.sleeptimer_on_startup) + set_sleep_timer(global_settings.sleeptimer_duration * 60); while (true) { Index: apps/main.c =================================================================== --- apps/main.c (revision 29159) +++ apps/main.c (working copy) @@ -359,6 +359,7 @@ #endif lang_init(core_language_builtin, language_strings, LANG_LAST_INDEX_IN_ARRAY); + powermgmt_init(); #ifdef DEBUG debug_init(); #endif Index: firmware/export/powermgmt.h =================================================================== --- firmware/export/powermgmt.h (revision 29159) +++ firmware/export/powermgmt.h (working copy) @@ -127,10 +127,11 @@ extern const unsigned short percent_to_volt_charge[11]; #endif +#endif /* PLATFORM_NATIVE */ + /* Start up power management thread */ void powermgmt_init(void) INIT_ATTR; -#endif /* PLATFORM_NATIVE */ /* Returns battery statust */ int battery_level(void); /* percent */ Index: firmware/target/hosted/sdl/system-sdl.c =================================================================== --- firmware/target/hosted/sdl/system-sdl.c (revision 29159) +++ firmware/target/hosted/sdl/system-sdl.c (working copy) @@ -65,10 +65,9 @@ int wps_verbose_level = 3; -void sys_poweroff(void) -{ -} +extern void sys_poweroff(void); + /* * This thread will read the buttons in an interrupt like fashion, and * also initializes SDL_INIT_VIDEO and the surfaces Index: firmware/powermgmt.c =================================================================== --- firmware/powermgmt.c (revision 29159) +++ firmware/powermgmt.c (working copy) @@ -55,6 +55,7 @@ && !defined (SIMULATOR) #include "pcf50606.h" #endif +#include "settings.h" /** Shared by sim **/ int last_sent_battery_level = 100; @@ -354,8 +355,22 @@ else if (sleeptimer_active) { /* Handle sleeptimer */ if (TIME_AFTER(tick, sleeptimer_endtick)) { - audio_stop(); + if (global_settings.sleeptimer_pause_mode == true) { + if ((audio_stat & AUDIO_STATUS_RECORD) || (audio_stat & AUDIO_STATUS_PRERECORD)) { + audio_pause_recording(); + } + if (get_radio_status() & FMRADIO_PLAYING) { + radio_pause(); + } + + if (audio_stat & AUDIO_STATUS_PLAY) { + audio_pause(); + } + sleeptimer_active = false; + } else { + audio_stop(); + } if (usb_inserted() #if CONFIG_CHARGING && !defined(HAVE_POWEROFF_WHILE_CHARGING) || charger_input_state != NO_CHARGER @@ -364,8 +379,7 @@ DEBUGF("Sleep timer timeout. Stopping...\n"); set_sleep_timer(0); backlight_off(); /* Nighty, nighty... */ - } - else { + } else if (global_settings.sleeptimer_pause_mode == false) { DEBUGF("Sleep timer timeout. Shutting off...\n"); sys_poweroff(); } Index: uisimulator/common/stubs.c =================================================================== --- uisimulator/common/stubs.c (revision 29159) +++ uisimulator/common/stubs.c (working copy) @@ -32,6 +32,7 @@ #include "power.h" #include "ata.h" /* for volume definitions */ +#include "kernel.h" /* access to current_tick */ static bool storage_spinning = false; @@ -385,17 +386,6 @@ (void)pitch; } -static int sleeptime; -void set_sleep_timer(int seconds) -{ - sleeptime = seconds; -} - -int get_sleep_timer(void) -{ - return sleeptime; -} - #ifdef HAVE_LCD_CHARCELLS void lcd_clearrect (int x, int y, int nx, int ny) { Index: uisimulator/common/powermgmt-sim.c =================================================================== --- uisimulator/common/powermgmt-sim.c (revision 29159) +++ uisimulator/common/powermgmt-sim.c (working copy) @@ -23,13 +23,39 @@ #include "system.h" #include #include "kernel.h" +#include "power.h" #include "powermgmt.h" +#include "debug.h" +#include "string.h" +#include "audio.h" +#include "thread-sdl.h" +#include "SDL.h" #define BATT_MINMVOLT 2500 /* minimum millivolts of battery */ #define BATT_MAXMVOLT 4500 /* maximum millivolts of battery */ #define BATT_MAXRUNTIME (10 * 60) /* maximum runtime with full battery in minutes */ +static bool sleeptimer_active = false; +static long sleeptimer_endtick; +static long last_event_tick; +static int poweroff_timeout = 0; + +/* Power history: power_history[0] is the newest sample */ +unsigned short power_history[POWER_HISTORY_LEN]; + +#define POWERMGMT_DEBUG_STACK 0 + +/* 2HZ sample rate unless otherwise specified */ +#define POWER_THREAD_STEP_TICKS (HZ/2) + +#if CONFIG_CPU == JZ4732 /* FIXME! */ +static char power_stack[DEFAULT_STACK_SIZE + POWERMGMT_DEBUG_STACK]; +#else +static char power_stack[DEFAULT_STACK_SIZE/2 + POWERMGMT_DEBUG_STACK]; +#endif +static const char power_thread_name[] = "power"; + extern void send_battery_level_event(void); extern int last_sent_battery_level; extern int battery_percent; @@ -120,7 +146,7 @@ void set_poweroff_timeout(int timeout) { - (void)timeout; + poweroff_timeout = timeout; } void set_battery_capacity(int capacity) @@ -128,6 +154,107 @@ (void)capacity; } + + +/* + * We shut off in the following cases: + * 1) The unit is idle, not playing music + * 2) The unit is playing music, but is paused + * 3) The battery level has reached shutdown limit + * + * We do not shut off in the following cases: + * 1) The USB is connected + * 2) The charger is connected + * 3) We are recording, or recording with pause + * 4) The radio is playing + */ +static void handle_auto_poweroff(void) +{ + DEBUGF("handle_auto_poweroff()\n"); + long timeout = poweroff_timeout*60*HZ; + int audio_stat = audio_status(); + long tick = current_tick; + + if (timeout && + (audio_stat == 0 || + (audio_stat == (AUDIO_STATUS_PLAY | AUDIO_STATUS_PAUSE) && + !sleeptimer_active))) { + + if (TIME_AFTER(tick, last_event_tick + timeout)) { + sys_poweroff(); + } + } + else if (sleeptimer_active) { + /* Handle sleeptimer */ + if (TIME_AFTER(tick, sleeptimer_endtick)) { + DEBUGF("Sleep timer timeout. Pause Playing...\n"); + audio_pause(); + sleeptimer_active = false; + } + } +} + + +static void power_thread(void) +{ + DEBUGF("power_thread() starting\n"); + long next_power_hist; + + next_power_hist = current_tick + HZ*60; + + while (1) + { + + /* Steady state */ + sleep(POWER_THREAD_STEP_TICKS); + + if (TIME_BEFORE(current_tick, next_power_hist)) + continue; + + /* increment to ensure there is a record for every minute + * rather than go forward from the current tick */ + next_power_hist += HZ*60; + + handle_auto_poweroff(); + } +} /* power_thread */ + + +void powermgmt_init(void) +{ + /* init history to 0 */ + create_thread(power_thread, power_stack, sizeof(power_stack), 0, + power_thread_name IF_PRIO(, PRIORITY_SYSTEM) + IF_COP(, CPU)); +} + +void set_sleep_timer(int seconds) +{ + if (seconds) { + DEBUGF("Setting sleep timer to %d seconds (%d minutes)\n", seconds, seconds/60); + sleeptimer_active = true; + sleeptimer_endtick = current_tick + seconds * HZ; + } else { + DEBUGF("Disabling sleep timer\n"); + sleeptimer_active = false; + sleeptimer_endtick = 0; + } +} + +int get_sleep_timer(void) +{ + if (sleeptimer_active) { + if (TIME_AFTER(sleeptimer_endtick, current_tick)) + return (sleeptimer_endtick - current_tick) / HZ; + DEBUGF("Sleep timer finished\n"); + set_sleep_timer(0); + } + return 0; +} + + + + #if BATTERY_TYPES_COUNT > 1 void set_battery_type(int type) { @@ -151,12 +278,30 @@ void reset_poweroff_timer(void) { + last_event_tick = current_tick; } void shutdown_hw(void) { } + void sys_poweroff(void) + { + power_off(); + } + +extern int playlist_update_resume_info(const struct mp3entry* id3); +void power_off(void) +{ + /* just to be sure the offset is actual */ + playlist_update_resume_info(audio_current_track()); + + SDL_Event event; + event.type = SDL_QUIT; + SDL_PushEvent (&event); +} + + void cancel_shutdown(void) { } Index: manual/appendix/config_file_options.tex =================================================================== --- manual/appendix/config_file_options.tex (revision 29159) +++ manual/appendix/config_file_options.tex (working copy) @@ -363,6 +363,10 @@ jump scroll delay & 0 to 250 & 0.01~s\\ }% + sleeptimer duration & 5 to 300 (in steps of 5) + & minutes\\ + sleeptimer on startup & off, on & N/A\\ + sleeptimer pause mode & off, on & N/A\\ \bottomrule \end{longtable} Index: manual/configure_rockbox/system_options.tex =================================================================== --- manual/configure_rockbox/system_options.tex (revision 29159) +++ manual/configure_rockbox/system_options.tex (working copy) @@ -707,3 +707,25 @@ }} \end{description} } +\subsection{Sleep Timer} + Sleep Timer related menu options. + The \setting{Sleep Timer} pauses your \dap{} after a given time. + Any currently active timer is disabled if you press pause and (if \setting{Start Timer On Power Up} is set) restarted when playing resumes. + \begin{description} + \item[Start Timer (\emph{duration}):] + Shown when the timer is inactive, this option will initiate the timer with the duration shown in brackets. + \item[Stop Timer (\emph{remaining}):] + Shown when the timer is active, this option will disable the current timer. + The time remaining before completion is shown in brackets. + \item[Timer Duration:] + The number of minutes from when new timers are initiated to their completion. + The values range from 5 minutes to 5 hours in 5 minute steps. + If a timer is currently active, the timer's duration will be set to the newly entered value. + The value set is persistent, see \reference{ref:config_file_options}. + \item[Start Timer On Power Up:] + If set, a timer will be initiated when the device starts. + The value set is persistent. + \item[Timer Pause Mode:] + If set, instead of powering down when the timer completes, the \dap{} will pause playback. + The value set is persistent. + \end{description} Index: manual/main_menu/main.tex =================================================================== --- manual/main_menu/main.tex (revision 29159) +++ manual/main_menu/main.tex (working copy) @@ -200,10 +200,6 @@ Time related menu options. Pressing \ActionStdContext{} will voice the current time if voice support is enabled \begin{description} \item [Set Time/Date: ] Set current time and date. - \item[Sleep Timer:] - The \setting{Sleep Timer} powers off your \dap{} after playing for a given - time. It can be set from \setting{Off} to 5 hours in 5 minute steps. - The \setting{Sleep Timer} is reset on boot. \item [Time Format: ] Choose 12 or 24 hour clock. \opt{alarm}{ \subsection{Wake-Up Alarm} @@ -237,13 +233,6 @@ \item[Credits:] Display the list of contributors. -\nopt{rtc}{ - \item[Sleep Timer:] - The \setting{Sleep Timer} powers off your \dap{} after playing for a given - time. It can be set from \setting{Off} to 5 hours in 5 minute steps. - The \setting{Sleep Timer} is reset on boot. -} - \item[Running Time:] Shows the runtime of your \dap{} in hours, minutes and seconds. \begin{description}