/** \file * Tweaks to default UI behavior */ #include "dryos.h" #include "bmp.h" #include "tasks.h" #include "debug.h" #include "menu.h" #include "property.h" #include "propvalues.h" #include "config.h" #include "gui.h" #include "lens.h" #include "math.h" #include "beep.h" #include "module.h" #include "shoot.h" #include "focus.h" #include "imgconv.h" #include "zebra.h" #include "cropmarks.h" #include "hdr.h" #include "lvinfo.h" #ifdef FEATURE_LCD_SENSOR_SHORTCUTS #include "lcdsensor.h" #endif static void lcd_adjust_position_step(); static void arrow_key_step(); static void preview_contrast_n_saturation_step(); static void adjust_saturation_level(int); static void grayscale_menus_step(); void clear_lv_afframe(); void NormalDisplay(); void MirrorDisplay(); void ReverseDisplay(); static void upside_down_step(); static void warn_step(); extern void display_gain_toggle(void* priv, int delta); #ifdef FEATURE_ZOOM_TRICK_5D3 // not reliable void zoom_trick_step(); #endif static CONFIG_INT("dof.preview.sticky", dofpreview_sticky, 0); #ifdef FEATURE_STICKY_DOF static int dofp_value = 0; static void dofp_set(int value) { dofp_value = value; prop_request_change(PROP_DOF_PREVIEW_MAYBE, &value, 2); } /*static void dofp_lock(void* priv, int delta) { dofp_set(1); }*/ PROP_HANDLER(PROP_LAST_JOB_STATE) { if (dofp_value) dofp_set(0); } static void dofp_update() { static int state = 0; // 0: allow 0->1, disallow 1->0 (first DOF press) // 1: allow everything => reset things (second DOF presss) static int old_value = 0; int d = dofpreview; // to avoid race condition //~ bmp_printf(FONT_MED, 0, 0, "DOFp: btn=%d old=%d state=%d hs=%d ", dofpreview, old_value, state, HALFSHUTTER_PRESSED); if (dofpreview_sticky == 1) { if (d) {bmp_printf(FONT_LARGE, 720-font_large.width*3, 50, "DOF"); info_led_on(); } else if (old_value) { redraw(); info_led_off(); } if (d != old_value) // transition { if (state == 0) { if (old_value && !d) { //~ beep(); dofp_set(1); msleep(100); state = 1; } } else { if (d == 0) state = 0; //~ beep(); } } old_value = d; } } #endif // ExpSim //**********************************************************************/ int get_expsim() { //bmp_printf(FONT_MED, 50, 50, "mov: %d expsim:%d lv_mov: %d", is_movie_mode(), _expsim, lv_movie_select); #if defined(CONFIG_7D) /* 7D has expsim in video mode, but expsim is for photo mode only. so return 2 if in video mode */ if(is_movie_mode()) { return 2; } #endif if (_expsim == 3) return 0; /* on 5D3, this means "off" and 0 means "when pressing DOF" */ return _expsim; } #ifdef CONFIG_EXPSIM static void video_refresh() { set_lv_zoom(lv_dispsize); lens_display_set_dirty(); } void set_expsim( int x ) { if (get_expsim() != x) { prop_request_change_wait(PROP_LIVE_VIEW_VIEWTYPE, &x, 4, 1000); #ifdef CONFIG_5D2 // Canon bug: FPS is not updated when toggling photo->movie while LiveView is active // No side effects in Canon firmware, since this is normally done in Canon menu (when LV is not running) if (x == 2) video_refresh(); #endif } } #ifdef FEATURE_EXPSIM static void expsim_toggle( void * priv, int delta) { #ifdef CONFIG_EXPSIM_MOVIE int e = MOD(get_expsim() + delta, 3); #else if (is_movie_mode()) return; int e = !get_expsim(); #endif set_expsim(e); #ifdef CONFIG_5D2 if (e == 2) // movie display, make sure movie recording is enabled { if (lv_movie_select != LVMS_ENABLE_MOVIE) { int x = LVMS_ENABLE_MOVIE; prop_request_change(PROP_LV_MOVIE_SELECT, &x, 4); } } else // photo display, disable movie recording { if (lv_movie_select == LVMS_ENABLE_MOVIE) { int x = 1; prop_request_change(PROP_LV_MOVIE_SELECT, &x, 4); } } #endif } static MENU_UPDATE_FUNC(expsim_display) { if (is_movie_mode()) { #ifndef CONFIG_EXPSIM_MOVIE MENU_SET_VALUE("Movie"); MENU_SET_ICON(MNI_DICE, 0); #endif } else if (_expsim == 3) { MENU_SET_VALUE("OFF"); MENU_SET_ICON(MNI_OFF, 0); } else { if (CONTROL_BV) { if (get_expsim()) { MENU_SET_RINFO("via Expo.Override"); MENU_SET_WARNING(MENU_WARN_INFO, "Usable in complete darkness; try with FPS Override or Bulb Timer."); } else { MENU_SET_WARNING(MENU_WARN_ADVICE, "Expo Override is active, LiveView exposure may be incorrect."); } } else if (shooting_mode == SHOOTMODE_M && !lens_info.name[0]) /* Canon's LiveView underexposure bug with manual lenses */ { MENU_SET_WARNING(MENU_WARN_ADVICE, "LiveView exposure may be incorrect. Enable expo override to fix it."); } } } #endif #else // no _expsim, use some dummy stubs void set_expsim(int expsim){}; #endif /* void set_pic_quality(int q) { if (q == -1) return; prop_request_change(PROP_PIC_QUALITY, &q, 4); prop_request_change(PROP_PIC_QUALITY2, &q, 4); prop_request_change(PROP_PIC_QUALITY3, &q, 4); } */ extern unsigned lcd_sensor_shortcuts; #ifdef FEATURE_ARROW_SHORTCUTS // backlight adjust //**********************************************************************/ static void show_display_gain_level() { int digic_iso_gain_photo = get_digic_iso_gain_photo(); int G = gain_to_ev_scaled(digic_iso_gain_photo, 1) - 10; NotifyBox(2000, "Display Gain : %d EV", G); } static void adjust_backlight_level(int delta) { if (backlight_level < 1 || backlight_level > 7) return; // kore wa dame desu yo if (!DISPLAY_IS_ON) call("TurnOnDisplay"); int digic_iso_gain_photo = get_digic_iso_gain_photo(); int G = gain_to_ev_scaled(digic_iso_gain_photo, 1) - 10; if (!is_movie_mode()) { if (delta < 0 && G > 0) // decrease display gain first { display_gain_toggle(0, -1); show_display_gain_level(); return; } if (delta > 0 && backlight_level == 7) // backlight at maximum, increase display gain { int oldG = G; if (G < 6) display_gain_toggle(0, 1); if (oldG == 0) redraw(); // cleanup exposure tools, they are no longer valid show_display_gain_level(); return; } } int level = COERCE(backlight_level + delta, 1, 7); prop_request_change(PROP_LCD_BRIGHTNESS, &level, 4); NotifyBox(2000, "LCD Backlight: %d", level); } #endif void set_backlight_level(int level) { level = COERCE(level, 1, 7); prop_request_change(PROP_LCD_BRIGHTNESS, &level, 4); } CONFIG_INT("af.frame.autohide", af_frame_autohide, 1); static int afframe_countdown = 0; void afframe_set_dirty() { afframe_countdown = 20; } void afframe_clr_dirty() { afframe_countdown = 0; } // this should be thread safe void clear_lv_afframe_if_dirty() { //~ #ifndef CONFIG_50D if (af_frame_autohide && afframe_countdown && liveview_display_idle()) { afframe_countdown--; if (!afframe_countdown) { BMP_LOCK( clear_lv_afframe(); ) } } //~ #endif } void clear_lv_afframe() { if (!lv) return; if (gui_menu_shown()) return; if (lv_dispsize != 1) return; if (lv_disp_mode) return; int xaf,yaf; get_yuv422_vram(); uint8_t* M = (uint8_t*)get_bvram_mirror(); if (!M) return; get_afframe_pos(720, 480, &xaf, &yaf); xaf = N2BM_X(xaf); yaf = N2BM_Y(yaf); int x0 = COERCE(xaf,BMP_W_MINUS+100, BMP_W_PLUS-100) - 100; int y0 = COERCE(yaf,BMP_H_MINUS+75+os.off_169, BMP_H_PLUS-75-os.off_169) - 75; int w = 200; int h = 150; int g = get_global_draw(); // on 5D2, LV grids are black, just like AF frame... // so, try to erase what's white and a few pixels of nearby black uint8_t * const bvram = bmp_vram(); #define Pr(X,Y) bvram[BM(X,Y)] #define Pw(X,Y) bvram[BM(COERCE(X, BMP_W_MINUS, BMP_W_PLUS-1), COERCE(Y, BMP_H_MINUS, BMP_H_PLUS-1))] // not quite efficient, but works for (int i = y0+h; i > y0; i--) { for (int j = x0+w; j > x0; j--) { int p = Pr(j,i); // clear focus box (white pixels, and any black neighbouring pixels from bottom-right - shadow) if (p == COLOR_WHITE) { for (int di = 2; di >= 0; di--) { for (int dj = 2; dj >= 0; dj--) { int p = Pr(j+dj,i+di); if (p == COLOR_BLACK) { int m = M[BM(j+dj,i+di)]; Pw(j+dj,i+di) = g && (m & 0x80) ? m & ~0x80 : 0; // if global draw on, copy color from ML cropmark, otherwise, transparent } } } int m = M[BM(j,i)]; Pw(j,i) = g && (m & 0x80) ? m & ~0x80 : 0; } } } #undef Pw #undef Pr afframe_countdown = 0; } #if defined(CONFIG_5D3) || defined(CONFIG_6D) static CONFIG_INT("play.quick.zoom", quickzoom, 0); #else static CONFIG_INT("play.quick.zoom", quickzoom, 2); #endif #define PLAY_ACTION_TRIGGER_WHEEL 0 #define PLAY_ACTION_TRIGGER_LR 1 #define PLAY_ACTION_TRIGGER_WHEEL_OR_LR 2 static CONFIG_INT("play.set.trigger", play_set_wheel_trigger, 0); static CONFIG_INT("play.set.wheel", play_set_wheel_action, 4); static CONFIG_INT("quick.delete", quick_delete, 0); int timelapse_playback = 0; #ifdef FEATURE_SET_MAINDIAL static void playback_set_wheel_action(int dir) { #ifdef CONFIG_5DC play_set_wheel_action = COERCE(play_set_wheel_action, 3, 4); #endif #ifdef FEATURE_PLAY_EXPOSURE_FUSION if (play_set_wheel_action == 1) expfuse_preview_update(dir); else #endif #ifdef FEATURE_PLAY_COMPARE_IMAGES if (play_set_wheel_action == 2) playback_compare_images(dir); else #endif #ifdef FEATURE_PLAY_TIMELAPSE if (play_set_wheel_action == 3) timelapse_playback = COERCE(timelapse_playback + dir, -1, 1); else #endif #ifdef FEATURE_PLAY_EXPOSURE_ADJUST if (play_set_wheel_action == 4) expo_adjust_playback(dir); else #endif {}; } #endif int is_pure_play_photo_mode() // no other dialogs active (such as delete) { if (!PLAY_MODE) return 0; #ifdef CONFIG_5DC return 1; #else extern thunk PlayMain_handler; return (intptr_t)get_current_dialog_handler() == (intptr_t)&PlayMain_handler; #endif } int is_pure_play_movie_mode() // no other dialogs active (such as delete) { if (!PLAY_MODE) return 0; #ifdef CONFIG_VXWORKS return 0; #else extern thunk PlayMovieGuideApp_handler; return (intptr_t)get_current_dialog_handler() == (intptr_t)&PlayMovieGuideApp_handler; #endif } int is_pure_play_photo_or_movie_mode() { return is_pure_play_photo_mode() || is_pure_play_movie_mode(); } int is_play_or_qr_mode() { return PLAY_OR_QR_MODE; } int is_play_mode() { return PLAY_MODE; } int is_menu_mode() { return MENU_MODE; } #ifdef FEATURE_SET_MAINDIAL static void print_set_maindial_hint(int set) { if (is_pure_play_photo_mode()) { if (set) { info_led_on(); get_yuv422_vram(); bmp_printf( SHADOW_FONT(FONT_LARGE), os.x0, os.y_max - font_large.height, "Scrollwheel: %s", play_set_wheel_action == 1 ? "Exposure Fusion" : play_set_wheel_action == 2 ? "Compare Images" : play_set_wheel_action == 3 ? "Timelapse Play" : play_set_wheel_action == 4 ? "Exposure Adjust" : "err" ); } else { info_led_off(); redraw(); } } } #endif #ifdef FEATURE_SET_MAINDIAL static void set_maindial_cleanup() { #ifdef FEATURE_PLAY_EXPOSURE_FUSION // reset exposure fusion preview extern int expfuse_running; expfuse_running = 0; #endif #if defined(CONFIG_5DC) expo_adjust_playback(0); // reset value #endif } #endif #if defined(FEATURE_SET_MAINDIAL) || defined(FEATURE_QUICK_ERASE) int handle_set_wheel_play(struct event * event) { #ifdef FEATURE_SET_MAINDIAL static int set_maindial_action_enabled = 0; static int play_set_wheel_hot = 0; if (!is_pure_play_photo_mode()) { if (set_maindial_action_enabled) { set_maindial_action_enabled = 0; print_set_maindial_hint(0); } set_maindial_cleanup(); return 1; } if (play_set_wheel_action && (play_set_wheel_trigger == PLAY_ACTION_TRIGGER_WHEEL || play_set_wheel_trigger == PLAY_ACTION_TRIGGER_WHEEL_OR_LR)) { if (event->param == BGMT_PRESS_SET) { // for cameras where SET does not send an unpress event, pressing SET again should do the trick set_maindial_action_enabled = !set_maindial_action_enabled; #if !defined(CONFIG_50D) && !defined(CONFIG_5DC) ASSERT(set_maindial_action_enabled); // most cameras are expected to send Unpress SET event (if they don't, one needs to fix the quick erase feature) #endif print_set_maindial_hint(set_maindial_action_enabled); } else if (event->param == BGMT_UNPRESS_SET) { set_maindial_action_enabled = 0; print_set_maindial_hint(0); } // make sure the display is updated, just in case if (PLAY_MODE && set_maindial_action_enabled) { print_set_maindial_hint(1); } } // SET+Wheel action in PLAY mode if (set_maindial_action_enabled && !IS_FAKE(event)) { if (event->param == BGMT_WHEEL_LEFT || event->param == BGMT_WHEEL_RIGHT || event->param == BGMT_WHEEL_UP || event->param == BGMT_WHEEL_DOWN) { int dir = event->param == BGMT_WHEEL_RIGHT || event->param == BGMT_WHEEL_DOWN ? 1 : -1; playback_set_wheel_action(dir); return 0; } #ifdef FEATURE_QUICK_ERASE #if !defined(CONFIG_5D3) && !defined(CONFIG_5DC) && !defined(CONFIG_50D) // 5D3: Canon has it; 5Dc/50D: no unpress SET event if (quick_delete) { if (event->param == BGMT_TRASH) { fake_simple_button(BGMT_UNPRESS_SET); fake_simple_button(BGMT_TRASH); fake_simple_button(BGMT_WHEEL_DOWN); fake_simple_button(BGMT_PRESS_SET); return 0; } } #endif #endif } // Left/Right action in PLAY mode if (play_set_wheel_trigger && (int32_t)MEM(IMGPLAY_ZOOM_LEVEL_ADDR) < 0) { if (event->param == BGMT_PRESS_LEFT || event->param == BGMT_PRESS_RIGHT) { int dir = event->param == BGMT_PRESS_RIGHT ? 1 : -1; play_set_wheel_hot = 1; playback_set_wheel_action(dir); return 0; } } // some other key pressed without maindial action being active, cleanup things if ((!set_maindial_action_enabled && event->param != BGMT_PRESS_SET && event->param != BGMT_UNPRESS_SET) || (play_set_wheel_trigger && play_set_wheel_hot && event->param != BGMT_PRESS_LEFT && event->param != BGMT_PRESS_RIGHT)) { play_set_wheel_hot = 0; set_maindial_cleanup(); } #endif #ifdef FEATURE_QUICK_ERASE #if defined(CONFIG_5DC) || defined(CONFIG_50D) // SET does not send "unpress", so just move cursor on "erase" by default if (quick_delete && PLAY_MODE) { if (event->param == BGMT_TRASH) { fake_simple_button(BGMT_WHEEL_DOWN); return 1; } } #endif #endif return 1; } #endif static CONFIG_INT("play.lv.button", play_lv_action, 0); #ifdef FEATURE_LV_BUTTON_RATE int play_rate_flag = 0; int rating_in_progress = 0; void play_lv_key_step() { #ifdef CONFIG_Q_MENU_PLAYBACK // wait for user request to settle int prev = play_rate_flag; int pure_play_photo_mode = is_pure_play_photo_mode(); int pure_play_photo_or_movie_mode = is_pure_play_photo_or_movie_mode(); if (prev) while(1) { for (int i = 0; i < 5; i++) { NotifyBox(1000, "Rate: %s%d...", (play_rate_flag > 0) ? "+" : "", play_rate_flag % 6); msleep(100); } if (play_rate_flag == prev) break; prev = play_rate_flag; } play_rate_flag = play_rate_flag % 6; if (play_rate_flag) { rating_in_progress = 1; NotifyBoxHide(); fake_simple_button(BGMT_Q); // rate image fake_simple_button(BGMT_PRESS_DOWN); #if defined(CONFIG_6D) // too fast msleep(200); #endif // for photos, we need to go down 2 steps // for movies, we only need 1 step if (pure_play_photo_mode) { fake_simple_button(BGMT_PRESS_DOWN); } #ifdef BGMT_UNPRESS_UDLR fake_simple_button(BGMT_UNPRESS_UDLR); #else #ifndef CONFIG_6D // unpress produces another unwanted curser move fake_simple_button(BGMT_UNPRESS_DOWN); #endif #endif // alter rating N times int n = play_rate_flag; #ifdef FEATURE_LV_BUTTON_RATE_UPDOWN if (play_rate_flag > 0) { #endif for (int i = 0; i < n; i++) fake_simple_button(BGMT_WHEEL_DOWN); #ifdef FEATURE_LV_BUTTON_RATE_UPDOWN } else { for (int i = 0; i > n; i--) fake_simple_button(BGMT_WHEEL_UP); } #endif fake_simple_button(BGMT_Q); // close dialog play_rate_flag = 0; msleep(500); for (int i = 0; i < 50; i++) { if (pure_play_photo_or_movie_mode) break; // rating done :) msleep(100); } rating_in_progress = 0; } #endif } #endif #ifdef FEATURE_LV_BUTTON_PROTECT #ifdef CONFIG_5D2 static volatile int protect_running = 0; static void protect_image_task() { protect_running = 1; StartPlayProtectGuideApp(); fake_simple_button(BGMT_PRESS_SET); fake_simple_button(BGMT_UNPRESS_SET); msleep(100); intptr_t h = get_current_dialog_handler(); if (h == (intptr_t)0xffb6aebc) // ?! null code here... { StopPlayProtectGuideApp(); } protect_running = 0; } #endif #endif #if defined(FEATURE_LV_BUTTON_PROTECT) || defined(FEATURE_LV_BUTTON_RATE) int handle_lv_play(struct event * event) { if (!play_lv_action) return 1; #ifdef CONFIG_5D2 if (event->param == BGMT_LV && PLAY_MODE) { if (protect_running) return 0; if (is_pure_play_photo_or_movie_mode()) { protect_running = 1; task_create("protect_task", 0x1e, 0x1000, protect_image_task, 0); return 0; } } #else if (!rating_in_progress && PLAY_MODE && (event->param == BGMT_LV #ifdef FEATURE_LV_BUTTON_RATE_UPDOWN || ((event->param == BGMT_PRESS_UP || event->param == BGMT_PRESS_DOWN) && (int32_t)MEM(IMGPLAY_ZOOM_LEVEL_ADDR) < 0) #endif )) { #ifdef FEATURE_LV_BUTTON_RATE if (!is_pure_play_photo_or_movie_mode()) { if (rating_in_progress) return 0; // user presses buttons too fast return 1; // not in main play dialog, maybe in Q menu somewhere } if (play_lv_action == 2) { #ifdef FEATURE_LV_BUTTON_RATE_UPDOWN if (event->param == BGMT_PRESS_DOWN) play_rate_flag--; else #endif play_rate_flag++; } #endif #ifdef FEATURE_LV_BUTTON_PROTECT if (play_lv_action == 1) { fake_simple_button(BGMT_Q); // toggle protect current image #ifdef CONFIG_6D fake_simple_button(BGMT_PRESS_DOWN); msleep(100); fake_simple_button(BGMT_PRESS_UP); msleep(100); fake_simple_button(BGMT_WHEEL_DOWN); #else fake_simple_button(BGMT_WHEEL_DOWN); #endif fake_simple_button(BGMT_Q); } #endif return 0; } #endif return 1; } #endif static CONFIG_INT("halfshutter.sticky", halfshutter_sticky, 0); #ifdef FEATURE_STICKY_HALFSHUTTER static void hs_show() { bmp_printf(FONT(FONT_LARGE, COLOR_WHITE, COLOR_RED), 720-font_large.width*3, 50, "HS"); } /*void hs_hide() { bmp_printf(FONT(FONT_LARGE, COLOR_WHITE, 0), 720-font_large.width*3, 50, " "); }*/ static void fake_halfshutter_step() { if (gui_menu_shown()) return; if (halfshutter_sticky) { static int state = 0; // 0: allow 0->1, disallow 1->0 (first press) // 1: allow everything => reset things (second presss) static int old_value = 0; int hs = HALFSHUTTER_PRESSED; if (hs) hs_show(); else if (old_value) redraw(); if (hs != old_value) // transition { if (state == 0) { if (old_value && !hs) { info_led_on(); SW1(1,50); state = 1; } } else { if (hs == 0) { state = 0; info_led_off(); } } } old_value = hs; } } #endif CONFIG_INT("focus.box.lv.jump", focus_box_lv_jump, 0); static CONFIG_INT("focus.box.lv.speed", focus_box_lv_speed, 1); #ifdef FEATURE_LV_FOCUS_BOX_FAST static int arrow_pressed = 0; static int arrow_unpressed = 0; #endif int handle_fast_zoom_box(struct event * event) { #ifdef FEATURE_LV_FOCUS_BOX_SNAP if (event->param == #ifdef BGMT_JOY_CENTER BGMT_JOY_CENTER #else BGMT_PRESS_SET #endif #ifndef CONFIG_550D // 550D should always center focus box with SET (it doesn't do by default) && (focus_box_lv_jump || (RECORDING && is_manual_focus())) #endif #ifdef FEATURE_LV_FOCUS_BOX_FAST && !arrow_pressed #endif && liveview_display_idle() && !gui_menu_shown()) { center_lv_afframe(); return 0; } #endif #ifdef FEATURE_LV_FOCUS_BOX_FAST if (!IS_FAKE(event)) { if (event->param == BGMT_PRESS_LEFT || event->param == BGMT_PRESS_RIGHT || event->param == BGMT_PRESS_UP || event->param == BGMT_PRESS_DOWN #ifdef BGMT_PRESS_UP_RIGHT || event->param == BGMT_PRESS_UP_RIGHT || event->param == BGMT_PRESS_UP_LEFT || event->param == BGMT_PRESS_DOWN_RIGHT || event->param == BGMT_PRESS_DOWN_LEFT #endif ) { arrow_pressed = event->param; arrow_unpressed = 0; } else if ( #ifdef BGMT_UNPRESS_UDLR event->param == BGMT_UNPRESS_UDLR || #else event->param == BGMT_UNPRESS_LEFT || event->param == BGMT_UNPRESS_RIGHT || event->param == BGMT_UNPRESS_UP || event->param == BGMT_UNPRESS_DOWN || #endif #ifdef BGMT_JOY_CENTER event->param == BGMT_JOY_CENTER || #endif event->param == BGMT_PRESS_SET || event->param == BGMT_UNPRESS_SET ) { arrow_unpressed = 1; } } #endif return 1; } #if defined(FEATURE_QUICK_ZOOM) || defined(FEATURE_REMEMBER_LAST_ZOOM_POS_5D3) static int quickzoom_pressed = 0; static int quickzoom_unpressed = 0; static int quickzoom_fake_unpressed = 0; int handle_fast_zoom_in_play_mode(struct event * event) { if (!quickzoom || !PLAY_MODE) return 1; if (!IS_FAKE(event)) { if (event->param == BGMT_PRESS_ZOOM_IN) { quickzoom_pressed = 1; // will be reset after it's handled quickzoom_unpressed = 0; quickzoom_fake_unpressed = 0; } else if (event->param == BGMT_UNPRESS_ZOOM_IN) { quickzoom_unpressed = 1; } #ifdef IMGPLAY_ZOOM_POS_X #ifdef BGMT_JOY_CENTER else if (event->param == BGMT_JOY_CENTER && (int32_t)MEM(IMGPLAY_ZOOM_LEVEL_ADDR) > 3 && is_pure_play_photo_mode()) #else else if (event->param == BGMT_PRESS_SET && (int32_t)MEM(IMGPLAY_ZOOM_LEVEL_ADDR) > 3 && is_pure_play_photo_mode()) #endif { if (IMGPLAY_ZOOM_POS_X != IMGPLAY_ZOOM_POS_X_CENTER || IMGPLAY_ZOOM_POS_Y != IMGPLAY_ZOOM_POS_Y_CENTER) { IMGPLAY_ZOOM_POS_X = IMGPLAY_ZOOM_POS_X_CENTER; IMGPLAY_ZOOM_POS_Y = IMGPLAY_ZOOM_POS_Y_CENTER; MEM(IMGPLAY_ZOOM_LEVEL_ADDR) -= 1; #ifdef CONFIG_5D3 fake_simple_button(BGMT_WHEEL_RIGHT); #else fake_simple_button(BGMT_PRESS_ZOOM_IN); fake_simple_button(BGMT_UNPRESS_ZOOM_IN); #endif return 0; } } #endif } else { if (event->param == BGMT_UNPRESS_ZOOM_IN) { quickzoom_fake_unpressed = 1; } } return 1; } #ifdef IMGPLAY_ZOOM_POS_X static int play_zoom_last_x = 0; static int play_zoom_last_y = 0; #endif static void play_zoom_center_on_last_af_point() { #ifdef IMGPLAY_ZOOM_POS_X if (play_zoom_last_x && play_zoom_last_y) { IMGPLAY_ZOOM_POS_X = play_zoom_last_x; IMGPLAY_ZOOM_POS_Y = play_zoom_last_y; } #endif } static void play_zoom_center_pos_update() { #ifdef IMGPLAY_ZOOM_POS_X if (PLAY_MODE && (int32_t)MEM(IMGPLAY_ZOOM_LEVEL_ADDR) > 5 && IMGPLAY_ZOOM_POS_X && IMGPLAY_ZOOM_POS_Y) { play_zoom_last_x = IMGPLAY_ZOOM_POS_X; play_zoom_last_y = IMGPLAY_ZOOM_POS_Y; } #endif } #endif // FEATURE_QUICK_ZOOM static void tweak_task( void* unused) { extern void movtweak_task_init(); movtweak_task_init(); TASK_LOOP { // keep this task responsive for first 2 seconds after turning display off (for reacting quickly to palette changes etc) static int display_countdown = 40; if (DISPLAY_IS_ON) display_countdown = 40; else if (display_countdown) display_countdown--; msleep(display_countdown || RECORDING || halfshutter_sticky || dofpreview_sticky ? 50 : 500); extern void movtweak_step(); movtweak_step(); #ifdef FEATURE_ZOOM_TRICK_5D3 // not reliable zoom_trick_step(); #endif #ifdef FEATURE_STICKY_HALFSHUTTER if (halfshutter_sticky) fake_halfshutter_step(); #endif #ifdef FEATURE_ARROW_SHORTCUTS arrow_key_step(); #endif #ifdef FEATURE_PLAY_TIMELAPSE // timelapse playback if (timelapse_playback) { if (!PLAY_MODE) { timelapse_playback = 0; continue; } //~ NotifyBox(1000, "Timelapse..."); fake_simple_button(timelapse_playback > 0 ? BGMT_WHEEL_DOWN : BGMT_WHEEL_UP); continue; } #endif // 5D3 already has fast zoom, but still requires some tweaking #ifdef FEATURE_REMEMBER_LAST_ZOOM_POS_5D3 if (quickzoom && quickzoom_pressed && PLAY_MODE) { if (play_zoom_last_x != IMGPLAY_ZOOM_POS_X_CENTER || play_zoom_last_y != IMGPLAY_ZOOM_POS_Y_CENTER) { while ((int32_t)(int32_t)MEM(IMGPLAY_ZOOM_LEVEL_ADDR) <= 5 && PLAY_MODE) msleep(100); msleep(200); if ((int32_t)(int32_t)MEM(IMGPLAY_ZOOM_LEVEL_ADDR) <= 5) continue; play_zoom_center_on_last_af_point(); MEM(IMGPLAY_ZOOM_LEVEL_ADDR) = MIN((int32_t)MEM(IMGPLAY_ZOOM_LEVEL_ADDR) - 1, IMGPLAY_ZOOM_LEVEL_MAX - 1); fake_simple_button(BGMT_WHEEL_RIGHT); } quickzoom_pressed = 0; } play_zoom_center_pos_update(); #endif // faster zoom in play mode #ifdef FEATURE_QUICK_ZOOM if (quickzoom && PLAY_MODE) { if (quickzoom_pressed) { if (quickzoom >= 2 && PLAY_MODE && (int32_t)MEM(IMGPLAY_ZOOM_LEVEL_ADDR) <= 1) { info_led_on(); quickzoom_pressed = 0; #ifdef CONFIG_5DC MEM(IMGPLAY_ZOOM_LEVEL_ADDR) = MAX((int32_t)MEM(IMGPLAY_ZOOM_LEVEL_ADDR), IMGPLAY_ZOOM_LEVEL_MAX - 1); MEM(IMGPLAY_ZOOM_LEVEL_ADDR + 4) = MAX((int32_t)MEM(IMGPLAY_ZOOM_LEVEL_ADDR + 4), IMGPLAY_ZOOM_LEVEL_MAX - 1); fake_simple_button(BGMT_PRESS_ZOOM_IN); fake_simple_button(BGMT_PRESS_UP); fake_simple_button(BGMT_UNPRESS_UDLR); // goes a bit off-center, no big deal #else for (int i = 0; i < 30; i++) { MEM(IMGPLAY_ZOOM_LEVEL_ADDR) = MAX((int32_t)MEM(IMGPLAY_ZOOM_LEVEL_ADDR), IMGPLAY_ZOOM_LEVEL_MAX - 1); MEM(IMGPLAY_ZOOM_LEVEL_ADDR + 4) = MAX((int32_t)MEM(IMGPLAY_ZOOM_LEVEL_ADDR + 4), IMGPLAY_ZOOM_LEVEL_MAX - 1); if (quickzoom == 3) play_zoom_center_on_selected_af_point(); else if (quickzoom == 4) play_zoom_center_on_last_af_point(); fake_simple_button(BGMT_PRESS_ZOOM_IN); msleep(20); } fake_simple_button(BGMT_UNPRESS_ZOOM_IN); #endif msleep(800); // not sure how to tell when it's safe to start zooming out info_led_off(); } else if (quickzoom >= 2 && PLAY_MODE && (int32_t)MEM(IMGPLAY_ZOOM_LEVEL_ADDR) == IMGPLAY_ZOOM_LEVEL_MAX) // already at 100% { msleep(100); MEM(IMGPLAY_ZOOM_LEVEL_ADDR) = 0; MEM(IMGPLAY_ZOOM_LEVEL_ADDR + 4) = 0; fake_simple_button(BGMT_PRESS_ZOOM_OUT); fake_simple_button(BGMT_UNPRESS_ZOOM_OUT); quickzoom_pressed = 0; } else { msleep(300); while (!quickzoom_unpressed && PLAY_MODE) { #ifdef CONFIG_5DC (int32_t)MEM(IMGPLAY_ZOOM_LEVEL_ADDR) = MIN((int32_t)MEM(IMGPLAY_ZOOM_LEVEL_ADDR) + 3, IMGPLAY_ZOOM_LEVEL_MAX); (int32_t)MEM(IMGPLAY_ZOOM_LEVEL_ADDR + 4) = MIN((int32_t)MEM(IMGPLAY_ZOOM_LEVEL_ADDR + 4) + 3, IMGPLAY_ZOOM_LEVEL_MAX); #endif fake_simple_button(BGMT_PRESS_ZOOM_IN); msleep(50); } fake_simple_button(BGMT_UNPRESS_ZOOM_IN); quickzoom_pressed = 0; } } if (get_zoom_out_pressed()) { msleep(300); while (get_zoom_out_pressed() && PLAY_MODE) { #ifdef CONFIG_5DC MEM(IMGPLAY_ZOOM_LEVEL_ADDR) = MAX((int32_t)MEM(IMGPLAY_ZOOM_LEVEL_ADDR) - 3, 0); MEM(IMGPLAY_ZOOM_LEVEL_ADDR + 4) = MAX((int32_t)MEM(IMGPLAY_ZOOM_LEVEL_ADDR + 4) - 3, 0); #endif fake_simple_button(BGMT_PRESS_ZOOM_OUT); msleep(50); } fake_simple_button(BGMT_UNPRESS_ZOOM_OUT); } play_zoom_center_pos_update(); } #endif #ifdef FEATURE_LV_FOCUS_BOX_FAST // faster focus box in liveview if (arrow_pressed && lv && liveview_display_idle()) { if (focus_box_lv_speed) { msleep(200); int delay = 30; while (!arrow_unpressed) { fake_simple_button(arrow_pressed); msleep(delay); if (delay > 10) delay -= 2; } } arrow_pressed = 0; } // faster focus box in playback #ifndef CONFIG_5D3 // doesn't need this, it's already very fast if (arrow_pressed && is_pure_play_photo_mode() && quickzoom && (int32_t)MEM(IMGPLAY_ZOOM_LEVEL_ADDR) > 0) { msleep(200); int delay = 100; while (!arrow_unpressed) { fake_simple_button(arrow_pressed); msleep(delay); if (delay > 30) delay -= 10; } arrow_pressed = 0; } #endif #endif #ifdef FEATURE_STICKY_DOF dofp_update(); #endif #ifdef FEATURE_LV_FOCUS_BOX_AUTOHIDE clear_lv_afframe_if_dirty(); #endif #ifdef FEATURE_LV_BUTTON_RATE play_lv_key_step(); #endif #ifdef FEATURE_UPSIDE_DOWN upside_down_step(); #endif #if defined(FEATURE_LV_SATURATION) || defined(FEATURE_LV_BRIGHTNESS_CONTRAST) || defined(FEATURE_DIGIC_FOCUS_PEAKING) || defined(FEATURE_LV_CRAZY_COLORS) preview_contrast_n_saturation_step(); #endif #ifdef FEATURE_COLOR_SCHEME grayscale_menus_step(); #endif #ifdef FEATURE_IMAGE_POSITION lcd_adjust_position_step(); #endif #ifdef FEATURE_WARNINGS_FOR_BAD_SETTINGS warn_step(); #endif #ifdef FEATURE_LV_DISPLAY_PRESETS // if disp presets is enabled, make sure there are no Canon graphics extern int disp_profiles_0; if (disp_profiles_0 && lv_disp_mode && liveview_display_idle() && !gui_menu_shown()) { fake_simple_button(BGMT_INFO); msleep(200); } #endif /* reset powersave counters for those events don't send a button code, e.g. shutter/aperture change * (GMT_OLC_INFO_CHANGED doesn't reset them, because it's also sent by auto exposure changes) * => we use heuristics like Canon bottom bar or popping up to detect these events */ if (lv_disp_mode == 0 && LV_BOTTOM_BAR_DISPLAYED) { idle_wakeup_reset_counters(0); } #ifdef ISO_ADJUSTMENT_ACTIVE if (ISO_ADJUSTMENT_ACTIVE) { idle_wakeup_reset_counters(0); } #endif } } TASK_CREATE("tweak_task", tweak_task, 0, 0x1e, 0x1000 ); CONFIG_INT("quick.review.allow.zoom", quick_review_allow_zoom, 0); #ifdef FEATURE_IMAGE_REVIEW_PLAY #ifdef CONFIG_5DC static int play_dirty = 0; #endif PROP_HANDLER(PROP_GUI_STATE) { int gui_state = buf[0]; if (gui_state == GUISTATE_QR && image_review_time == 0xff && quick_review_allow_zoom==1 && !is_intervalometer_running() && !is_hdr_bracketing_enabled() && NOT_RECORDING) { fake_simple_button(BGMT_PLAY); } #ifdef CONFIG_5DC play_dirty = 2; #endif } #endif #ifdef FEATURE_SWAP_MENU_ERASE CONFIG_INT("swap.menu", swap_menu, 0); int handle_swap_menu_erase(struct event * event) { if (swap_menu && !IS_FAKE(event)) { if (event->param == BGMT_TRASH) { fake_simple_button(BGMT_MENU); return 0; } if (event->param == BGMT_MENU) { fake_simple_button(BGMT_TRASH); return 0; } } return 1; } #endif #ifdef FEATURE_SWAP_INFO_PLAY CONFIG_INT("swap.info", swap_info, 0); int handle_swap_info_play(struct event * event) { if (swap_info && !IS_FAKE(event)) { if (event->param == BGMT_INFO) { fake_simple_button(BGMT_PLAY); return 0; } if (event->param == BGMT_PLAY) { fake_simple_button(BGMT_INFO); return 0; } } return 1; } #endif #ifdef FEATURE_AUTO_MIRRORING_HACK extern unsigned display_dont_mirror; #endif #ifdef CONFIG_VARIANGLE_DISPLAY void display_orientation_toggle(void* priv, int dir) { int o = DISPLAY_ORIENTATION; if (o < 0 || o > 2) return; o = MOD(o + dir, 3); if (o == 0) NormalDisplay(); else if (o == 1) ReverseDisplay(); else MirrorDisplay(); } #endif CONFIG_INT("digital.zoom.shortcut", digital_zoom_shortcut, 1); static CONFIG_INT("arrows.mode", arrow_keys_mode, 0); static CONFIG_INT("arrows.set", arrow_keys_use_set, 1); #ifdef CONFIG_5D2 static CONFIG_INT("arrows.audio", arrow_keys_audio, 0); static CONFIG_INT("arrows.iso_kelvin", arrow_keys_iso_kelvin, 0); #else #ifdef CONFIG_AUDIO_CONTROLS static CONFIG_INT("arrows.audio", arrow_keys_audio, 1); #else static CONFIG_INT("arrows.audio", arrow_keys_audio_unused, 1); #ifdef FEATURE_ARROW_SHORTCUTS static int arrow_keys_audio = 0; #endif #endif static CONFIG_INT("arrows.iso_kelvin", arrow_keys_iso_kelvin, 1); #endif static CONFIG_INT("arrows.tv_av", arrow_keys_shutter_aperture, 0); static CONFIG_INT("arrows.bright_sat", arrow_keys_bright_sat, 0); #ifdef FEATURE_ARROW_SHORTCUTS static void arrow_key_set_toggle(void* priv, int delta) { arrow_keys_use_set = !arrow_keys_use_set; } static MENU_UPDATE_FUNC(arrow_key_set_display) { MENU_SET_VALUE( arrow_keys_use_set ? "ON" : "OFF" ); MENU_SET_ICON(MNI_BOOL(arrow_keys_use_set), 0); MENU_SET_ENABLED(arrow_keys_use_set); } static int is_arrow_mode_ok(int mode) { switch (mode) { case 0: return 1; case 1: return arrow_keys_audio; case 2: return arrow_keys_iso_kelvin; case 3: return arrow_keys_shutter_aperture; case 4: return arrow_keys_bright_sat; } return 0; } static void arrow_key_mode_toggle() { if (arrow_keys_mode >= 10) // temporarily disabled { arrow_keys_mode = arrow_keys_mode - 10; if (is_arrow_mode_ok(arrow_keys_mode)) return; } do { arrow_keys_mode = MOD(arrow_keys_mode + 1, 5); } while (!is_arrow_mode_ok(arrow_keys_mode)); NotifyBoxHide(); } static void shutter_180() { lens_set_rawshutter(shutter_ms_to_raw(1000 / video_mode_fps / 2)); } static void brightness_saturation_reset(void); #ifdef FEATURE_WHITE_BALANCE int handle_push_wb(struct event * event) { if (!lv) return 1; if (gui_menu_shown()) return 1; #ifdef CONFIG_5D2 extern thunk LiveViewWbApp_handler; if (event->param == BGMT_PRESS_SET && (intptr_t)get_current_dialog_handler() == (intptr_t)&LiveViewWbApp_handler) { kelvin_n_gm_auto(); return 0; } #endif #ifdef CONFIG_5D3 if (event->param == BGMT_RATE && liveview_display_idle()) { // only do this if no arrow shortcut is enabled if (!arrow_keys_audio && !arrow_keys_iso_kelvin && !arrow_keys_shutter_aperture && !arrow_keys_bright_sat) { kelvin_n_gm_auto(); return 0; } } #endif return 1; } #endif int handle_arrow_keys(struct event * event) { #ifdef FEATURE_WHITE_BALANCE if (handle_push_wb(event)==0) return 0; #endif if (gui_menu_shown()) return 1; if (!liveview_display_idle()) return 1; #ifdef CONFIG_4_3_SCREEN if (lv_dispsize > 1) return 1; // flickers in zoom mode => completely disable them #endif // if no shortcut is enabled, do nothing if (!arrow_keys_audio && !arrow_keys_iso_kelvin && !arrow_keys_shutter_aperture && !arrow_keys_bright_sat) return 1; if (event->param == BGMT_PRESS_HALFSHUTTER || event->param == BGMT_LV) { if (arrow_keys_mode%10) { arrow_keys_mode = 10 + (arrow_keys_mode%10); // temporarily disable NotifyBoxHide(); } return 1; } #ifdef CONFIG_550D static int flash_movie_pressed; if (BGMT_FLASH_MOVIE) { flash_movie_pressed = BGMT_PRESS_FLASH_MOVIE; if (flash_movie_pressed) arrow_key_mode_toggle(); return !flash_movie_pressed; } static int t_press = 0; if (BGMT_PRESS_AV) { t_press = get_ms_clock_value(); } if (BGMT_UNPRESS_AV) { int t_unpress = get_ms_clock_value(); if (t_unpress - t_press < 400) arrow_key_mode_toggle(); } #endif #ifdef CONFIG_600D extern int disp_zoom_pressed; if (event->param == BGMT_UNPRESS_DISP && !disp_zoom_pressed) { arrow_key_mode_toggle(); return 1; } #endif #ifdef CONFIG_60D static int metering_btn_pressed; if (BGMT_METERING_LV) { metering_btn_pressed = BGMT_PRESS_METERING_LV; if (metering_btn_pressed) arrow_key_mode_toggle(); return !metering_btn_pressed; } #endif #ifdef CONFIG_50D if (event->param == BGMT_FUNC) { arrow_key_mode_toggle(); return 0; } #endif #if defined(CONFIG_5D2) || defined(CONFIG_7D) if (event->param == BGMT_PICSTYLE) { arrow_key_mode_toggle(); return 0; } #endif #ifdef CONFIG_5D3 if (event->param == BGMT_RATE) { arrow_key_mode_toggle(); return 0; } #endif #ifdef CONFIG_6D if (event->param == BGMT_AFPAT_UNPRESS) { arrow_key_mode_toggle(); return 0; } #endif if (arrow_keys_mode && liveview_display_idle() && !gui_menu_shown()) { // maybe current mode is no longer enabled in menu if (!is_arrow_mode_ok(arrow_keys_mode)) return 1; if (arrow_keys_use_set && NOT_RECORDING) if (event->param == BGMT_PRESS_SET #ifdef BGMT_JOY_CENTER || event->param == BGMT_JOY_CENTER #endif ) { switch (arrow_keys_mode) { #ifdef FEATURE_INPUT_SOURCE case 1: input_toggle(); break; #endif #ifdef FEATURE_WHITE_BALANCE case 2: redraw(); kelvin_n_gm_auto(); if (arrow_keys_mode%10) arrow_keys_mode = 10 + (arrow_keys_mode%10); // temporarily disable break; #endif case 3: shutter_180(); break; case 4: brightness_saturation_reset(); break; default: return 1; } lens_display_set_dirty(); return 0; } if (event->param == BGMT_PRESS_UP) { switch (arrow_keys_mode) { #ifdef FEATURE_HEADPHONE_OUTPUT_VOLUME case 1: out_volume_up(); break; #endif #ifdef FEATURE_WHITE_BALANCE case 2: kelvin_toggle((void*)-1, 1); break; #endif #ifdef FEATURE_EXPO_APERTURE case 3: aperture_toggle((void*)-1, 1); break; #endif case 4: adjust_saturation_level(1); break; default: return 1; } keyrepeat_ack(event->param); lens_display_set_dirty(); return 0; } if (event->param == BGMT_PRESS_DOWN) { switch (arrow_keys_mode) { #ifdef FEATURE_HEADPHONE_OUTPUT_VOLUME case 1: out_volume_down(); break; #endif #ifdef FEATURE_WHITE_BALANCE case 2: kelvin_toggle((void*)-1, -1); break; #endif #ifdef FEATURE_EXPO_APERTURE case 3: aperture_toggle((void*)-1, -1); break; #endif case 4: adjust_saturation_level(-1); break; default: return 1; } lens_display_set_dirty(); keyrepeat_ack(event->param); return 0; } if (event->param == BGMT_PRESS_LEFT) { switch (arrow_keys_mode) { #ifdef FEATURE_ANALOG_GAIN case 1: volume_down(); break; #endif #ifdef FEATURE_EXPO_ISO case 2: iso_toggle((void*)-1, -1); break; #endif #ifdef FEATURE_EXPO_SHUTTER case 3: shutter_toggle((void*)-1, -1); break; #endif case 4: adjust_backlight_level(-1); break; default: return 1; } lens_display_set_dirty(); keyrepeat_ack(event->param); return 0; } if (event->param == BGMT_PRESS_RIGHT) { switch (arrow_keys_mode) { #ifdef FEATURE_ANALOG_GAIN case 1: volume_up(); break; #endif #ifdef FEATURE_EXPO_ISO case 2: iso_toggle((void*)-1, 1); break; #endif #ifdef FEATURE_EXPO_SHUTTER case 3: shutter_toggle((void*)-1, 1); break; #endif case 4: adjust_backlight_level(1); break; default: return 1; } lens_display_set_dirty(); keyrepeat_ack(event->param); return 0; } } return 1; } // only for toggling shortcuts in 500D static void arrow_key_step() { if (!lv) return; if (gui_menu_shown()) return; #ifdef FEATURE_LCD_SENSOR_SHORTCUTS extern int lcd_release_running; int lcd = get_lcd_sensor_shortcuts() && display_sensor && DISPLAY_SENSOR_POWERED && !lcd_release_running; static int prev_lcd = 0; if (lcd && !prev_lcd) { arrow_key_mode_toggle(); } prev_lcd = lcd; #endif } static MENU_UPDATE_FUNC(arrow_key_check) { #ifdef FEATURE_LCD_SENSOR_SHORTCUTS int lcd_sensor_mandatory = streq(ARROW_MODE_TOGGLE_KEY, "LCD sensor"); if (!lcd_sensor_shortcuts) { if (lcd_sensor_mandatory) { MENU_SET_WARNING(MENU_WARN_NOT_WORKING, "To use this feature, enable LCD Sensor Shortcuts in Misc Key Settings."); } else { MENU_SET_WARNING(MENU_WARN_INFO, "To use LCD sensor, enable LCD Sensor Shortcuts in Misc Key Settings."); } } else if (!get_lcd_sensor_shortcuts()) { MENU_SET_WARNING(lcd_sensor_mandatory ? MENU_WARN_NOT_WORKING : MENU_WARN_INFO, "LCD Sensor Shortcuts are disabled in this mode (see Misc Key Settings)." ); } #endif } int arrow_keys_shortcuts_active() { return (arrow_keys_mode && arrow_keys_mode < 10 && is_arrow_mode_ok(arrow_keys_mode)); } #endif // FEATURE_ARROW_SHORTCUTS void display_shortcut_key_hints_lv() { #if defined(FEATURE_ARROW_SHORTCUTS) || defined(FEATURE_FOLLOW_FOCUS) static int old_mode = 0; int mode = 0; if (!liveview_display_idle()) return; #ifdef CONFIG_4_3_SCREEN if (lv_dispsize > 1) return; // flickers in zoom mode #endif #ifdef FEATURE_LCD_SENSOR_SHORTCUTS extern int lcd_release_running; int lcd = get_lcd_sensor_shortcuts() && display_sensor && DISPLAY_SENSOR_POWERED && !lcd_release_running; #else int lcd = 0; #endif if (arrow_keys_shortcuts_active()) mode = arrow_keys_mode; else if (!mode && is_follow_focus_active() && get_follow_focus_mode()==0 && !is_manual_focus() && !lcd) mode = 10; if (mode == 0 && old_mode == 0) return; get_yuv422_vram(); int x0 = os.x0 + os.x_ex/2; int y0 = os.y0 + os.y_ex/2; int fnt = FONT(FONT_MED, COLOR_WHITE, 0) | FONT_ALIGN_CENTER | FONT_ALIGN_FILL | FONT_TEXT_WIDTH(100); if (mode != old_mode) { bmp_printf(fnt, x0 - 150, y0 - font_med.height/2, " "); bmp_printf(fnt, x0 + 150, y0 - font_med.height/2, " "); bmp_printf(fnt, x0, y0 - 100 - font_med.height/2, " "); bmp_printf(fnt, x0, y0 + 100 - font_med.height/2, " "); bmp_printf(fnt, x0, y0 - font_med.height/2," "); if (!should_draw_zoom_overlay()) crop_set_dirty(20); } fnt = SHADOW_FONT(FONT(FONT_MED, COLOR_WHITE, COLOR_BLACK)) | FONT_ALIGN_CENTER; if (mode == 1) { #ifdef FEATURE_ANALOG_GAIN bmp_printf(fnt, x0 - 150, y0 - font_med.height/2, "-Rec"); bmp_printf(fnt, x0 + 150, y0 - font_med.height/2, "Rec+"); #endif #ifdef FEATURE_HEADPHONE_OUTPUT_VOLUME bmp_printf(fnt, x0, y0 - 100 - font_med.height/2, "Out+"); bmp_printf(fnt, x0, y0 + 100 - font_med.height/2, "-Out"); #endif #ifdef FEATURE_INPUT_SOURCE if (arrow_keys_use_set && NOT_RECORDING) bmp_printf(SHADOW_FONT(FONT_MED), x0 - font_med.width*3, y0 - font_med.height/2, "Input"); #endif } else if (mode == 2) { #ifdef FEATURE_EXPO_ISO bmp_printf(fnt, x0 - 150, y0 - font_med.height/2, "-ISO"); bmp_printf(fnt, x0 + 150, y0 - font_med.height/2, "ISO+"); #endif #ifdef FEATURE_WHITE_BALANCE bmp_printf(fnt, x0, y0 - 100 - font_med.height/2, "Kel+"); bmp_printf(fnt, x0, y0 + 100 - font_med.height/2, "-Kel"); if (arrow_keys_use_set && NOT_RECORDING) bmp_printf(SHADOW_FONT(FONT_MED), x0 - font_med.width*3, y0 - font_med.height/2, "PushWB"); #endif } else if (mode == 3) { #ifdef FEATURE_EXPO_SHUTTER bmp_printf(fnt, x0 - 150, y0 - font_med.height/2, "-Tv "); bmp_printf(fnt, x0 + 150, y0 - font_med.height/2, " Tv+"); #endif #ifdef FEATURE_EXPO_APERTURE bmp_printf(fnt, x0, y0 - 100 - font_med.height/2, " Av+"); bmp_printf(fnt, x0, y0 + 100 - font_med.height/2, "-Av "); #endif if (arrow_keys_use_set && NOT_RECORDING) bmp_printf(SHADOW_FONT(FONT_MED), x0 - font_med.width*3, y0 - font_med.height/2, "180deg"); } else if (mode == 4) { bmp_printf(fnt, x0 - 150, y0 - font_med.height/2, "-Bri"); bmp_printf(fnt, x0 + 150, y0 - font_med.height/2, "Bri+"); bmp_printf(fnt, x0, y0 - 100 - font_med.height/2, "Sat+"); bmp_printf(fnt, x0, y0 + 100 - font_med.height/2, "-Sat"); if (arrow_keys_use_set && NOT_RECORDING) bmp_printf(SHADOW_FONT(FONT_MED), x0 - font_med.width*3, y0 - font_med.height/2, "Reset"); } else if (mode == 10) { #ifdef FEATURE_FOLLOW_FOCUS const int xf = x0; const int yf = y0; const int xs = 150; bmp_printf(fnt, xf - xs, yf - font_med.height/2, get_follow_focus_dir_h() > 0 ? " +FF" : " -FF"); bmp_printf(fnt, xf + xs, yf - font_med.height/2, get_follow_focus_dir_h() > 0 ? "FF- " : "FF+ "); bmp_printf(fnt, xf, yf - 100 - font_med.height/2, get_follow_focus_dir_v() > 0 ? "FF++" : "FF--"); bmp_printf(fnt, xf, yf + 100 - font_med.height/2, get_follow_focus_dir_v() > 0 ? "FF--" : "FF++"); #endif } old_mode = mode; #endif } #ifdef FEATURE_ZOOM_TRICK_5D3 // not reliable // some buttons send an unknown button event (GMT_GUICMD_PRESS_BUTTON_SOMETHING) // including MFn, light and old zoom button // but a lot of other buttons send this event, and also other events // // so: guesswork: if the unknown event is sent and no other events are sent at the same moment (+/- 200 ms or so), // consider it as shortcut key for LiveView 5x/10x zoom CONFIG_INT("zoom.trick", zoom_trick, 0); static int countdown_for_unknown_button = 0; static int timestamp_for_unknown_button = 0; static int numclicks_for_unknown_button = 0; void zoom_trick_step() { if (!zoom_trick) return; if (!lv && !PLAY_OR_QR_MODE) return; int current_timestamp = get_ms_clock_value(); static int prev_timestamp = 0; if (prev_timestamp != current_timestamp) { if (countdown_for_unknown_button) countdown_for_unknown_button--; } prev_timestamp = current_timestamp; if (lv && !liveview_display_idle()) { timestamp_for_unknown_button = 0; numclicks_for_unknown_button = 0; return; } if (!timestamp_for_unknown_button) return; if ((lv && current_timestamp - timestamp_for_unknown_button >= 300 && numclicks_for_unknown_button == 2) || //~ (PLAY_MODE && is_pure_play_photo_mode() && current_timestamp - timestamp_for_unknown_button >= 100) || (PLAY_OR_QR_MODE && current_timestamp - timestamp_for_unknown_button >= 100)) { // action! if (zoom_trick == 1) fake_simple_button(BGMT_PRESS_ZOOM_IN); if (zoom_trick == 2) arrow_key_mode_toggle(); timestamp_for_unknown_button = 0; numclicks_for_unknown_button = 0; } } PROP_HANDLER(PROP_AF_MODE) { countdown_for_unknown_button = 2; } int handle_zoom_trick_event(struct event * event) { if (!zoom_trick) return 1; if (event->param == GMT_GUICMD_PRESS_BUTTON_SOMETHING) { if (!countdown_for_unknown_button) { int t = get_ms_clock_value(); if (t - timestamp_for_unknown_button > 500) numclicks_for_unknown_button = 0; timestamp_for_unknown_button = t; numclicks_for_unknown_button++; } } else { timestamp_for_unknown_button = 0; countdown_for_unknown_button = 2; numclicks_for_unknown_button = 0; } return 1; } #endif #ifdef FEATURE_WARNINGS_FOR_BAD_SETTINGS static CONFIG_INT("warn.mode", warn_mode, 0); static CONFIG_INT("warn.picq", warn_picq, 0); static CONFIG_INT("warn.alo", warn_alo, 0); static CONFIG_INT("warn.wb", warn_wb, 0); static CONFIG_INT("warn.mf", warn_mf, 0); static CONFIG_INT("warn.msg", warn_msg, 0); static int warn_code = 0; static char* get_warn_msg(char* separator) { static char msg[15 + 24 + 15 + 21 + 20 + 10 /* Termination \0 and some spare */]; msg[0] = '\0'; // Max length: 15 (only one can be active) if (warn_code & 1 && warn_mode==1) { STR_APPEND(msg, "Mode is not M%s", separator); } if (warn_code & 1 && warn_mode==2) { STR_APPEND(msg, "Mode is not Av%s", separator); } if (warn_code & 1 && warn_mode==3) { STR_APPEND(msg, "Mode is not Tv%s", separator); } if (warn_code & 1 && warn_mode==4) { STR_APPEND(msg, "Mode is not P%s", separator); } // Max length: 24 (only one can be active) if (warn_code & 2 && warn_picq==1) { STR_APPEND(msg, "Pic quality is not RAW%s", separator); } if (warn_code & 2 && warn_picq==2) { STR_APPEND(msg, "Pic quality is not fine%s", separator); } // Length: 15 if (warn_code & 4) { STR_APPEND(msg, "ALO is enabled%s", separator); } // Length: 21 if (warn_code & 8) { STR_APPEND(msg, "WB isn't set to auto%s", separator); } // Max length: 20 (only one can be active) if (warn_code & 16 && warn_mf == 1) { STR_APPEND(msg, "Focus is not auto%s", separator); } if (warn_code & 16 && warn_mf == 2) { STR_APPEND(msg, "Focus is not manual%s", separator); } return msg; } static void warn_action(int code) { // warn_msg: // 0 "LED, popup, beep", // 1 "LED, popup, rep. beep", // 2 "LED, popup", // 3 "popup, beep", // 4 "popup, rep. beep", // 5 "popup only" // 6 "rep. popup" bool led_active = code && warn_msg < 3; bool repeated_beep_active = code && (warn_msg == 1 || warn_msg == 4); bool beep_active = code && (warn_msg == 0 || warn_msg == 1 || warn_msg == 3 || warn_msg == 4); bool popup_repeated = warn_msg == 6; // blink LED every second, if active if (led_active || repeated_beep_active) { static int aux = 0; if (should_run_polling_action(1000, &aux)) { if (led_active) { static int k = 0; k++; if (k%2) info_led_on(); else info_led_off(); } if (repeated_beep_active) { beep(); } } } // when warning condition changes, beep static int prev_code = 0; if (code != prev_code) { if (code) // not good { if (beep_active) beep(); } else // OK, back to good configuration { info_led_blink(2,50,50); } } prev_code = code; // when warning condition changes, and display is on, show what's the problem static int prev_code_d = 0; if ((code != prev_code_d || popup_repeated) && DISPLAY_IS_ON && !gui_menu_shown()) { NotifyBoxHide(); msleep(200); if (code) NotifyBox(3000, get_warn_msg("\n")); prev_code_d = code; } } static void warn_step() { warn_code = 0; if (shooting_mode != SHOOTMODE_MOVIE) { if (warn_mode == 1 && shooting_mode != SHOOTMODE_M) warn_code |= 1; if (warn_mode == 2 && shooting_mode != SHOOTMODE_AV) warn_code |= 1; if (warn_mode == 3 && shooting_mode != SHOOTMODE_TV) warn_code |= 1; if (warn_mode == 4 && shooting_mode != SHOOTMODE_P) warn_code |= 1; } int raw = pic_quality & 0x60000; int rawsize = pic_quality & 0xF; if (warn_picq == 1 && (!raw || rawsize)) warn_code |= 2; if (warn_picq == 2 && !(pic_quality == PICQ_LARGE_FINE || pic_quality == PICQ_RAW || pic_quality == PICQ_RAW_JPG_LARGE_FINE)) warn_code |= 2; if (warn_alo && get_alo() != ALO_OFF) warn_code |= 4; if (warn_wb && lens_info.wb_mode) warn_code |= 8; if (warn_mf == 1 && is_manual_focus()) warn_code |= 16; if (warn_mf == 2 && !is_manual_focus()) warn_code |= 16; warn_action(warn_code); } static MENU_UPDATE_FUNC(warn_display) { if (warn_code) MENU_SET_WARNING(MENU_WARN_ADVICE, get_warn_msg(", ")); } #endif static struct menu_entry key_menus[] = { #if defined(FEATURE_LV_FOCUS_BOX_FAST) || defined(FEATURE_LV_FOCUS_BOX_SNAP) || defined(FEATURE_LV_FOCUS_BOX_AUTOHIDE) { .name = "Focus box settings", .select = menu_open_submenu, .submenu_width = 700, .help = "Tweaks for LiveView focus box: move faster, snap to points.", .depends_on = DEP_LIVEVIEW, .children = (struct menu_entry[]) { #ifdef FEATURE_LV_FOCUS_BOX_FAST { .name = "Speed", .priv = &focus_box_lv_speed, .max = 1, .icon_type = IT_BOOL, .choices = (const char *[]) {"Normal (OFF)", "Fast"}, .help = "Move the focus box faster (in LiveView).", }, #endif #ifdef FEATURE_LV_FOCUS_BOX_SNAP { .name = "Snap points", .priv = &focus_box_lv_jump, #ifdef FEATURE_LV_FOCUS_BOX_SNAP_TO_X5_RAW .max = 5, #else .max = 4, #endif .icon_type = IT_DICE_OFF, .choices = (const char *[]) { "Center (OFF)", "Center/Top/Right", "Center/T/R/B/L", "Center/TL/TR/BR/BL", "Center + 8 pts" #ifdef FEATURE_LV_FOCUS_BOX_SNAP_TO_X5_RAW ,"Center to x5 RAW" #endif }, .help = "Snap the focus box to preset points (press CENTER key)", }, #endif #ifdef FEATURE_LV_FOCUS_BOX_AUTOHIDE { .name = "Display", .priv = &af_frame_autohide, .max = 1, .choices = (const char *[]) {"Show", "Auto-Hide"}, .help = "You can hide the focus box (the little white rectangle).", .icon_type = IT_DISABLE_SOME_FEATURE, //.essential = FOR_LIVEVIEW, }, #endif MENU_EOL, } }, #endif #ifdef FEATURE_ARROW_SHORTCUTS { .name = "Arrow/SET shortcuts", .select = menu_open_submenu, .update = arrow_key_check, .submenu_width = 650, .help = "Choose functions for arrows keys. Toggle w. " ARROW_MODE_TOGGLE_KEY ".", .depends_on = DEP_LIVEVIEW, .children = (struct menu_entry[]) { #ifdef CONFIG_AUDIO_CONTROLS { .name = "Audio Gain", .priv = &arrow_keys_audio, .max = 1, .help = "LEFT/RIGHT: input gain. UP/DOWN: output gain. SET: Input.", }, #endif { .name = "ISO/Kelvin", .priv = &arrow_keys_iso_kelvin, .max = 1, .help = "LEFT/RIGHT: ISO. UP/DN: Kelvin white balance. SET: PushWB.", }, { .name = "Shutter/Aperture", .priv = &arrow_keys_shutter_aperture, .max = 1, .help = "LEFT/RIGHT: Shutter. UP/DN: Aperture. SET: 180d shutter.", }, { .name = "LCD Bright/Saturation", .priv = &arrow_keys_bright_sat, .max = 1, .help = "LEFT/RIGHT: LCD bright. UP/DN: LCD saturation. SET: reset.", }, { .name = "Use SET button", .select = arrow_key_set_toggle, // use a function => this item will not be considered for submenu color .update = arrow_key_set_display, .help = "Enables functions for SET when you use arrow shortcuts.", }, MENU_EOL, }, }, #endif #if defined(FEATURE_LCD_SENSOR_SHORTCUTS) || defined(FEATURE_STICKY_DOF) || defined(FEATURE_STICKY_HALFSHUTTER) || defined(FEATURE_SWAP_MENU_ERASE) || defined(FEATURE_SWAP_INFO_PLAY) || defined(FEATURE_DIGITAL_ZOOM_SHORTCUT) { .name = "Misc key settings", .select = menu_open_submenu, .submenu_width = 656, .help = "Misc options related to shortcut keys.", .children = (struct menu_entry[]) { #ifdef FEATURE_LCD_SENSOR_SHORTCUTS { .name = "LCD Sensor Shortcuts", .priv = &lcd_sensor_shortcuts, .max = 2, .choices = (const char *[]) {"OFF", "ON", "Movie"}, .help = "Use the LCD face sensor as an extra key in ML.", }, #endif #ifdef FEATURE_STICKY_DOF { .name = "Sticky DOF Preview", .priv = &dofpreview_sticky, .max = 1, .help = "Makes the DOF preview button sticky (press to toggle).", }, #endif #ifdef FEATURE_STICKY_HALFSHUTTER { .name = "Sticky HalfShutter", .priv = &halfshutter_sticky, .max = 1, .help = "Makes the half-shutter button sticky (press to toggle).", }, #endif #ifdef FEATURE_SWAP_MENU_ERASE { .name = "Swap MENU <--> ERASE", .priv = &swap_menu, .max = 1, .help = "Swaps MENU and ERASE buttons." }, #endif #ifdef FEATURE_SWAP_INFO_PLAY { .name = "Swap INFO <--> PLAY", .priv = &swap_info, .max = 1, .help = "Swaps INFO and PLAY buttons." }, #endif #ifdef FEATURE_DIGITAL_ZOOM_SHORTCUT { .name = "DigitalZoom Shortcut", .priv = &digital_zoom_shortcut, .max = 1, .choices = (const char *[]) {"3x...10x", "1x, 3x"}, .help = "Movie: DISP + Zoom In toggles between 1x and 3x modes." }, #endif MENU_EOL }, }, #endif }; static struct menu_entry tweak_menus[] = { #ifdef FEATURE_WARNINGS_FOR_BAD_SETTINGS { .name = "Warning for bad settings", .select = menu_open_submenu, .update = warn_display, .help = "Warn if some of your settings are changed by mistake.", .submenu_width = 700, .children = (struct menu_entry[]) { { .name = "Mode warning", .priv = &warn_mode, .max = 4, .icon_type = IT_DICE_OFF, .choices = (const char *[]) {"OFF", "other than M", "other than Av", "other than Tv", "other than P"}, .help = "Warn if you turn the mode dial to some other position.", }, { .name = "Quality warning", .priv = &warn_picq, .max = 2, .choices = (const char *[]) {"OFF", "other than RAW", "other than fine"}, .help = "Warn if you change the picture quality to something else.", }, { .name = "ALO warning", .priv = &warn_alo, .max = 1, .choices = (const char *[]) {"OFF", "other than OFF"}, .help = "Warn if you enable ALO by mistake.", }, { .name = "WB warning", .priv = &warn_wb, .max = 1, .choices = (const char *[]) {"OFF", "other than AWB"}, .help = "Warn if you disable AWB by mistake.", }, { .name = "AF/MF warning", .priv = &warn_mf, .max = 2, .choices = (const char *[]) {"OFF", "other than AF", "other than MF"}, .help = "Warn on Manual / Automatic Focus", }, { .name = "Warning message", .priv = &warn_msg, .max = 6, .choices = (const char *[]) {"LED, popup, beep", "LED, popup, rep. beep", "LED, popup", "popup, beep", "popup, rep. beep", "popup only", "rep. popup"}, .help = "Warn type, LED, Messagebox, Beep", }, MENU_EOL, }, }, #endif }; #ifdef FEATURE_UPSIDE_DOWN extern int menu_upside_down; // reverse arrow keys int handle_upside_down(struct event * event) { // only reverse first arrow press // then wait for unpress event (or different arrow press) before reversing again extern int menu_upside_down; static int last_arrow_faked = 0; if (menu_upside_down && !IS_FAKE(event) && last_arrow_faked) { switch (event->param) { #ifdef BGMT_UNPRESS_UDLR case BGMT_UNPRESS_UDLR: #else case BGMT_UNPRESS_LEFT: case BGMT_UNPRESS_RIGHT: case BGMT_UNPRESS_UP: case BGMT_UNPRESS_DOWN: #endif last_arrow_faked = 0; return 1; } } if (menu_upside_down && !IS_FAKE(event) && last_arrow_faked != event->param) { switch (event->param) { case BGMT_PRESS_LEFT: last_arrow_faked = BGMT_PRESS_RIGHT; break; case BGMT_PRESS_RIGHT: last_arrow_faked = BGMT_PRESS_LEFT; break; case BGMT_PRESS_UP: last_arrow_faked = BGMT_PRESS_DOWN; break; case BGMT_PRESS_DOWN: last_arrow_faked = BGMT_PRESS_UP; break; #ifdef BGMT_PRESS_UP_LEFT case BGMT_PRESS_UP_LEFT: last_arrow_faked = BGMT_PRESS_DOWN_RIGHT; break; case BGMT_PRESS_DOWN_RIGHT: last_arrow_faked = BGMT_PRESS_UP_LEFT; break; case BGMT_PRESS_UP_RIGHT: last_arrow_faked = BGMT_PRESS_DOWN_LEFT; break; case BGMT_PRESS_DOWN_LEFT: last_arrow_faked = BGMT_PRESS_UP_RIGHT; break; #endif default: return 1; } fake_simple_button(last_arrow_faked); return 0; } return 1; } static void upside_down_step() { extern int menu_upside_down; if (menu_upside_down) { if (!gui_menu_shown()) { get_yuv422_vram(); bmp_draw_to_idle(1); canon_gui_disable_front_buffer(0); int voffset = (lv || PLAY_MODE || QR_MODE) ? (os.y0 + os.y_ex/2 - (BMP_H_PLUS+BMP_H_MINUS)/2) * 2 : 0; BMP_LOCK( if (zebra_should_run()) bmp_flip_ex(bmp_vram_real(), bmp_vram_idle(), (void*)get_bvram_mirror(), voffset); else bmp_flip(bmp_vram_real(), bmp_vram_idle(), voffset); ) } //~ msleep(100); } } #endif void screenshot_start(); #ifdef FEATURE_EXPSIM struct menu_entry expo_tweak_menus[] = { { #ifdef CONFIG_EXPSIM_MOVIE .name = "LV Display", .max = 2, .choices = (const char *[]) {"Photo, no ExpSim", "Photo, ExpSim", "Movie"}, .icon_type = IT_DICE, .help = "Exposure simulation (LiveView display type).", #else .name = "ExpSim", .max = 1, .help = "Exposure simulation.", #endif .priv = &_expsim, .select = expsim_toggle, .update = expsim_display, .depends_on = DEP_LIVEVIEW, }, }; #endif static CONFIG_INT("lv.bri", preview_brightness, 0); // range: 0-2 static CONFIG_INT("lv.con", preview_contrast, 0); // range: -3:3 static CONFIG_INT("lv.sat", preview_saturation, 0); // range: -2:2, 3 special #define PREVIEW_BRIGHTNESS_INDEX preview_brightness #define PREVIEW_CONTRAST_INDEX (preview_contrast + 3) #define PREVIEW_SATURATION_INDEX_RAW (preview_saturation + 2) // when adjusting WB, you can see color casts easier if saturation is increased #define PREVIEW_SATURATION_BOOST_WB (preview_saturation == 3) #define PREVIEW_SATURATION_INDEX (PREVIEW_SATURATION_BOOST_WB ? (is_adjusting_wb() ? 4 : 2) : PREVIEW_SATURATION_INDEX_RAW) #define PREVIEW_SATURATION_GRAYSCALE (preview_saturation == -2) #define PREVIEW_CONTRAST_AUTO (preview_contrast == 3) static CONFIG_INT("lv.crazy", preview_crazy, 0); // range: 0:2 static CONFIG_INT("lv.peak", preview_peaking, 0); // range: 0:2 CONFIG_INT("bmp.color.scheme", bmp_color_scheme, 0); static CONFIG_INT("lcd.adjust.position", lcd_adjust_position, 0); static int focus_peaking_grayscale_running() { extern int focus_peaking_grayscale; return focus_peaking_grayscale && is_focus_peaking_enabled() && !focus_peaking_as_display_filter() && zebra_should_run() ; } #ifdef FEATURE_LV_SATURATION static int is_adjusting_wb() { #if defined(CONFIG_5D2) || defined(CONFIG_5D3) // these cameras have a transparent LiveView dialog for adjusting Kelvin white balance // (maybe 7D too) extern thunk LiveViewWbApp_handler; if ((intptr_t)get_current_dialog_handler() == (intptr_t)&LiveViewWbApp_handler) return 1; #endif if (lv && gui_menu_shown() && menu_active_but_hidden() && is_menu_entry_selected("Expo", "WhiteBalance")) return 1; return 0; } #endif static void preview_contrast_n_saturation_step() { if (ml_shutdown_requested) return; if (!DISPLAY_IS_ON) return; #ifdef CONFIG_5DC if (!PLAY_OR_QR_MODE) return; // can't check current saturation value => update saturation only twice per playback session // actually this register looks quite safe to write, but... just in case if (play_dirty) play_dirty--; else return; msleep(100); #else if (!lv) return; #endif #ifdef FEATURE_DIGIC_FOCUS_PEAKING static int peaking_hs_last_press = 0; int halfshutter_pressed = get_halfshutter_pressed(); if (halfshutter_pressed) { peaking_hs_last_press = get_ms_clock_value(); } int preview_peaking_force_normal_image = halfshutter_pressed || /* show normal image on half-hutter press */ get_ms_clock_value() < peaking_hs_last_press + 500; /* and keep it at least 500ms (avoids flicker with fast toggling) */ #endif #ifdef FEATURE_LV_SATURATION int saturation_register = 0xC0F140c4; #ifndef CONFIG_5DC int current_saturation = (int) shamem_read(saturation_register); #ifdef FEATURE_LV_CRAZY_COLORS current_saturation &= 0xFF00FF; #else current_saturation &= 0xFF; #endif #endif static int saturation_values[] = {0,0x40,0x80,0xC0,0xFF}; int desired_saturation = saturation_values[PREVIEW_SATURATION_INDEX]; if (focus_peaking_grayscale_running()) desired_saturation = 0; #ifdef FEATURE_DIGIC_FOCUS_PEAKING if (preview_peaking == 2 && !preview_peaking_force_normal_image) desired_saturation = 0; else if (preview_peaking == 3 && !preview_peaking_force_normal_image) desired_saturation = 0x40; #endif #ifdef FEATURE_LV_CRAZY_COLORS if (preview_crazy == 2) desired_saturation |= 0x10000; #endif #ifdef CONFIG_5DC EngDrvOut(saturation_register, desired_saturation | (desired_saturation<<8)); return; // contrast not working, freezes the camera #else if (current_saturation != desired_saturation) { EngDrvOutLV(saturation_register, desired_saturation | (desired_saturation<<8)); } #endif #endif #ifdef FEATURE_LV_BRIGHTNESS_CONTRAST int brightness_contrast_register = 0xC0F141B8; int current_contrast = (int) shamem_read(brightness_contrast_register); // -------- xxxxxxxx -------- -------- brightness (offset): 8bit signed // -------- -------- -------- xxxxxxxx contrast (gain factor): 8bit unsigned, 0x80 = 1.0 // mid value = (int8_t)brightness + 128 * (uint8_t) contrast / 0x80 // default set (at brightness 0): mid value = 128 // at brightness 1: mid value = 160 // at brightness 2: mid value = 192 static int contrast_values_at_brigthness_0[] = {0x7F0000, 0x400040, 0x200060, 0x80, 0xE000A0, 0xC000C0}; static int contrast_values_at_brigthness_1[] = {0x7F0000, 0x600040, 0x400060, 0x200080, 0x0000A0, 0xe000C0}; static int contrast_values_at_brigthness_2[] = {0x7F0000, 0x7F0040, 0x600060, 0x400080, 0x2000A0, 0x0000C0}; int desired_contrast = 0x80; if (PREVIEW_CONTRAST_AUTO) // auto contrast { // normal brightness => normal contrast // high brightness => low contrast // very high brightness => very low contrast if (preview_brightness == 0) desired_contrast = contrast_values_at_brigthness_0[3]; else if (preview_brightness == 1) desired_contrast = contrast_values_at_brigthness_1[2]; else if (preview_brightness == 2) desired_contrast = contrast_values_at_brigthness_2[1]; } else // manual contrast { if (preview_brightness == 0) desired_contrast = contrast_values_at_brigthness_0[PREVIEW_CONTRAST_INDEX]; else if (preview_brightness == 1) desired_contrast = contrast_values_at_brigthness_1[PREVIEW_CONTRAST_INDEX]; else if (preview_brightness == 2) desired_contrast = contrast_values_at_brigthness_2[PREVIEW_CONTRAST_INDEX]; } #ifdef FEATURE_DIGIC_FOCUS_PEAKING if ((preview_peaking == 2 || preview_peaking == 3) && !preview_peaking_force_normal_image) desired_contrast = contrast_values_at_brigthness_2[4]; #endif if (gui_menu_shown() && !menu_active_but_hidden()) desired_contrast = contrast_values_at_brigthness_0[3]; // do not apply this adjustment while ML menu is on (so you can read it in low contrast modes) if (current_contrast != desired_contrast) { EngDrvOutLV(brightness_contrast_register, desired_contrast); } #endif #ifdef FEATURE_LV_CRAZY_COLORS int crazy_register = 0xC0F14040; static int crazy_dirty = 0; int current_crazy_value = (int) shamem_read(crazy_register); int desired_crazy_value = preview_crazy == 1 ? 0x10 : preview_crazy == 2 ? 0xA00001 : 1; if (preview_crazy || crazy_dirty) { if (current_crazy_value != desired_crazy_value) { EngDrvOutLV(crazy_register, desired_crazy_value); crazy_dirty = preview_crazy; } } #endif #ifdef FEATURE_DIGIC_FOCUS_PEAKING int filter_register = 0xC0F14140; /* EnableFilter */ static int filter_dirty = 0; int current_filter_value = (int) shamem_read(filter_register); int desired_filter_value = gui_menu_shown() && !menu_active_but_hidden() ? 0 : preview_peaking == 1 || (preview_peaking > 1 && preview_peaking_force_normal_image) ? 0x4d4 : preview_peaking == 2 || preview_peaking == 3 ? 0x4c0 : preview_peaking; if (preview_peaking || filter_dirty) { if (current_filter_value != desired_filter_value) { EngDrvOutLV(filter_register, desired_filter_value); filter_dirty = preview_peaking; } } #endif } #ifdef FEATURE_LV_BRIGHTNESS_CONTRAST static void preview_show_contrast_curve() { int brightness_contrast_register = 0xC0F141B8; int value = (int) shamem_read(brightness_contrast_register); int contrast = value & 0xFF; int brightness = (int8_t)(value >> 16); int x0 = 360-128; int y0 = 300-128; bmp_draw_rect(COLOR_BLACK, x0, y0, 255, 255 * 2/3); bmp_draw_rect(COLOR_WHITE, x0-1, y0-1, 257, 255 * 2/3 + 2); for (int x = 0; x < 256; x++) { int y = COERCE(brightness + x * contrast / 0x80, 0, 255); y = (256 - y) * 2/3; bmp_putpixel(x0 + x, y0 + y - 2, COLOR_WHITE); bmp_putpixel(x0 + x, y0 + y - 1, COLOR_RED); bmp_putpixel(x0 + x, y0 + y , COLOR_RED); bmp_putpixel(x0 + x, y0 + y + 1, COLOR_RED); bmp_putpixel(x0 + x, y0 + y + 2, COLOR_WHITE); } } #endif #ifdef FEATURE_LV_SATURATION static MENU_UPDATE_FUNC(preview_saturation_display) { extern int focus_peaking_grayscale; if (focus_peaking_grayscale && is_focus_peaking_enabled()) MENU_SET_WARNING(MENU_WARN_NOT_WORKING, "Focus peaking with grayscale preview is enabled."); if (PREVIEW_SATURATION_BOOST_WB) MENU_SET_ICON(MNI_AUTO, 0); } #endif #ifdef FEATURE_LV_BRIGHTNESS_CONTRAST static MENU_UPDATE_FUNC(preview_contrast_display) { if (preview_contrast == 3) MENU_SET_VALUE( preview_brightness == 0 ? "Auto (normal)" : preview_brightness == 1 ? "Auto (low)" : preview_brightness == 2 ? "Auto (very low)" : "err" ); if (EXT_MONITOR_CONNECTED) MENU_SET_WARNING(MENU_WARN_NOT_WORKING, "Does not work on external monitors."); if (PREVIEW_CONTRAST_AUTO) MENU_SET_ICON(MNI_AUTO, 0); if (menu_active_but_hidden()) preview_show_contrast_curve(); } static MENU_UPDATE_FUNC(preview_brightness_display) { if (EXT_MONITOR_CONNECTED) MENU_SET_WARNING(MENU_WARN_NOT_WORKING, "Does not work on external monitors."); if (menu_active_but_hidden()) preview_show_contrast_curve(); } #endif #ifdef FEATURE_ARROW_SHORTCUTS static void adjust_saturation_level(int delta) { preview_saturation = COERCE((int)preview_saturation + delta, -2, 2); NotifyBox(2000, "LCD Saturation : %s", preview_saturation == -2 ? "0 (Grayscale)" : preview_saturation == -1 ? "Low" : preview_saturation == 0 ? "Normal" : preview_saturation == 1 ? "High" : "Very high" ); } static void brightness_saturation_reset() { preview_saturation = 0; set_backlight_level(5); set_display_gain_equiv(0); NotifyBox(2000, "LCD Saturation: Normal\n" "LCD Backlight : 5 \n" "Display Gain : 0 EV "); } #endif #ifdef FEATURE_COLOR_SCHEME void alter_bitmap_palette_entry(int color, int base_color, int luma_scale_factor, int chroma_scale_factor) { #ifndef CONFIG_VXWORKS int orig_palette_entry = LCD_Palette[3*base_color + 2]; int8_t opacity = (orig_palette_entry >> 24) & 0xFF; uint8_t orig_y = (orig_palette_entry >> 16) & 0xFF; int8_t orig_u = (orig_palette_entry >> 8) & 0xFF; int8_t orig_v = (orig_palette_entry >> 0) & 0xFF; int y = COERCE((int)orig_y * luma_scale_factor / 256, 0, 255); int u = COERCE((int)orig_u * chroma_scale_factor / 256, -127, 127); int v = COERCE((int)orig_v * chroma_scale_factor / 256, -127, 127); int new_palette_entry = ((opacity & 0xFF) << 24) | ((y & 0xFF) << 16) | ((u & 0xFF) << 8) | ((v & 0xFF)); if (!DISPLAY_IS_ON) return; EngDrvOut(LCD_Palette[3*color], new_palette_entry); EngDrvOut(LCD_Palette[3*color+0x300], new_palette_entry); #endif } static void alter_bitmap_palette(int dim_factor, int grayscale, int u_shift, int v_shift) { #ifndef CONFIG_VXWORKS if (!bmp_is_on()) return; // 255 is reserved for ClearScreen, don't alter it for (int i = 0; i < 255; i++) { if (i==0 || i==3 || i==0x14) continue; // don't alter transparent entries int orig_palette_entry = LCD_Palette[3*i + 2]; //~ bmp_printf(FONT_LARGE,0,0,"%x ", orig_palette_entry); //~ msleep(300); //~ continue; int8_t opacity = (orig_palette_entry >> 24) & 0xFF; uint8_t orig_y = (orig_palette_entry >> 16) & 0xFF; int8_t orig_u = (orig_palette_entry >> 8) & 0xFF; int8_t orig_v = (orig_palette_entry >> 0) & 0xFF; int y = orig_y / dim_factor; int u = grayscale ? 0 : COERCE((int)orig_u / dim_factor + u_shift * y / 256, -128, 127); int v = grayscale ? 0 : COERCE((int)orig_v / dim_factor + v_shift * y / 256, -128, 127); int new_palette_entry = ((opacity & 0xFF) << 24) | ((y & 0xFF) << 16) | ((u & 0xFF) << 8) | ((v & 0xFF)); if (!DISPLAY_IS_ON) return; EngDrvOut(LCD_Palette[3*i], new_palette_entry); EngDrvOut(LCD_Palette[3*i+0x300], new_palette_entry); } #endif } static void grayscale_menus_step() { /* #ifndef CONFIG_VXWORKS static int warning_color_dirty = 0; if (gui_menu_shown()) { // make the warning text blinking, so beginners will notice it... int t = *(uint32_t*)0xC0242014; alter_bitmap_palette_entry(MENU_WARNING_COLOR, COLOR_RED, 512 - ABS((t >> 11) - 256), ABS((t >> 11) - 256)); warning_color_dirty = 1; } else if (warning_color_dirty) { alter_bitmap_palette_entry(MENU_WARNING_COLOR, MENU_WARNING_COLOR, 256, 256); warning_color_dirty = 0; } #endif */ // problem: grayscale registers are not overwritten by Canon when palette is changed // so we don't know when to refresh it // => need to use pure guesswork static int prev_sig = 0; static int prev_b = 0; // optimization: try to only update palette after a display mode change // but this is not 100% reliable => update at least once every second int guimode = CURRENT_GUI_MODE; int d = DISPLAY_IS_ON; int b = bmp_color_scheme; int sig = (int)get_current_dialog_handler() + d + guimode + b*31415 + get_seconds_clock(); int transition = (sig != prev_sig); if (ml_shutdown_requested) return; if (!DISPLAY_IS_ON) return; if (!transition) return; prev_sig = sig; #ifdef CONFIG_5D3 if (get_yuv422_vram()->vram == 0 && !lv) { /* 5D3-123 quirk: YUV422 RAM is not initialized until going to LiveView or Playback mode * (and even there, you need a valid image first) * Workaround: if YUV422 was not yet initialized by Canon, remove the transparency from color 0 (make it black). * * Any other cameras requiring this? Probably not, since the quirk is likely related to the dual monitor support. * * Note: alter_bitmap_palette will not affect color 0, so it will not break this workaround (yet). */ alter_bitmap_palette_entry(0, COLOR_BLACK, 256, 256); } #endif if (bmp_color_scheme || prev_b) { //~ info_led_on(); for (int i = 0; i < 3; i++) { if (DISPLAY_IS_ON) { if (bmp_color_scheme == 0) alter_bitmap_palette(1,0,0,0); else if (bmp_color_scheme == 1) alter_bitmap_palette(3,0,0,0); else if (bmp_color_scheme == 2) alter_bitmap_palette(1,1,0,0); else if (bmp_color_scheme == 3) alter_bitmap_palette(3,1,0,0); else if (bmp_color_scheme == 4) alter_bitmap_palette(5,0,-170/2,500/2); // strong shift towards red else if (bmp_color_scheme == 5) alter_bitmap_palette(3,0,-170/2,-500/2); // strong shift toward green (pink 5,0,170/2,500/2) } msleep(50); } //~ info_led_off(); } prev_b = b; } #endif #ifdef FEATURE_IMAGE_POSITION static void lcd_adjust_position_step() { if (ml_shutdown_requested) return; if (!DISPLAY_IS_ON) return; static int factory_position = -1; static int check_position = -1; int position_register = 0xC0F14164; int current_position = (int) shamem_read(position_register); if (factory_position == -1) check_position = factory_position = current_position; int desired_position = factory_position - lcd_adjust_position * 16; if (current_position != desired_position) { if (current_position == check_position) { EngDrvOut(position_register, desired_position); check_position = desired_position; } else check_position = factory_position = current_position; // Canon code changed it? } } #endif #ifdef FEATURE_DISPLAY_SHAKE CONFIG_INT("display.shake", display_shake, 0); // called from LV state object, once per frame // this updates only odd frames; combined with the triple buffering scheme, // this will exaggerate motion and camera shake void display_shake_step() { if (!display_shake) return; if (!lv) return; if (!DISPLAY_IS_ON) return; if (hdmi_code >= 5) return; static int k; k++; if (k%2) return; if ((MEM(REG_EDMAC_WRITE_LV_ADDR) & 0xFFFF) != (YUV422_LV_BUFFER_1 & 0xFFFF)) return; MEM(REG_EDMAC_WRITE_LV_ADDR) += (vram_lv.pitch * vram_lv.height); } #endif CONFIG_INT("defish.preview", defish_preview, 0); #define defish_projection (defish_preview==1 ? 0 : 1) //~ static CONFIG_INT("defish.projection", defish_projection, 0); //~ static CONFIG_INT("defish.hd", DEFISH_HD, 1); #define DEFISH_HD 1 #ifndef FEATURE_DEFISHING_PREVIEW #define defish_preview 0 #endif static CONFIG_INT("anamorphic.preview", anamorphic_preview, 0); //~ CONFIG_INT("anamorphic.ratio.idx", anamorphic_ratio_idx, 0); #define anamorphic_ratio_idx (anamorphic_preview-1) #ifndef FEATURE_ANAMORPHIC_PREVIEW #define anamorphic_preview 0 #endif #ifdef FEATURE_ANAMORPHIC_PREVIEW static int anamorphic_ratio_num[10] = {5, 4, 7, 3, 5, 9, 2}; static int anamorphic_ratio_den[10] = {4, 3, 5, 2, 3, 5, 1}; static MENU_UPDATE_FUNC(anamorphic_preview_display) { /* if (anamorphic_preview) { int num = anamorphic_ratio_num[anamorphic_ratio_idx]; int den = anamorphic_ratio_den[anamorphic_ratio_idx]; MENU_SET_VALUE( "%d:%d", num, den ); } */ if (defish_preview) MENU_SET_WARNING(MENU_WARN_NOT_WORKING, "Too much for this lil' cam... both defishing and anamorphic"); } // for focus peaking (exception, since it doesn't operate on squeezed LV buffer, but on unsqeezed HD one // so... we'll try to squeeze the bitmap coords for output static int16_t anamorphic_bmp_y_lut[480]; int FAST anamorphic_squeeze_bmp_y(int y) { if (likely(!anamorphic_preview)) return y; if (unlikely(!lv)) return y; if (unlikely(hdmi_code >= 5)) return y; if (unlikely(y < 0 || y >= 480)) return y; static int prev_idx = -1; if (unlikely(prev_idx != (int)anamorphic_ratio_idx)) // update the LUT { int num = anamorphic_ratio_num[anamorphic_ratio_idx]; int den = anamorphic_ratio_den[anamorphic_ratio_idx]; int yc = os.y0 + os.y_ex / 2; for (int y = 0; y < 480; y++) anamorphic_bmp_y_lut[y] = (y - yc) * den/num + yc; prev_idx = anamorphic_ratio_idx; } return anamorphic_bmp_y_lut[y]; } static void yuvcpy_dark(uint32_t* dst, uint32_t* src, size_t n, int parity) { if (parity) { for(size_t i = 0; i < n/4; i++) *dst++ = ((*src++) & 0xFE000000) >> 1; } else { for(size_t i = 0; i < n/4; i++) *dst++ = ((*src++) & 0x0000FE00) >> 1; } } static void FAST anamorphic_squeeze() { if (!anamorphic_preview) return; if (!get_global_draw()) return; if (!lv) return; if (hdmi_code >= 5) return; int num = anamorphic_ratio_num[anamorphic_ratio_idx]; int den = anamorphic_ratio_den[anamorphic_ratio_idx]; uint32_t* src_buf; uint32_t* dst_buf; display_filter_get_buffers(&src_buf, &dst_buf); if (!src_buf || !dst_buf) return; src_buf = CACHEABLE(src_buf); dst_buf = CACHEABLE(dst_buf); int mv = is_movie_mode(); int ym = os.y0 + os.y_ex/2; for (int y = os.y0; y < os.y_ex; y++) { int ya = (y-ym) * num/den + ym; if (ya > os.y0 && ya < os.y_max) { if (!mv || (ya > os.y0 + os.off_169 && ya < os.y_max - os.off_169)) #ifdef CONFIG_DMA_MEMCPY dma_memcpy(&dst_buf[LV(0,y)/4], &src_buf[LV(0,ya)/4], 720*2); #else memcpy(&dst_buf[LV(0,y)/4], &src_buf[LV(0,ya)/4], 720*2); #endif else yuvcpy_dark(&dst_buf[LV(0,y)/4], &src_buf[LV(0,ya)/4], 720*2, y%2); } else memset(&dst_buf[LV(0,y)/4], 0, 720*2); } } #endif #ifdef FEATURE_DEFISHING_PREVIEW /*static MENU_UPDATE_FUNC(defish_preview_display) { if (defish_preview) MENU_SET_VALUE( defish_projection ? "Panini" : "Rectilinear" ); }*/ //~ CONFIG_STR("defish.lut", defish_lut_file, "ML/SETTINGS/recti.lut"); #if defined(CONFIG_FULLFRAME) #define defish_lut_file_rectilin "ML/DATA/ff8r.lut" #define defish_lut_file_panini "ML/DATA/ff8p.lut" #else #define defish_lut_file_rectilin "ML/DATA/apsc8r.lut" #define defish_lut_file_panini "ML/DATA/apsc8p.lut" #endif static uint16_t* defish_lut_load() { char* defish_lut_file = defish_projection ? defish_lut_file_panini : defish_lut_file_rectilin; int size = 0; uint16_t* defish_lut = (uint16_t*) read_entire_file(defish_lut_file, &size); return defish_lut; } static void FAST defish_draw_lv_color_loop(uint64_t* src_buf, uint64_t* dst_buf, int* ind) { src_buf = CACHEABLE((intptr_t)src_buf & ~7); dst_buf = CACHEABLE((intptr_t)dst_buf & ~7); for (int i = 720 * (os.y0/4); i < 720 * (os.y_max/4); i++) dst_buf[i] = src_buf[ind[i]]; } int* defish_ind; static MENU_SELECT_FUNC(defish_toggle) { menu_numeric_toggle(priv, delta, 0, 2); if (!defish_preview && defish_ind) /* no longer needed */ { BMP_LOCK( /* make sure we don't free it while it's being used */ if (defish_ind) { free(defish_ind); defish_ind = 0; } ) } } static void defish_draw_lv_color() { if (!get_global_draw()) return; if (!lv) return; uint32_t* src_buf; uint32_t* dst_buf; display_filter_get_buffers(&src_buf, &dst_buf); if (!src_buf || !dst_buf) return; if (DEFISH_HD) src_buf = (void*)(get_yuv422_hd_vram()->vram); // small speedup (26fps with cacheable vs 20 without) src_buf = CACHEABLE(src_buf); dst_buf = CACHEABLE(dst_buf); static int defish_just_allocated = 0; if (!defish_ind) { defish_ind = malloc(720*120*4); defish_just_allocated = 1; } if (!defish_ind) return; static int prev_sig = 0; int sig = defish_projection + vram_lv.width + vram_hd.width + DEFISH_HD*314; if (sig != prev_sig || defish_just_allocated) { prev_sig = sig; defish_just_allocated = 0; bzero32(defish_ind, 720*120*4); uint16_t * defish_lut = defish_lut_load(); if (!defish_lut) return; info_led_on(); for (int y = BM2LV_Y(os.y0); y < BM2LV_Y(os.y0 + os.y_ex/2); y++) { for (int x = BM2LV_X(os.x0); x < BM2LV_X(os.x0 + os.x_ex/2); x += 2) { // i,j are normalized values: [0,0 ... 720x480) int j = LV2N_X(x); int i = LV2N_Y(y); static int off_i[] = {0, 0,479,479}; static int off_j[] = {0,719, 0,719}; int id, jd; if (DEFISH_HD) { id = (int)defish_lut[(i * 360 + j) * 2 + 1] / 16; jd = (int)defish_lut[(i * 360 + j) * 2] * 361 / 256 / 16; } else { id = (int)defish_lut[(i * 360 + j) * 2 + 1] / 256; jd = (int)defish_lut[(i * 360 + j) * 2] * 361 / 256 / 256; } int k; for (k = 0; k < 4; k++) { int Y = (off_i[k] ? N2LV_Y(off_i[k]) - y + BM2LV_Y(os.y0) - 1 : y); int X = (off_j[k] ? N2LV_X(off_j[k]) - x + BM2LV_X(os.x0) : x); int is = COERCE(LV(X,Y)/8, 0, 720*120-1); int ids; if (DEFISH_HD) { int Id = (off_i[k] ? off_i[k]*16 - id : id); int Jd = (off_j[k] ? off_j[k]*16 - jd : jd); ids = Nh2HD(Jd,Id)/8; } else { int Id = (off_i[k] ? off_i[k] - id : id); int Jd = (off_j[k] ? off_j[k] - jd : jd); ids = N2LV(Jd,Id)/8; } defish_ind[is] = ids; } } } info_led_off(); fio_free(defish_lut); } defish_draw_lv_color_loop((uint64_t*)src_buf, (uint64_t*)dst_buf, defish_ind); } void defish_draw_play() { struct vram_info * vram = get_yuv422_vram(); uint32_t * lvram = (uint32_t *)vram->vram; uint32_t * aux_buf = (void*)YUV422_HD_BUFFER_2; if (!lvram) return; uint8_t * const bvram = bmp_vram(); if (!bvram) return; int w = vram->width; int h = vram->height; int buf_size = w * h * 2; if (!PLAY_OR_QR_MODE || !DISPLAY_IS_ON) return; uint16_t * defish_lut = defish_lut_load(); if (!defish_lut) return; memcpy(aux_buf, lvram, buf_size); for (int y = BM2LV_Y(os.y0); y < BM2LV_Y(os.y0 + os.y_ex/2); y++) { for (int x = BM2LV_X(os.x0); x < BM2LV_X(os.x0 + os.x_ex/2); x++) { // i,j are normalized values: [0,0 ... 720x480) int j = LV2N_X(x); int i = LV2N_Y(y); static int off_i[] = {0, 0,479,479}; static int off_j[] = {0,719, 0,719}; //~ int id = defish_lut[(i * 360 + j) * 2 + 1]; //~ int jd = defish_lut[(i * 360 + j) * 2] * 360 / 255; int id = (int)defish_lut[(i * 360 + j) * 2 + 1] / 256; int jd = (int)defish_lut[(i * 360 + j) * 2] * 361 / 256 / 256; //~ bmp_printf(FONT_MED, 100, 100, "%d,%d => %x,%x ", i,j,id,jd); //~ msleep(200); int k; for (k = 0; k < 4; k++) { int Y = (off_i[k] ? N2LV_Y(off_i[k]) - y + BM2LV_Y(os.y0) - 1 : y); int X = (off_j[k] ? N2LV_X(off_j[k]) - x + BM2LV_X(os.x0) : x); int Id = (off_i[k] ? off_i[k] - id : id); int Jd = (off_j[k] ? off_j[k] - jd : jd); //~ lvram[LV(X,Y)/4] = aux_buf[N2LV(Jd,Id)/4]; // Rather than copying an entire uyvy pair, copy only one pixel (and overwrite luma for both pixels in the bin) // => slightly better image quality // Actually, IQ is far lower than what Nona does with proper interpolation // but this is enough for preview purposes //~ uint32_t new_color = get_yuv_pixel_averaged(aux_buf, Id, Jd); int pixoff_src = N2LV(Jd,Id) / 2; uint32_t new_color = yuv422_get_pixel(aux_buf, pixoff_src); int pixoff_dst = LV(X,Y) / 2; uint32_t* dst = &lvram[pixoff_dst / 2]; uint32_t mask = (pixoff_dst % 2 ? 0xffFF00FF : 0x00FFffFF); *(dst) = (new_color & mask) | (*(dst) & ~mask); } } if (!PLAY_OR_QR_MODE || !DISPLAY_IS_ON) break; if ((void*)get_yuv422_vram()->vram != (void*)lvram) break; // user moved to a new image? } free(defish_lut); } #endif #ifdef CONFIG_DISPLAY_FILTERS #ifdef CONFIG_CAN_REDIRECT_DISPLAY_BUFFER_EASILY static void* display_filter_buffer_unaligned = 0; static void* display_filter_buffer = 0; static void* last_canon_buffer = 0; #endif static int display_filter_valid_image = 0; void display_filter_get_buffers(uint32_t** src_buf, uint32_t** dst_buf) { //~ struct vram_info * vram = get_yuv422_vram(); //~ int buf_size = 720*480*2; //~ void* src = (void*)vram->vram; //~ void* dst = src_buf + buf_size; #if defined(CONFIG_CAN_REDIRECT_DISPLAY_BUFFER_EASILY) // the EDMAC buffer is currently updating; use the previous one, which is complete static void* prev = 0; static void* buff = 0; void* current = (void*)shamem_read(REG_EDMAC_WRITE_LV_ADDR); // EDMAC may not point exactly to the LV buffer (e.g. it may skip the 16:9 bars or whatever) // so we'll try to choose some buffer that's close enough to the EDMAC address int c = (int) current; int b1 = (int)CACHEABLE(YUV422_LV_BUFFER_1); int b2 = (int)CACHEABLE(YUV422_LV_BUFFER_2); int b3 = (int)CACHEABLE(YUV422_LV_BUFFER_3); #ifdef YUV422_LV_BUFFER_4 int b4 = (int)CACHEABLE(YUV422_LV_BUFFER_4); #endif if (ABS(c - b1) < 200000) current = (void*)b1; else if (ABS(c - b2) < 200000) current = (void*)b2; else if (ABS(c - b3) < 200000) current = (void*)b3; #ifdef YUV422_LV_BUFFER_4 else if (ABS(c - b4) < 200000) current = (void*)b4; #endif if (current != prev) buff = prev; prev = current; *src_buf = buff; *dst_buf = CACHEABLE(display_filter_buffer); #else // just use some reasonable defaults that won't crash the camera *src_buf = CACHEABLE(YUV422_LV_BUFFER_1); *dst_buf = CACHEABLE(YUV422_LV_BUFFER_2); #endif } // type 1 filters: compute histogram on filtered image // type 2 filters: compute histogram on original image int display_filter_enabled() { #ifndef CONFIG_CAN_REDIRECT_DISPLAY_BUFFER return 0; #endif if (EXT_MONITOR_CONNECTED) return 0; // non-scalable code if (!lv) return 0; int mdf = 0; #ifdef CONFIG_MODULES mdf = module_display_filter_enabled(); #endif int fp = focus_peaking_as_display_filter(); if (!(defish_preview || anamorphic_preview || fp || mdf)) return 0; if (!zebra_should_run()) return 0; if (should_draw_zoom_overlay()) return 0; // not enough CPU power to run MZ and filters at the same time return fp ? 2 : 1; } #if defined(CONFIG_5D2) || defined(CONFIG_50D) || defined(CONFIG_7D) static int display_broken = 0; int display_broken_for_mz() { return display_broken; } #endif int display_filter_lv_vsync(int old_state, int x, int input, int z, int t) { #if defined(CONFIG_5D2) int sync = (MEM(x+0xe0) == YUV422_LV_BUFFER_1); int hacked = ( MEM(0x44fc+0xBC) == MEM(0x44fc+0xc4) && MEM(0x44fc+0xc4) == MEM(x+0xe0)); display_broken = hacked; if (!display_filter_valid_image) return CBR_RET_CONTINUE; if (!display_filter_enabled()) { display_filter_valid_image = 0; return CBR_RET_CONTINUE; } if (display_filter_enabled()) { if (sync || hacked) { MEM(0x44fc+0xBC) = 0; YUV422_LV_BUFFER_DISPLAY_ADDR = YUV422_LV_BUFFER_2; // update buffer 1, display buffer 2 extern void EnableImagePhysicalScreenParameter(); EnableImagePhysicalScreenParameter(); } } #elif defined(CONFIG_50D) //455C - Debug Flag //445C + A4 - Current LV or 0 //455C + AC - Current Lv or 0 //x + C8 = LV buffer.. print x, look around int sync = (MEM(x+0xc8) == YUV422_LV_BUFFER_1); int hacked = ( MEM(0x455c+0xA4) == MEM(0x455c+0xAC) && MEM(0x455c+0xAC) == MEM(x+0xc8)); display_broken = hacked; if (!display_filter_valid_image) return CBR_RET_CONTINUE; if (!display_filter_enabled()) { display_filter_valid_image = 0; return CBR_RET_CONTINUE; } if (display_filter_enabled()) { if (sync || hacked) { MEM(0x455c+0xA4) = 0; YUV422_LV_BUFFER_DISPLAY_ADDR = YUV422_LV_BUFFER_2; // update buffer 1, display buffer 2 extern void EnableImagePhysicalScreenParameter(); EnableImagePhysicalScreenParameter(); } } #elif defined(CONFIG_7D) //4430 - Debug Flag //445C + E8 - Current LV or 0 //455C + F0 - Current Lv or 0 //x + F4 = LV buffer.. print x, look around int sync = (MEM(x+0xF4) == YUV422_LV_BUFFER_1); int hacked = ( MEM(0x4430+0xE8) == MEM(0x4430+0xF0) && MEM(0x4430+0xF0) == MEM(x+0xF4)); display_broken = hacked; if (!display_filter_valid_image) return CBR_RET_CONTINUE; if (!display_filter_enabled()) { display_filter_valid_image = 0; return CBR_RET_CONTINUE; } if (display_filter_enabled()) { if (sync || hacked) { MEM(0x4430+0xE8) = 0; YUV422_LV_BUFFER_DISPLAY_ADDR = YUV422_LV_BUFFER_2; // update buffer 1, display buffer 2 extern void EnableImagePhysicalScreenParameter(); EnableImagePhysicalScreenParameter(); } } #elif defined(CONFIG_CAN_REDIRECT_DISPLAY_BUFFER_EASILY) // all new cameras should work with this method if (!display_filter_buffer) return CBR_RET_CONTINUE; if (!display_filter_valid_image) return CBR_RET_CONTINUE; if (!display_filter_enabled()) { display_filter_valid_image = 0; return CBR_RET_CONTINUE; } /* save the old buffer (to restore it when turning off display filters) */ void* current_buffer = (void*) YUV422_LV_BUFFER_DISPLAY_ADDR; if (current_buffer != display_filter_buffer) last_canon_buffer = current_buffer; /* switch the displayed buffer to our filtered image */ YUV422_LV_BUFFER_DISPLAY_ADDR = (uint32_t) display_filter_buffer; #endif return CBR_RET_STOP; } void display_filter_step(int k) { if (!display_filter_enabled()) { #ifdef CONFIG_CAN_REDIRECT_DISPLAY_BUFFER_EASILY /* for new cameras: if there are no more display filters active, free the output buffer */ if (display_filter_buffer) { if (YUV422_LV_BUFFER_DISPLAY_ADDR == (uint32_t) display_filter_buffer) { YUV422_LV_BUFFER_DISPLAY_ADDR = (uint32_t) last_canon_buffer; } free(display_filter_buffer_unaligned); display_filter_buffer = 0; } #endif return; } #ifdef CONFIG_CAN_REDIRECT_DISPLAY_BUFFER_EASILY if (!display_filter_buffer) { /* for new cameras: when you enable a display filter, allocate the output buffer */ /* some routines (e.g. defishing) use 64-bit operations, so allocate a bit more and align the buffer */ display_filter_buffer_unaligned = malloc(720*480*2 + 32); display_filter_buffer = ALIGN64SUP(display_filter_buffer_unaligned); } #endif msleep(20); //~ if (!HALFSHUTTER_PRESSED) return; #ifdef CONFIG_MODULES if (module_display_filter_update()) { } else #endif #ifdef FEATURE_DEFISHING_PREVIEW if (defish_preview) { if (k % 2 == 0) BMP_LOCK( if (lv) defish_draw_lv_color(); ) } else #endif #ifdef FEATURE_ANAMORPHIC_PREVIEW if (anamorphic_preview) { if (k % 1 == 0) BMP_LOCK( if (lv) anamorphic_squeeze(); ) } else #endif #ifdef FEATURE_FOCUS_PEAK_DISP_FILTER if (focus_peaking_as_display_filter()) { if (k % 1 == 0) BMP_LOCK( if (lv) peak_disp_filter(); ) } else #endif { } display_filter_valid_image = 1; } #endif #ifdef CONFIG_KILL_FLICKER CONFIG_INT("kill.canon.gui", kill_canon_gui_mode, 1); #endif extern int clearscreen; //~ extern int clearscreen_mode; extern int screen_layout_menu_index; extern MENU_UPDATE_FUNC(screen_layout_update); extern void screen_layout_toggle(void* priv, int delta); extern int hdmi_force_vga; extern MENU_UPDATE_FUNC(hdmi_force_display); extern MENU_UPDATE_FUNC(display_gain_print); extern int display_gain_menu_index; static struct menu_entry display_menus[] = { #ifdef FEATURE_DIGIC_FOCUS_PEAKING { .name = "LV DIGIC peaking", .priv = &preview_peaking, .min = 0, #ifdef FEATURE_LV_SATURATION .max = 3, /* to get raw values, set .max = 0x1000, .unit = UNIT_HEX and comment out .choices */ .edit_mode = EM_MANY_VALUES_LV, #else .max = 1, /* the other options require saturation controls available */ #endif .choices = (const char *[]) {"OFF", "Slightly sharper", "Edge image", "Edge + chroma"}, .help = "Focus peaking via DIGIC. No CPU usage!", .depends_on = DEP_LIVEVIEW, }, #endif #ifdef FEATURE_LV_BRIGHTNESS_CONTRAST { .name = "LV brightness", .priv = &preview_brightness, .max = 2, .help = "For LiveView preview only. Does not affect recording.", .update = preview_brightness_display, .edit_mode = EM_MANY_VALUES_LV, .choices = (const char *[]) {"Normal", "High", "Very high"}, .depends_on = DEP_LIVEVIEW, .icon_type = IT_PERCENT_OFF, }, { .name = "LV contrast", .priv = &preview_contrast, .min = -3, .max = 3, .update = preview_contrast_display, .help = "For LiveView preview only. Does not affect recording.", .edit_mode = EM_MANY_VALUES_LV, .choices = (const char *[]) {"Zero", "Very low", "Low", "Normal", "High", "Very high", "Auto"}, .depends_on = DEP_LIVEVIEW, .icon_type = IT_PERCENT_OFF, }, #endif #ifdef FEATURE_LV_SATURATION { .name = "LV saturation", .priv = &preview_saturation, .min = -2, .max = 3, .update = preview_saturation_display, .help = "For LiveView preview only. Does not affect recording.", .help2 = " \n" " \n" " \n" " \n" " \n" "Boost on WB: increase saturation when you are adjusting WB.", .edit_mode = EM_MANY_VALUES_LV, .choices = (const char *[]) {"Grayscale", "Low", "Normal", "High", "Very high", "Boost on WB adjust"}, .depends_on = DEP_LIVEVIEW, .icon_type = IT_PERCENT_OFF, /* .submenu_width = 650, .children = (struct menu_entry[]) { { .name = "Boost when adjusting WB", .priv = &preview_saturation_boost_wb, .max = 1, .help = "Increase LiveView saturation when adjusting white balance.", }, MENU_EOL }*/ }, #endif #ifdef FEATURE_LV_DISPLAY_GAIN { .name = "LV display gain", .priv = &display_gain_menu_index, .update = display_gain_print, .select = display_gain_toggle, .max = 6, .choices = CHOICES("OFF", "1 EV", "2 EV", "3 EV", "4 EV", "5 EV", "6 EV"), .icon_type = IT_PERCENT_OFF, .help = "Makes LiveView usable in complete darkness (photo mode).", .help2 = "Tip: if it gets really dark, also enable FPS override.", .edit_mode = EM_MANY_VALUES_LV, .depends_on = DEP_LIVEVIEW | DEP_PHOTO_MODE, }, #endif #ifdef FEATURE_CLEAR_OVERLAYS { .name = "Clear overlays", .priv = &clearscreen, .max = 4, .choices = (const char *[]) {"OFF", "HalfShutter", "WhenIdle", "Always", "Recording"}, .icon_type = IT_DICE_OFF, .help = "Clear bitmap overlays from LiveView display.", .depends_on = DEP_LIVEVIEW, /* .children = (struct menu_entry[]) { { .name = "Mode", .priv = &clearscreen_mode, .max = 3, .help = "Clear screen when you hold shutter halfway or when idle.", }, MENU_EOL }, */ }, #endif #ifdef FEATURE_DEFISHING_PREVIEW #ifndef CONFIG_DISPLAY_FILTERS #error This requires CONFIG_DISPLAY_FILTERS. #endif { .name = "Defishing", .priv = &defish_preview, .select = defish_toggle, //~ .update = defish_preview_display, .max = 2, .depends_on = DEP_GLOBAL_DRAW, .choices = (const char *[]) {"OFF", "Rectilinear", "Panini"}, .help = "Preview straightened images from fisheye lenses. LV+PLAY.", /* .children = (struct menu_entry[]) { { .name = "Projection", .priv = &defish_projection, .max = 1, .choices = (const char *[]) {"Rectilinear", "Panini"}, .icon_type = IT_DICE, .help = "Projection used for defishing (Rectilinear or Panini).", }, MENU_EOL } */ }, #endif #ifdef FEATURE_ANAMORPHIC_PREVIEW #ifndef CONFIG_DISPLAY_FILTERS #error This requires CONFIG_DISPLAY_FILTERS. #endif { .name = "Anamorphic", .priv = &anamorphic_preview, .update = anamorphic_preview_display, .max = 7, .choices = (const char *[]) {"OFF", "5:4 (1.25)", "4:3 (1.33)", "7:5 (1.4)", "3:2 (1.5)", "5:3 (1.66)", "9:5 (1.8)", "2:1"}, .help = "Stretches LiveView image vertically, for anamorphic lenses.", .depends_on = DEP_LIVEVIEW | DEP_GLOBAL_DRAW, /* .children = (struct menu_entry[]) { { .name = "Stretch Ratio", .priv = &anamorphic_ratio_idx, .max = 6, .choices = (const char *[]) {"5:4 (1.25)", "4:3 (1.33)", "7:5 (1.4)", "3:2 (1.5)", "5:3 (1.66)", "9:5 (1.8)", "2:1"}, .help = "Aspect ratio used for anamorphic preview correction.", }, MENU_EOL },*/ }, #endif #if defined(CONFIG_KILL_FLICKER) || defined(FEATURE_SCREEN_LAYOUT) || defined(FEATURE_IMAGE_POSITION) || defined(FEATURE_UPSIDE_DOWN) || defined(FEATURE_IMAGE_ORIENTATION) || defined(FEATURE_AUTO_MIRRORING_HACK) || defined(FEATURE_FORCE_HDMI_VGA) { .name = "Advanced settings", .select = menu_open_submenu, .submenu_width = 710, .help = "Screen orientation, position fine-tuning...", .children = (struct menu_entry[]) { #ifdef CONFIG_KILL_FLICKER { .name = "Kill Canon GUI", .priv = &kill_canon_gui_mode, .max = 2, .choices = CHOICES("OFF", "Idle/Menus", "Idle/Menus+Keys"), .depends_on = DEP_GLOBAL_DRAW, .help = "Workarounds for disabling Canon graphics elements." }, #endif #ifdef FEATURE_SCREEN_LAYOUT { .name = "Screen Layout", .priv = &screen_layout_menu_index, .max = 4, .update = screen_layout_update, .select = screen_layout_toggle, .choices = CHOICES( #ifdef CONFIG_4_3_SCREEN "4:3 display,auto", #else "3:2 display,t/b", #endif "16:10 HDMI,t/b", "16:9 HDMI,t/b", "Bottom,under 3:2", "Bottom,under16:9" ), .help = "Position of top/bottom bars, useful for external displays.", .depends_on = DEP_LIVEVIEW, }, #endif #ifdef FEATURE_COLOR_SCHEME { .name = "Color scheme", .priv = &bmp_color_scheme, .max = 5, .choices = (const char *[]) {"Default", "Dark", "Bright Gray", "Dark Gray", "Dark Red", "Dark Green"}, .help = "Color scheme for bitmap overlays (ML menus, Canon menus...)", .icon_type = IT_DICE_OFF, }, #endif #ifdef FEATURE_IMAGE_POSITION { .name = "Image position", .priv = &lcd_adjust_position, .min = -2, .max = 2, .choices = (const char *[]) {"-16px", "-8px", "Normal", "+8px", "+16px"}, .icon_type = IT_PERCENT_OFF, .help = "May make the image easier to see from difficult angles.", }, #endif #ifdef FEATURE_UPSIDE_DOWN { .name = "UpsideDown mode", .priv = &menu_upside_down, .max = 1, .help = "Displays overlay graphics upside-down and flips arrow keys.", }, #endif #ifdef FEATURE_IMAGE_ORIENTATION { .name = "Orientation", .priv = (int*)&DISPLAY_ORIENTATION, .select = display_orientation_toggle, .max = 2, .choices = (const char *[]) {"Normal", "Reverse", "Mirror"}, .help = "Display + LiveView orientation: Normal / Reverse / Mirror." }, #endif #ifdef FEATURE_AUTO_MIRRORING_HACK { .name = "Auto Mirroring", .priv = &display_dont_mirror, .max = 1, .choices = (const char *[]) {"Allow", "Don't allow"}, .help = "Prevents display mirroring, which may reverse ML texts.", .icon_type = IT_DISABLE_SOME_FEATURE, }, #endif #ifdef FEATURE_LV_CRAZY_COLORS { .name = "LV crazy colors", .priv = &preview_crazy, .min = 0, .max = 2, .edit_mode = EM_MANY_VALUES_LV, .choices = (const char *[]) {"OFF", "Swap U-V", "Extreme Chroma"}, .depends_on = DEP_LIVEVIEW, .icon_type = IT_PERCENT_OFF, .help = "Crazy color effects that may help with white balance.", .help2 = "For LiveView preview only. Does not affect recording.\n" "Swap U-V: reverses red and blue components\n" "Extreme Chroma: highly saturated image showing WB direction\n", }, #endif #ifdef FEATURE_DISPLAY_SHAKE #ifndef CONFIG_CAN_REDIRECT_DISPLAY_BUFFER_EASILY #define This requires CONFIG_CAN_REDIRECT_DISPLAY_BUFFER_EASILY. #endif { .name = "Display Shake", .priv = &display_shake, .max = 1, .help = "Emphasizes camera shake on LiveView display.", .depends_on = DEP_LIVEVIEW, }, #endif #ifdef FEATURE_FORCE_HDMI_VGA { .name = "Force HDMI-VGA", .priv = &hdmi_force_vga, .max = 1, .help = "Force low resolution (720x480) on HDMI displays.", }, #endif MENU_EOL }, }, #endif }; #ifndef CONFIG_5DC static struct menu_entry play_menus[] = { #if defined(FEATURE_SET_MAINDIAL) || defined(FEATURE_IMAGE_REVIEW_PLAY) || defined(FEATURE_QUICK_ZOOM) || defined(FEATURE_REMEMBER_LAST_ZOOM_POS_5D3) || defined(FEATURE_LV_BUTTON_PROTECT) || defined(FEATURE_LV_BUTTON_RATE) || defined(FEATURE_QUICK_ERASE) { .name = "Image review settings", .select = menu_open_submenu, .submenu_width = 715, .help = "Options for PLAY (image review) mode.", .depends_on = DEP_PHOTO_MODE, .children = (struct menu_entry[]) { #ifdef FEATURE_SET_MAINDIAL { .name = "Play mode actions", .help = "Several helpful image actions you can trigger in PLAY mode.", .select = menu_open_submenu, .submenu_width = 660, .children = (struct menu_entry[]) { { .name = "Action type", .priv = &play_set_wheel_action, .max = 4, .choices = (const char *[]) {"OFF", "Exposure Fusion", "Compare Images", "Timelapse Play", "Exposure Adjust"}, .help = "Chose the action type to perform when triggered.", .icon_type = IT_PERCENT_OFF, }, { .name = "Trigger key(s)", .priv = &play_set_wheel_trigger, .max = 2, .choices = (const char *[]) {"Set+MainDial", "Left/Right", "L/R & Set+Dial"}, .help = "Either use a key combination and/or just an easier single keystroke.", .icon_type = IT_DICE, }, MENU_EOL } }, #endif #ifdef FEATURE_IMAGE_REVIEW_PLAY { .name = "Image Review", .priv = &quick_review_allow_zoom, .max = 1, .choices = (const char *[]) {"QuickReview default", "CanonMnu:Hold->PLAY"}, .help = "When you set \"ImageReview: Hold\", it will go to Play mode.", .icon_type = IT_BOOL, }, #endif #ifdef FEATURE_QUICK_ZOOM { .name = "Quick Zoom", .priv = &quickzoom, .max = 4, .choices = (const char *[]) {"OFF", "ON (fast zoom)", "SinglePress -> 100%", "Full zoom on AF pt.", "Full Z on last pos."}, .help = "Faster zoom in Play mode, for pixel peeping :)", //.essential = FOR_PHOTO, .icon_type = IT_DICE_OFF, }, #endif #ifdef FEATURE_REMEMBER_LAST_ZOOM_POS_5D3 { .name = "Remember last Zoom pos", .priv = &quickzoom, .max = 1, .help = "Remember last Zoom position in playback mode.", .icon_type = IT_BOOL, }, #endif #if defined(FEATURE_LV_BUTTON_PROTECT) || defined(FEATURE_LV_BUTTON_RATE) { .name = "LV button", .priv = &play_lv_action, .choices = (const char *[]) {"Default", "Protect Image", "Rate Image"}, #if defined(FEATURE_LV_BUTTON_PROTECT) && defined(FEATURE_LV_BUTTON_RATE) .max = 2, .help = "You may use the LiveView button to protect or rate images.", #ifdef FEATURE_LV_BUTTON_RATE_UPDOWN .help2 = "Also up/down keys/joystick work as +/- rating if zoomed out.", #endif .icon_type = IT_DICE_OFF, #elif defined(FEATURE_LV_BUTTON_PROTECT) .max = 1, .help = "You may use the LiveView button to protect images quickly.", .icon_type = IT_BOOL, #else #error Hudson, we have a problem! #endif }, #endif #ifdef FEATURE_QUICK_ERASE { .name = "Quick Erase", .priv = &quick_delete, .max = 1, #ifdef CONFIG_50D // no unpress SET, use the 5Dc method .help = "Delete files quickly with fewer keystrokes (be careful!!!)", #else .choices = (const char *[]) {"OFF", "SET+Erase"}, .help = "Delete files quickly with SET+Erase (be careful!!!)", #endif }, #endif MENU_EOL, }, }, #endif }; #else // CONFIG_5DC (todo: cleanup this mess) static MENU_UPDATE_FUNC(preview_saturation_display_5dc) { extern int focus_peaking_grayscale; if (focus_peaking_grayscale && is_focus_peaking_enabled()) MENU_SET_WARNING(MENU_WARN_NOT_WORKING, "Focus peaking with grayscale preview is enabled."); } static struct menu_entry play_menus[] = { { .name = "Saturation", .priv = &preview_saturation, .min = -1, .max = 2, .update = preview_saturation_display_5dc, .choices = (const char *[]) {"0 (Grayscale)", "Normal", "High", "Very high"}, .help = "For preview only - adjust display saturation.", .icon_type = IT_BOOL, }, { .name = "Image Review", .priv = &quick_review_allow_zoom, .max = 1, .choices = (const char *[]) {"QuickReview default", "CanonMnu:Hold->PLAY"}, .help = "When you set \"ImageReview: Hold\", it will go to Play mode.", .icon_type = IT_BOOL, }, { .name = "Quick Zoom", .priv = &quickzoom, .max = 2, // don't know how to move the image around .choices = (const char *[]) {"OFF", "ON (fast zoom)"}, .help = "Faster zoom in Play mode, for pixel peeping :)", //.essential = FOR_PHOTO, .icon_type = IT_BOOL, }, { .name = "Quick Erase", .priv = &quick_delete, .max = 1, .help = "Delete files quickly with fewer keystrokes (be careful!!!)", }, { .name = "SET+MainDial", .priv = &play_set_wheel_action, .min = 2, .max = 3, .choices = (const char *[]) {"Timelapse Play", "Exposure Adjust"}, .help = "What to do when you press SET and turn the scrollwheel.", //.essential = FOR_PHOTO, .icon_type = IT_BOOL, //~ .edit_mode = EM_MANY_VALUES, }, }; #endif static void tweak_init() { menu_add( "Prefs", play_menus, COUNT(play_menus) ); #ifdef FEATURE_LV_ZOOM_SETTINGS extern struct menu_entry tweak_menus_shoot[]; menu_add( "Prefs", tweak_menus_shoot, 1 ); #endif menu_add( "Prefs", key_menus, COUNT(key_menus) ); menu_add( "Prefs", tweak_menus, COUNT(tweak_menus) ); menu_add( "Display", display_menus, COUNT(display_menus) ); } INIT_FUNC(__FILE__, tweak_init); // dummy stubs #ifndef FEATURE_ARROW_SHORTCUTS int arrow_keys_shortcuts_active() { return 0; } #endif