#include "dryos.h" #include "property.h" #include "config.h" #include "zebra.h" #include "shoot.h" #include "bmp.h" #include "lens.h" /* Canon's powersave timer */ /* ======================= */ void powersave_prolong() { /* reset the powersave timer (as if you would press a button) */ int prolong = 3; /* AUTO_POWEROFF_PROLONG */ prop_request_change(PROP_ICU_AUTO_POWEROFF, &prolong, 4); } void powersave_prohibit() { /* disable powersave timer */ int powersave_prohibit = 2; /* AUTO_POWEROFF_PROHIBIT */ prop_request_change(PROP_ICU_AUTO_POWEROFF, &powersave_prohibit, 4); } void powersave_permit() { /* re-enable powersave timer */ int powersave_permit = 1; /* AUTO_POWEROFF_PERMIT */ prop_request_change(PROP_ICU_AUTO_POWEROFF, &powersave_permit, 4); } /* Paused LiveView */ /* =============== */ static int lv_zoom_before_pause = 0; void PauseLiveView() // this should not include "display off" command { if (ml_shutdown_requested) return; if (sensor_cleaning) return; if (PLAY_MODE) return; if (MENU_MODE) return; if (LV_NON_PAUSED) { //~ ASSERT(DISPLAY_IS_ON); int x = 1; BMP_LOCK( lv_zoom_before_pause = lv_dispsize; prop_request_change_wait(PROP_LV_ACTION, &x, 4, 1000); msleep(100); clrscr(); lv_paused = 1; ) ASSERT(LV_PAUSED); } } // returns 1 if it did wakeup int ResumeLiveView() { info_led_on(); int ans = 0; if (ml_shutdown_requested) return 0; if (sensor_cleaning) return 0; if (PLAY_MODE) return 0; if (MENU_MODE) return 0; if (LV_PAUSED) { int x = 0; BMP_LOCK( prop_request_change_wait(PROP_LV_ACTION, &x, 4, 1000); int iter = 10; while (!DISPLAY_IS_ON && iter--) msleep(100); ) while (sensor_cleaning) msleep(100); if (lv) set_lv_zoom(lv_zoom_before_pause); msleep(100); ans = 1; } lv_paused = 0; idle_wakeup_reset_counters(-1357); info_led_off(); return ans; } /* Display on/off */ /* ============== */ /* handled in debug.c, handle_tricky_canon_calls */ /* todo: move them here */ void display_on() { fake_simple_button(MLEV_TURN_ON_DISPLAY); } void display_off() { fake_simple_button(MLEV_TURN_OFF_DISPLAY); } /* LED blinking when idle */ /* ====================== */ /* useful if you forget your camera on, with the display turned off */ static CONFIG_INT("idle.blink", idle_blink, 1); /* called every 100ms from zebra.c */ void idle_led_blink_step(int k) { // Here we're blinking the info LED approximately once every five // seconds to show the user that their camera is still on and has // not dropped into standby mode. But it's distracting to blink // it every five seconds, and if the user pushed a button recently // then they already _know_ that their camera is still on, so // let's only do it if the camera's buttons have been idle for at // least 30 seconds. if (k % 50 == 0 && !DISPLAY_IS_ON && lens_info.job_state == 0 && NOT_RECORDING && !get_halfshutter_pressed() && !is_intervalometer_running() && idle_blink) if ((get_seconds_clock() - get_last_time_active()) > 30) info_led_blink(1, 10, 10); } /* ML implementation: Powersave in LiveView */ /* ======================================== */ CONFIG_INT("idle.display.turn_off.after", idle_display_turn_off_after, 0); // this also enables power saving for intervalometer static CONFIG_INT("idle.display.dim.after", idle_display_dim_after, 0); static CONFIG_INT("idle.display.gdraw_off.after", idle_display_global_draw_off_after, 0); static CONFIG_INT("idle.rec", idle_rec, 0); static CONFIG_INT("idle.shortcut.key", idle_shortcut_key, 0); /* also used in zebra.c */ volatile int idle_globaldraw_disable = 0; #ifdef FEATURE_POWERSAVE_LIVEVIEW static int idle_countdown_display_dim = 50; static int idle_countdown_display_off = 50; static int idle_countdown_globaldraw = 50; static int idle_countdown_clrscr = 50; static int idle_countdown_display_dim_prev = 50; static int idle_countdown_display_off_prev = 50; static int idle_countdown_globaldraw_prev = 50; static int idle_countdown_clrscr_prev = 50; /* this will block all Canon drawing routines when the camera is idle */ /* (workaround for 50D) */ #ifdef CONFIG_KILL_FLICKER static int idle_countdown_killflicker = 5; static int idle_countdown_killflicker_prev = 5; extern int kill_canon_gui_mode; #endif int idle_is_powersave_enabled() { return idle_display_dim_after || idle_display_turn_off_after || idle_display_global_draw_off_after; } int idle_is_powersave_enabled_on_info_disp_key() { return idle_is_powersave_enabled() && idle_shortcut_key; } int idle_is_powersave_active() { return (idle_display_dim_after && !idle_countdown_display_dim_prev) || (idle_display_turn_off_after && !idle_countdown_display_off_prev) || (idle_display_global_draw_off_after && !idle_countdown_globaldraw_prev); } void idle_force_powersave_in_1s() { idle_countdown_display_off = MIN(idle_countdown_display_off, 10); idle_countdown_display_dim = MIN(idle_countdown_display_dim, 10); idle_countdown_globaldraw = MIN(idle_countdown_globaldraw, 10); } void idle_force_powersave_now() { idle_countdown_display_off = MIN(idle_countdown_display_off, 1); idle_countdown_display_dim = MIN(idle_countdown_display_dim, 1); idle_countdown_globaldraw = MIN(idle_countdown_globaldraw, 1); } int handle_powersave_key(struct event * event) { if (event->param == BGMT_INFO) { if (!idle_shortcut_key) return 1; if (!lv) return 1; if (!idle_is_powersave_enabled()) return 1; if (IS_FAKE(event)) return 1; if (gui_menu_shown()) return 1; if (!idle_is_powersave_active()) { idle_force_powersave_now(); info_led_blink(1,50,0); } return 0; } return 1; } #ifdef CONFIG_LCD_SENSOR CONFIG_INT("lcdsensor.wakeup", lcd_sensor_wakeup, 1); #endif void idle_wakeup_reset_counters(int reason) // called from handle_buttons { if (ml_shutdown_requested) return; #if 0 NotifyBox(2000, "wakeup: %d ", reason); #endif //~ bmp_printf(FONT_LARGE, 50, 50, "wakeup: %d ", reason); // when sensor is covered, timeout changes to 3 seconds #ifdef CONFIG_LCD_SENSOR int sensor_status = lcd_sensor_wakeup && display_sensor && DISPLAY_SENSOR_POWERED; #else int sensor_status = 0; #endif // those are for powersaving idle_countdown_display_off = sensor_status ? 25 : idle_display_turn_off_after * 10; idle_countdown_display_dim = sensor_status ? 25 : idle_display_dim_after * 10; idle_countdown_globaldraw = sensor_status ? 25 : idle_display_global_draw_off_after * 10; if (reason == -2345) // disable powersave during recording return; // those are not for powersaving idle_countdown_clrscr = 30; if (reason == -10 || reason == -11) // focus event (todo: should define constants for those) return; #ifdef CONFIG_KILL_FLICKER idle_countdown_killflicker = 10; #endif } // called at 10 Hz static void update_idle_countdown(int* countdown) { //~ bmp_printf(FONT_MED, 200, 200, "%d ", *countdown); if ((liveview_display_idle() && !get_halfshutter_pressed() && !gui_menu_shown()) || !DISPLAY_IS_ON) { if (*countdown) (*countdown)--; } else { idle_wakeup_reset_counters(-100); // will reset all idle countdowns } #ifdef CONFIG_LCD_SENSOR int sensor_status = lcd_sensor_wakeup && display_sensor && DISPLAY_SENSOR_POWERED; #else int sensor_status = 0; #endif static int prev_sensor_status = 0; if (sensor_status != prev_sensor_status) idle_wakeup_reset_counters(-1); prev_sensor_status = sensor_status; } static void idle_action_do(int* countdown, int* prev_countdown, void(*action_on)(void), void(*action_off)(void)) { if (ml_shutdown_requested) return; update_idle_countdown(countdown); int c = *countdown; // *countdown may be changed by "wakeup" => race condition //~ bmp_printf(FONT_MED, 100, 200, "%d->%d ", *prev_countdown, c); if (*prev_countdown && !c) { //~ info_led_blink(1, 50, 50); //~ bmp_printf(FONT_MED, 100, 200, "action "); msleep(500); action_on(); //~ msleep(500); //~ bmp_printf(FONT_MED, 100, 200, " "); } else if (!*prev_countdown && c) { //~ info_led_blink(1, 50, 50); //~ bmp_printf(FONT_MED, 100, 200, "unaction"); msleep(500); action_off(); //~ msleep(500); //~ bmp_printf(FONT_MED, 100, 200, " "); } *prev_countdown = c; } static void idle_display_off_show_warning() { extern int motion_detect; if (motion_detect || RECORDING) { NotifyBox(3000, "DISPLAY OFF..."); } else { NotifyBox(3000, "DISPLAY AND SENSOR OFF..."); } } static void idle_display_off() { extern int motion_detect; if (!(motion_detect || RECORDING)) PauseLiveView(); display_off(); msleep(300); idle_countdown_display_off = 0; ASSERT(!(RECORDING && LV_PAUSED)); ASSERT(!DISPLAY_IS_ON); } static void idle_display_on() { ResumeLiveView(); display_on(); redraw(); } static void idle_bmp_off() { bmp_off(); } static void idle_bmp_on() { bmp_on(); } static PROP_INT(PROP_LCD_BRIGHTNESS_MODE, lcd_brightness_mode); static PROP_INT(PROP_LOGICAL_CONNECT, logical_connect); // EOS utility? static int old_backlight_level = 0; static void idle_display_dim() { ASSERT(lv || lv_paused); #ifdef CONFIG_AUTO_BRIGHTNESS int backlight_mode = lcd_brightness_mode; if (backlight_mode == 0) // can't restore brightness properly in auto mode { NotifyBox(2000, "LCD brightness is automatic.\n" "ML will not dim the display."); return; } #endif old_backlight_level = backlight_level; set_backlight_level(1); } static void idle_display_undim() { if (old_backlight_level) { set_backlight_level(old_backlight_level); old_backlight_level = 0; } } void idle_globaldraw_dis() { idle_globaldraw_disable++; } void idle_globaldraw_en() { if (idle_globaldraw_disable > 0) idle_globaldraw_disable--; } #ifdef CONFIG_KILL_FLICKER static void black_bars_50D() { if (!get_global_draw()) return; if (!is_movie_mode()) return; get_yuv422_vram(); if (video_mode_resolution > 1) { bmp_fill(COLOR_BLACK, os.x0, os.y0, os.off_43, os.y_ex); bmp_fill(COLOR_BLACK, os.x_max - os.off_43, os.y0, os.off_43, os.y_ex); } else { bmp_fill(COLOR_BLACK, os.x0, os.y0, os.x_ex, os.off_169); bmp_fill(COLOR_BLACK, os.x0, os.y_max - os.off_169, os.x_ex, os.off_169); } } /* also called from _redraw_do */ void idle_kill_flicker() { if (!canon_gui_front_buffer_disabled()) { get_yuv422_vram(); canon_gui_disable_front_buffer(); clrscr(); if (is_movie_mode()) { black_bars_50D(); if (RECORDING) { fill_circle(os.x_max - 12, os.y0 + 28, 10, COLOR_RED); } } } } static void idle_stop_killing_flicker() { if (canon_gui_front_buffer_disabled()) { canon_gui_enable_front_buffer(0); } } #endif /* called from zebra.c */ void idle_powersave_step() { if (RECORDING && idle_rec == 0) // don't go to powersave when recording idle_wakeup_reset_counters(-2345); if (NOT_RECORDING && idle_rec == 1) // don't go to powersave when not recording idle_wakeup_reset_counters(-2345); if (logical_connect) idle_wakeup_reset_counters(-305); // EOS utility if (idle_display_dim_after) { idle_action_do(&idle_countdown_display_dim, &idle_countdown_display_dim_prev, idle_display_dim, idle_display_undim); } if (idle_display_turn_off_after) { idle_action_do(&idle_countdown_display_off, &idle_countdown_display_off_prev, idle_display_off, idle_display_on); // show a warning that display is going to be turned off (and clear it if some button is pressed) static int warning_dirty = 0; if (idle_countdown_display_off == 30) { idle_display_off_show_warning(); warning_dirty = 1; } else if (warning_dirty && idle_countdown_display_off > 30) { NotifyBoxHide(); warning_dirty = 0; } } if (idle_display_global_draw_off_after) idle_action_do(&idle_countdown_globaldraw, &idle_countdown_globaldraw_prev, idle_globaldraw_dis, idle_globaldraw_en); /* zebra.c */ extern int clearscreen; if (clearscreen == 2) // clear overlay when idle idle_action_do(&idle_countdown_clrscr, &idle_countdown_clrscr_prev, idle_bmp_off, idle_bmp_on); #ifdef CONFIG_KILL_FLICKER /* see ZEBRAS_IN_LIVEVIEW in zebra.c */ int zebras_in_liveview = get_global_draw_setting() & 1; if (kill_canon_gui_mode == 1) { if (zebras_in_liveview && !gui_menu_shown()) { int idle = liveview_display_idle() && lv_disp_mode == 0; if (idle) { if (!canon_gui_front_buffer_disabled()) idle_kill_flicker(); } else { if (canon_gui_front_buffer_disabled()) idle_stop_killing_flicker(); } static int prev_idle = 0; if (!idle && prev_idle != idle) redraw(); prev_idle = idle; } } else if (kill_canon_gui_mode == 2) // LV transparent menus and key presses { if (zebras_in_liveview && !gui_menu_shown() && lv_disp_mode == 0) idle_action_do(&idle_countdown_killflicker, &idle_countdown_killflicker_prev, idle_kill_flicker, idle_stop_killing_flicker); } #endif } PROP_HANDLER(PROP_LV_ACTION) { idle_display_undim(); // restore LCD brightness, especially for shutdown } static char* idle_time_format(int t) { static char msg[50]; if (t) snprintf(msg, sizeof(msg), "after %d%s", t < 60 ? t : t/60, t < 60 ? "sec" : "min"); else snprintf(msg, sizeof(msg), "OFF"); return msg; } static MENU_UPDATE_FUNC(idle_display_dim_print) { MENU_SET_VALUE( idle_time_format(CURRENT_VALUE) ); #ifdef CONFIG_AUTO_BRIGHTNESS int backlight_mode = lcd_brightness_mode; if (backlight_mode == 0) // can't restore brightness properly in auto mode { MENU_SET_WARNING(MENU_WARN_NOT_WORKING, "LCD brightness is auto in Canon menu. It won't work."); } #endif } static MENU_UPDATE_FUNC(idle_display_feature_print) { MENU_SET_VALUE( idle_time_format(CURRENT_VALUE) ); } static int timeout_values[] = {0, 5, 10, 20, 30, 60, 120, 300, 600, 900}; static int current_timeout_index(int t) { int i; for (i = 0; i < COUNT(timeout_values); i++) if (t == timeout_values[i]) return i; return 0; } static void idle_timeout_toggle(void* priv, int sign) { int* t = (int*)priv; int i = current_timeout_index(*t); i = MOD(i + sign, COUNT(timeout_values)); *(int*)priv = timeout_values[i]; } static struct menu_entry powersave_menus[] = { { .name = "Powersave in LiveView", .select = menu_open_submenu, .submenu_width = 715, .help = "Options for reducing power consumption during idle times.", .depends_on = DEP_LIVEVIEW, .children = (struct menu_entry[]) { { .name = "Enable power saving", .priv = &idle_rec, .max = 2, .choices = (const char *[]) {"on Standby", "on Recording", "on STBY+REC"}, .help = "If enabled, powersave (see above) works when recording too." }, #ifdef CONFIG_LCD_SENSOR { .name = "Use LCD sensor", .priv = &lcd_sensor_wakeup, .max = 1, .help = "With the LCD sensor you may wakeup or force powersave mode." }, #endif { .name = "Use shortcut key", .priv = &idle_shortcut_key, .max = 1, .choices = (const char *[]) {"OFF", INFO_BTN_NAME}, .help = "Shortcut key for enabling powersave modes right away." }, { .name = "Dim display", .priv = &idle_display_dim_after, .update = idle_display_dim_print, .select = idle_timeout_toggle, .max = 900, .icon_type = IT_PERCENT_LOG_OFF, .help = "Dim LCD display in LiveView when idle, to save power.", }, { .name = "Turn off LCD", .priv = &idle_display_turn_off_after, .update = idle_display_feature_print, .select = idle_timeout_toggle, .max = 900, .icon_type = IT_PERCENT_LOG_OFF, .help = "Turn off display and pause LiveView when idle and not REC.", }, { .name = "Turn off GlobalDraw", .priv = &idle_display_global_draw_off_after, .update = idle_display_feature_print, .select = idle_timeout_toggle, .max = 900, .icon_type = IT_PERCENT_LOG_OFF, .help = "Turn off GlobalDraw when idle, to save some CPU cycles.", //~ .edit_mode = EM_MANY_VALUES, }, MENU_EOL }, } }; static void powersave_init() { menu_add( "Prefs", powersave_menus, COUNT(powersave_menus) ); } INIT_FUNC(__FILE__, powersave_init); #else /* some dummy stubs to compile without FEATURE_POWERSAVE_LIVEVIEW */ void idle_wakeup_reset_counters(int reason) { } int handle_powersave_key(struct event * event) { return 1; } int idle_is_powersave_active() { return 0; } int idle_is_powersave_enabled_on_info_disp_key() { return 0; } void idle_globaldraw_dis() { } void idle_globaldraw_en() { } void idle_force_powersave_now() { } void idle_force_powersave_in_1s() { } #endif