Raw File
tweaks.c
/** \file
 * Tweaks to default UI behavior
 */
#include "zebra.h"
#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"

void display_gain_toggle(void* priv, int dir);
void clear_lv_affframe();

static void upside_down_step();

CONFIG_INT("dof.preview.sticky", dofpreview_sticky, 0);

static void
dofp_display( void * priv, int x, int y, int selected )
{
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
		"DOF Preview (photos): %s",
		dofpreview_sticky  ? "Sticky" : "Normal"
	);
}

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);
	return prop_cleanup(token, property);
}

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"); card_led_on(); }
		else if (old_value) { redraw(); card_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;
	}
}


// ExpSim
//**********************************************************************
CONFIG_INT( "expsim", expsim_setting, 2);

static void set_expsim( int x )
{
	if (expsim != x)
	{
		prop_request_change(PROP_LIVE_VIEW_VIEWTYPE, &x, 4);
	}
}
/*
static void
expsim_toggle( void * priv )
{
	// off, on, auto
	if (!expsim_auto && !expsim) // off->on
	{
		set_expsim(1);
	}
	else if (!expsim_auto && expsim) // on->auto
	{
		expsim_auto = 1;
	}
	else // auto->off
	{
		expsim_auto = 0;
		set_expsim(0);
	}
}*/
int get_expsim_auto_value()
{
	extern int bulb_ramp_calibration_running; 
	if (bulb_ramp_calibration_running) 
		return 0; // temporarily disable ExpSim to make sure display gain will work
	
	#if defined(CONFIG_50D) || defined(CONFIG_5D2)
	if (is_movie_mode()) return expsim_setting;
	#else
	if (is_movie_mode()) return 2;
	#endif

	// underexposure bug with manual lenses in M mode
	#if defined(CONFIG_60D)
	if (expsim_setting == 2 &&
		shooting_mode == SHOOTMODE_M && 
		!lens_info.name[0] && 
		lens_info.raw_iso != 0 && 
		lens_info.raw_shutter < 93 // the image will be too dark to preview via BV mode, better turn off ExpSim
	)
		return 0;
	#endif

	// silent pic in matrix mode requires expsim on
	extern int silent_pic_running;
	if (silent_pic_running) return 1;
	
	if (expsim_setting == 2)
	{
		if ((lv_dispsize > 1 || should_draw_zoom_overlay()) && !get_halfshutter_pressed()) return 0;
		else return 1;
	}
	else return expsim_setting;
}

static void
expsim_display( void * priv, int x, int y, int selected )
{
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
		"Exp.Sim     : %s",
		expsim == 2 ? "Movie" :
		expsim_setting == 2 ? (get_expsim_auto_value() ? "Auto (ON)" : "Auto (OFF)") : 
		get_expsim_auto_value() ? "ON" : "OFF"
	);
	if (CONTROL_BV) menu_draw_icon(x, y, MNI_WARNING, (intptr_t) "Exposure override is active.");
	else if (!lv) menu_draw_icon(x, y, MNI_WARNING, (intptr_t) "This option works only in LiveView");
	else menu_draw_icon(x, y, expsim == 2 ? MNI_AUTO : expsim != get_expsim_auto_value() ? MNI_WARNING : expsim_setting == 2 ? MNI_AUTO : MNI_BOOL(expsim), (intptr_t) "Could not set ExpSim");
}

static void expsim_update()
{
	if (!lv) return;
	if (shooting_mode == SHOOTMODE_MOVIE) return;
	int expsim_auto_value = get_expsim_auto_value();
	if (expsim_auto_value != expsim)
	{
		if (MENU_MODE && !gui_menu_shown()) // ExpSim changed from Canon menu
		{ 
			expsim_setting = expsim;
			NotifyBox(2000, "ML: Auto ExpSim disabled.");
		}
		else
		{
			set_expsim(expsim_auto_value); // shooting mode, ML decides to toggle ExpSim
		}
	}
}

static void expsim_toggle(void* priv, int delta)
{
	if (is_movie_mode()) return;
	menu_ternary_toggle(priv, delta); msleep(100);
}

// LV metering
//**********************************************************************

CONFIG_INT("lv.metering", lv_metering, 0);

static void
lv_metering_print( void * priv, int x, int y, int selected )
{
	//unsigned z = *(unsigned*) priv;
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
		"LV Auto ISO (M mode): %s",
		lv_metering == 0 ? "OFF" :
		lv_metering == 1 ? "Spotmeter" :
		lv_metering == 2 ? "CenteredHist" :
		lv_metering == 3 ? "HighlightPri" :
		lv_metering == 4 ? "NoOverexpose" : "err"
	);
	if (lv_metering)
	{
		if (shooting_mode != SHOOTMODE_M || !lv)
			menu_draw_icon(x, y, MNI_WARNING, (intptr_t) "Only works in photo mode (M), LiveView");
		if (!expsim)
			menu_draw_icon(x, y, MNI_WARNING, (intptr_t) "ExpSim is OFF");
	}
}

static void
shutter_alter( int sign)
{
	#ifdef CONFIG_60D
	sign *= 3;
	#endif
	
	if (AE_VALUE > 5*8 && sign < 0) return;
	if (AE_VALUE < -5*8 && sign > 0) return;
	
	int rs = lens_info.raw_shutter;
	//~ for (int k = 0; k < 8; k++)
	{
		rs += sign;
		lens_set_rawshutter(rs);
		msleep(10);
		if (lens_info.raw_shutter == rs) return;
	}
}

static void
iso_alter( int sign)
{
	sign = -sign;
	sign *= 8;
	
	if (AE_VALUE > 5*8 && sign < 0) return;
	if (AE_VALUE < -5*8 && sign > 0) return;
	
	int ri = lens_info.raw_iso;
	//~ for (int k = 0; k < 8; k++)
	{
		ri += sign;
		ri = MIN(ri, 120); // max ISO 6400
		lens_set_rawiso(ri);
		msleep(10);
		if (lens_info.raw_iso == ri) return;
	}
}

static void
lv_metering_adjust()
{
	if (!lv) return;
	if (!expsim) return;
	if (gui_menu_shown()) return;
	if (!liveview_display_idle()) return;
	if (ISO_ADJUSTMENT_ACTIVE) return;
	if (get_halfshutter_pressed()) return;
	if (lv_dispsize != 1) return;
	//~ if (shooting_mode != SHOOTMODE_P && shooting_mode != SHOOTMODE_AV && shooting_mode != SHOOTMODE_TV) return;
	if (shooting_mode != SHOOTMODE_M) return;
	
	if (lv_metering == 1)
	{
		int Y,U,V;
		get_spot_yuv(5, &Y, &U, &V);
		//bmp_printf(FONT_LARGE, 0, 100, "Y %d AE %d  ", Y, lens_info.ae);
		iso_alter(SGN(Y-128));
	}
	else if (lv_metering == 2) // centered histogram
	{
		int under, over;
		get_under_and_over_exposure_autothr(&under, &over);
		//~ bmp_printf(FONT_MED, 10, 40, "over=%d under=%d ", over, under);
		if (over > under) iso_alter(1);
		else iso_alter(-1);
	}
	else if (lv_metering == 3) // highlight priority
	{
		int under, over;
		get_under_and_over_exposure(10, 235, &under, &over);
		//~ bmp_printf(FONT_MED, 10, 40, "over=%d under=%d ", over, under);
		if (over > 100 && under < over * 5) iso_alter(1);
		else iso_alter(-1);
	}
	else if (lv_metering == 4) // don't overexpose
	{
		int under, over;
		get_under_and_over_exposure(5, 235, &under, &over);
		//~ bmp_printf(FONT_MED, 10, 40, "over=%d ", over);
		if (over > 100) iso_alter(1);
		else iso_alter(-1);
	}
	msleep(500);
	//~ bee
	//~ beep();
}

// auto burst pic quality
//**********************************************************************

CONFIG_INT("burst.auto.picquality", auto_burst_pic_quality, 0);

static 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);
}

int picq_saved = -1;
static void decrease_pic_quality()
{
	if (picq_saved == -1) picq_saved = pic_quality; // save only first change
	
	int newpicq = 0;
 	switch(pic_quality)
 	{
		case PICQ_RAW_JPG_LARGE_FINE:
 			newpicq = PICQ_LARGE_FINE;
 			break;
 		case PICQ_RAW:
			newpicq = PICQ_LARGE_FINE;
 			break;
 		case PICQ_LARGE_FINE:
 			newpicq = PICQ_MED_FINE;
 			break;
		//~ case PICQ_MED_FINE:
			//~ newpicq = PICQ_SMALL_FINE;
			//~ break;
 		//~ case PICQ_SMALL_FINE:
 			//~ newpicq = PICQ_SMALL_COARSE;
 			//~ break;
 		case PICQ_LARGE_COARSE:
 			newpicq = PICQ_MED_COARSE;
 			break;
		//~ case PICQ_MED_COARSE:
			//~ newpicq = PICQ_SMALL_COARSE;
			//~ break;
 	}
 	if (newpicq) set_pic_quality(newpicq);
}
 
static void restore_pic_quality()
{
	if (picq_saved != -1) set_pic_quality(picq_saved);
	picq_saved = -1;
}

static void adjust_burst_pic_quality()
{
	if (lens_info.job_state == 0) { restore_pic_quality(); return; }
	if (burst_count < 4) decrease_pic_quality();
	else if (burst_count >= 5) restore_pic_quality();
}

#if !defined(CONFIG_60D) && !defined(CONFIG_50D) && !defined(CONFIG_5D2)
PROP_HANDLER(PROP_BURST_COUNT)
{
	int burst_count = buf[0];

	if (auto_burst_pic_quality && avail_shot > burst_count)
	{
		adjust_burst_pic_quality();
	}

	return prop_cleanup(token, property);
}
#endif

static void
auto_burst_pic_display(
	void *			priv,
	int			x,
	int			y,
	int			selected
)
{
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
		"Auto BurstPicQuality: %s", 
		auto_burst_pic_quality ? "ON" : "OFF"
	);
}

void lcd_sensor_shortcuts_print( void * priv, int x, int y, int selected);
extern unsigned lcd_sensor_shortcuts;

// backlight adjust
//**********************************************************************

int display_gain = 0;

void show_display_gain()
{
	int gain_ev = 0;
	if (display_gain) gain_ev = gain_to_ev(display_gain) - 10;
	NotifyBox(2000, "Display Gain : %s%d EV", display_gain ? "+" : "", gain_ev);
	redraw();
}

void adjust_backlight_level(int delta)
{
	if (backlight_level < 1 || backlight_level > 7) return; // kore wa dame desu yo
	if (tft_status) call("TurnOnDisplay");
	
	// if we run out of backlight, adjust display gain instead
	
	if (lv)
	{
		if (backlight_level == 7 && delta > 0)
		{
			beep();
			display_gain_toggle(0, 1);
			show_display_gain();
			return;
		}

		if (backlight_level == 7 && delta < 0 && display_gain)
		{
			beep();
			display_gain_toggle(0, -1);
			show_display_gain();
			return;
		}
	}
	
	int level = COERCE(backlight_level + delta, 1, 7);
	prop_request_change(PROP_BACKLIGHT_LEVEL, &level, 4);
	NotifyBoxHide();
	NotifyBox(2000, "LCD Backlight: %d", level);
}
void set_backlight_level(int level)
{
	level = COERCE(level, 1, 7);
	prop_request_change(PROP_BACKLIGHT_LEVEL, &level, 4);
}

CONFIG_INT("af.frame.autohide", af_frame_autohide, 1);

static void
af_frame_autohide_display(
        void *                  priv,
        int                     x,
        int                     y,
        int                     selected
)
{
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
		"AF frame display    : %s", 
		af_frame_autohide ? "AutoHide" : "Show"
	);
}

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_affframe_if_dirty()
{
	//~ #ifndef CONFIG_50D
	if (af_frame_autohide && afframe_countdown && liveview_display_idle())
	{
		BMP_LOCK (
			if (afframe_countdown)
			{
				afframe_countdown--;
				if (!afframe_countdown) 
					clear_lv_affframe();
			}
		)
	}
	//~ #endif
}

void clear_lv_affframe()
{
	if (!lv) return;
	if (gui_menu_shown()) return;
	if (lv_dispsize != 1) return;
	struct vram_info *	lv = get_yuv422_vram();
	if( !lv->vram )	return;
	int xaf,yaf;
	
	get_afframe_pos(720, 480, &xaf, &yaf);
	xaf = N2BM_X(xaf);
	yaf = N2BM_Y(yaf);
	//~ bmp_printf(FONT_LARGE, 200, 200, "af %d %d ", xaf, yaf);
	//~ bmp_fill(0, COERCE(xaf,100, BMP_WIDTH-100) - 90, COERCE(yaf,100, BMP_HEIGHT-100) - 75, 180, 150 );
	int x0 = COERCE(xaf,100, BMP_WIDTH-100) - 100;
	int y0 = COERCE(yaf, 75+os.off_169, BMP_HEIGHT-75-os.off_169) - 75;
	int w = 200;
	int h = 150;
	for (int i = y0; i < y0 + h; i++)
	{
		for (int j = x0; j < x0+w; j++)
		{
			int p = bmp_getpixel(j,i);
			if (p == COLOR_BLACK || p == COLOR_WHITE)
				bmp_putpixel(j,i,0);
			asm("nop");
			asm("nop");
			asm("nop");
			asm("nop"); // just in case
		}
	}
	crop_set_dirty(5);
	afframe_countdown = 0;
}

CONFIG_INT("play.quick.zoom", quickzoom, 2);

static void
quickzoom_display(
        void *                  priv,
        int                     x,
        int                     y,
        int                     selected
)
{
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
		"Zoom in PLAY mode : %s", 
		quickzoom == 0 ? "Normal" :
		quickzoom == 2 ? "Fast+100%" :
		quickzoom == 1 ? "Fast" : "err"
	);
}

#ifdef CONFIG_60D

CONFIG_INT("display.off.halfshutter", display_off_by_halfshutter_enabled, 0);

int display_turned_off_by_halfshutter = 0; // 1 = display was turned off, -1 = display should be turned back on (ML should take action)

PROP_INT(PROP_INFO_BUTTON_FUNCTION, info_button_function);

static void display_on_and_go_to_main_shooting_screen()
{
	if (lv) return;
	if (tft_status == 0) return; // display already on
	if (gui_state != GUISTATE_IDLE) return;
	
	display_turned_off_by_halfshutter = 0;
	
	int old = info_button_function;
	int x = 1;
	//~ card_led_blink(5,100,100);
	prop_request_change(PROP_INFO_BUTTON_FUNCTION, &x, 4); // temporarily make the INFO button display only the main shooting screen
	fake_simple_button(BGMT_DISP);
	msleep(300);
	prop_request_change(PROP_INFO_BUTTON_FUNCTION, &old, 4); // restore user setting back
}

int handle_disp_button_in_photo_mode() // called from handle_buttons
{
	if (display_off_by_halfshutter_enabled && display_turned_off_by_halfshutter == 1 && gui_state == GUISTATE_IDLE && !gui_menu_shown())
	{
		display_turned_off_by_halfshutter = -1; // request: ML should turn it on
		return 0;
	}
	return 1;
}

static void display_off_by_halfshutter()
{
	static int prev_gui_state = 0;
	if (prev_gui_state != GUISTATE_IDLE) 
	{ 
		msleep(100);
		prev_gui_state = gui_state;
		while (get_halfshutter_pressed()) msleep(100);
		return; 
	}
	prev_gui_state = gui_state;
		
	if (!lv && gui_state == GUISTATE_IDLE) // main shooting screen, photo mode
	{
		if (tft_status == 0) // display is on
		{
			if (get_halfshutter_pressed())
			{
				// wait for long half-shutter press (1 second)
				int i;
				for (i = 0; i < 10; i++)
				{
					msleep(100);
					if (!get_halfshutter_pressed()) return;
					if (tft_status) return;
				}
				fake_simple_button(BGMT_DISP); // turn display off
				while (get_halfshutter_pressed()) msleep(100);
				display_turned_off_by_halfshutter = 1; // next INFO press will go to main shooting screen
				return;
			}
		}
		else // display is off
		{
			if (display_turned_off_by_halfshutter == -1)
			{
				display_on_and_go_to_main_shooting_screen();
				display_turned_off_by_halfshutter = 0;
			}
		}
	}
}

static void
display_off_by_halfshutter_print(
        void *                  priv,
        int                     x,
        int                     y,
        int                     selected
)
{
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
		"DispOFF(Photo) : %s", // better name for this?
		display_off_by_halfshutter_enabled ? "HalfShutter" : "OFF"
	);
	if (display_off_by_halfshutter_enabled && lv)
		menu_draw_icon(x, y, MNI_WARNING, (intptr_t)"This option does not work in LiveView");
}

#endif

CONFIG_INT("crop.playback", cropmarks_play, 0);

static void
cropmarks_play_display(
        void *                  priv,
        int                     x,
        int                     y,
        int                     selected
)
{
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
		"Cropmarks   (PLAY): %s", 
		cropmarks_play ? "ON" : "OFF"
	);
}

CONFIG_INT("play.set.wheel", play_set_wheel_action, 2);

static void
play_set_wheel_display(
        void *                  priv,
        int                     x,
        int                     y,
        int                     selected
)
{
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
		"SET+MainDial(PLAY): %s", 
		play_set_wheel_action == 0 ? "422 Preview" :
		play_set_wheel_action == 1 ? "ExposureFusion" : 
		play_set_wheel_action == 2 ? "CompareImages" : 
		play_set_wheel_action == 3 ? "TimelapsePlay" : "err"
	);
}

CONFIG_INT("quick.delete", quick_delete, 0);
static void
quick_delete_print(
        void *                  priv,
        int                     x,
        int                     y,
        int                     selected
)
{
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
		"Quick Erase       : %s", 
		quick_delete ? "SET+Erase" : "OFF"
	);
}

int timelapse_playback = 0;

void playback_set_wheel_action(int dir)
{
	if (play_set_wheel_action == 0) play_next_422(dir);
	else if (play_set_wheel_action == 1) expfuse_preview_update(dir);
	else if (play_set_wheel_action == 2) playback_compare_images(dir);
	else if (play_set_wheel_action == 3) timelapse_playback += dir;
}

int handle_set_wheel_play(struct event * event)
{
	if (gui_menu_shown()) return 1;
	
	extern int set_pressed;
	// SET button pressed
	//~ if (event->param == BGMT_PRESS_SET) set_pressed = 1;
	//~ if (event->param == BGMT_UNPRESS_SET) set_pressed = 0;
	//~ if (event->param == BGMT_PLAY) set_pressed = 0;

	// reset exposure fusion preview
	extern int expfuse_running;
	if (set_pressed == 0)
	{
		expfuse_running = 0;
	}

	// SET+Wheel action in PLAY mode
	if ( PLAY_MODE && get_set_pressed())
	{
		if (!IS_FAKE(event) && (event->param == BGMT_WHEEL_LEFT || event->param == BGMT_WHEEL_RIGHT))
		{
			int dir = event->param == BGMT_WHEEL_RIGHT ? 1 : -1;
			playback_set_wheel_action(dir);
			return 0;
		}
		
		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;
			}
		}
	}
	
	return 1;
}

CONFIG_INT("play.lv.button", play_lv_action, 1);

static void
play_lv_display(
        void *                  priv,
        int                     x,
        int                     y,
        int                     selected
)
{
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
		"LV button   (PLAY): %s", 
		play_lv_action == 0 ? "Default" :
		play_lv_action == 1 ? "Protect Image" : "err"
	);
}

#if defined(CONFIG_60D) || defined(CONFIG_600D)
int handle_lv_play(struct event * event)
{
	if (!play_lv_action) return 1;
	
	if (event->param == BGMT_LV && PLAY_MODE)
	{
		fake_simple_button(BGMT_Q); // toggle protect current image
		fake_simple_button(BGMT_WHEEL_DOWN);
		fake_simple_button(BGMT_Q);
		return 0;
	}
	return 1;
}
#endif


// don't save it in config file, it's easy to forget it activated
int fake_halfshutter = 0;

static void
fake_halfshutter_print(
	void *			priv,
	int			x,
	int			y,
	int			selected
)
{
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
		"Shutter Half-Press  : %s",
		fake_halfshutter == 1 ? "Sticky" : 
		fake_halfshutter == 2 ? "Every second" : 
		fake_halfshutter == 3 ? "Every 200ms" : 
		fake_halfshutter == 4 ? "Every 20ms" : 
		"OFF"
	);
}

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, "  ");
}

void 
fake_halfshutter_step()
{
	if (gui_menu_shown()) return;
	if (fake_halfshutter >= 2)
	{
		if (gui_state == GUISTATE_IDLE && !gui_menu_shown() && !get_halfshutter_pressed())
		hs_show();
		SW1(1,5);
		SW1(0,0);
		hs_hide();
		if (fake_halfshutter == 2) msleep(1000);
		else if (fake_halfshutter == 3) msleep(200);
		else msleep(20);
	}
	else if (fake_halfshutter == 1) // 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)
				{
					card_led_on();
					SW1(1,50);
					state = 1;
				}
			}
			else
			{
				if (hs == 0) { state = 0; card_led_off(); }
			}
		}
		old_value = hs;
	}
}

static void
tweak_task( void* unused)
{
	do_movie_mode_remap();
	
	int k;
	for (k = 0; ; k++)
	{
		if (fake_halfshutter)
			fake_halfshutter_step(); // this one should msleep as needed
		else
			msleep(50);
		
		if (lv_metering && !is_movie_mode() && lv && k % 5 == 0)
		{
			lv_metering_adjust();
		}
		
		// 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;
		}
		
		// faster zoom in play mode
		if (quickzoom && PLAY_MODE)
		{
			if (get_zoom_in_pressed()) 
			{
				for (int i = 0; i < 10; i++)
				{
					if (quickzoom == 2 && PLAY_MODE && MEM(IMGPLAY_ZOOM_LEVEL_ADDR) <= 1)
					{
						MEM(IMGPLAY_ZOOM_LEVEL_ADDR) = IMGPLAY_ZOOM_LEVEL_MAX-1;
						MEM(IMGPLAY_ZOOM_LEVEL_ADDR + 4) = IMGPLAY_ZOOM_LEVEL_MAX-1;
						#ifdef CONFIG_500D
						fake_simple_button(BGMT_PRESS_ZOOMIN_MAYBE);
						#endif
					}
					msleep(30);
				}
				while (get_zoom_in_pressed()) {	fake_simple_button(BGMT_PRESS_ZOOMIN_MAYBE); msleep(50); }
			}
			
			if (get_zoom_out_pressed())
			{
				msleep(300);
				while (get_zoom_out_pressed()) {	fake_simple_button(BGMT_PRESS_ZOOMOUT_MAYBE); msleep(50); }
			}
		}
		
		expsim_update();
		
		dofp_update();

		clear_lv_affframe_if_dirty();
		
		extern unsigned disp_profiles_0;
		if (FLASH_BTN_MOVIE_MODE)
		{
			int k = 0;
			int longpress = 0;
			if (disp_profiles_0) bmp_printf(FONT_MED, 245, 100, "DISP preset toggle");
			while (FLASH_BTN_MOVIE_MODE)
			{
				msleep(100);
				k++;
				
				if (k > 3)
				{
					longpress = 1; // long press doesn't toggle
					if (disp_profiles_0) bmp_printf(FONT(FONT_MED, 1, 0), 245, 100, "                  ");
				}
				BMP_LOCK( draw_ml_bottombar(0,0); )
			}
			if (disp_profiles_0 && !longpress) toggle_disp_mode();
			redraw();
		}
		
		#ifdef CONFIG_60D
		if (display_off_by_halfshutter_enabled)
			display_off_by_halfshutter();
		#endif
		
		upside_down_step();
		
		if ((lv_disp_mode == 0 && LV_BOTTOM_BAR_DISPLAYED) || ISO_ADJUSTMENT_ACTIVE)
			idle_wakeup_reset_counters();
	}
}

TASK_CREATE("tweak_task", tweak_task, 0, 0x1e, 0x1000 );

extern int quick_review_allow_zoom;

static void
qrplay_display(
        void *                  priv,
        int                     x,
        int                     y,
        int                     selected
)
{
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
		"After taking a pic: %s", 
		quick_review_allow_zoom ? "Hold->Play" : "QuickReview"
	);
}
/*
extern int set_on_halfshutter;

static void
set_on_halfshutter_display(
        void *                  priv,
        int                     x,
        int                     y,
        int                     selected
)
{
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
		"HalfShutter in DLGs : %s", 
		set_on_halfshutter ? "SET" : "Cancel"
	);
}*/

extern int iso_round_only;
static void
iso_round_only_display(
        void *                  priv,
        int                     x,
        int                     y,
        int                     selected
)
{
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
		"ISO selection       : %s", 
		iso_round_only ? "100x, 160x" : "All values"
	);
	menu_draw_icon(x, y, iso_round_only ? MNI_ON : MNI_AUTO, 0);
}


CONFIG_INT("swap.menu", swap_menu, 0);
static void
swap_menu_display(
        void *                  priv,
        int                     x,
        int                     y,
        int                     selected
)
{
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
		"Swap MENU <-> ERASE : %s", 
		swap_menu ? "ON" : "OFF"
	);
}

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;
}

/*extern int picstyle_disppreset_enabled;
static void
picstyle_disppreset_display(
        void *                  priv,
        int                     x,
        int                     y,
        int                     selected
)
{
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
		"PicSty->DISP preset : %s",
		picstyle_disppreset_enabled ? "ON" : "OFF"
	);
}*/

extern unsigned display_dont_mirror;
static void
display_dont_mirror_display(
        void *                  priv,
        int                     x,
        int                     y,
        int                     selected
)
{
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
		"Auto Mirroring : %s", 
		display_dont_mirror ? "Don't allow": "Allow"
	);
}

/*
int night_vision = 0;
void night_vision_toggle(void* priv)
{
	night_vision = !night_vision;
	call("lvae_setdispgain", night_vision ? 65535 : 0);
	menu_show_only_selected();
}

static void night_vision_print(
	void *			priv,
	int			x,
	int			y,
	int			selected
)
{
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
		"Night Vision Mode   : %s", 
		night_vision ? "ON" : "OFF"
	);
	if (night_vision && (!lv || is_movie_mode()))
		menu_draw_icon(x, y, MNI_WARNING, 0);
}
*/

void set_display_gain(int display_gain)
{
	if (CONTROL_BV) CONTROL_BV_ZERO = display_gain;
	call("lvae_setdispgain", COERCE(display_gain, 0, 65535));
}

// 1024 = 0 EV
// +: off, +1EV, +2EV, ... , +6EV, -3EV, -2EV, -1EV, off
// -: reverse
void display_gain_toggle(void* priv, int dir)
{
	int d = display_gain;
	if (!d) d = 1024;
	 
	if (dir > 0)
	{
		display_gain = d * 2;
		if (display_gain > 65536) display_gain = 128;
	}
	else if (dir < 0)
	{
		if (d <= 128) display_gain = 65536;
		else display_gain = d / 2; 
	}
	else display_gain = 0;

	if (display_gain == 1024) display_gain = 0;

	set_display_gain(display_gain);
}

static void display_gain_reset(void* priv, int delta)
{
    display_gain_toggle(0,0);
}

int gain_to_ev(int gain)
{
	if (gain == 0) return 0;
	return (int) roundf(log2f(gain));
}

static void display_gain_print(
	void *			priv,
	int			x,
	int			y,
	int			selected
)
{
	int gain_ev = 0;
	if (display_gain) gain_ev = gain_to_ev(display_gain) - 10;
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
		"LV Disp.Gain: %s%d EV",
		gain_ev > 0 ? "+" : gain_ev < 0 ? "-" : "",
		ABS(gain_ev)
	);
	if (display_gain)
	{
		if (!lv) menu_draw_icon(x, y, MNI_WARNING, (intptr_t) "This option works only in LiveView");
		else menu_draw_icon(x, y, MNI_PERCENT, gain_ev * 100 / 6);
	}
}

/*
PROP_INT(PROP_ELECTRIC_SHUTTER, eshutter);

static void eshutter_display(
	void *			priv,
	int			x,
	int			y,
	int			selected
)
{
	int gain_ev = 0;
	if (display_gain) gain_ev = gain_to_ev(display_gain) - 10;
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
		"Electric Shutter    : %d",
		eshutter
	);
}
static void set_eshutter(int e)
{
	e = COERCE(e, 0, 2);
	call("lv_eshutter", e);
	eshutter = e;
	GUI_SetElectricShutter(e);
	//~ prop_request_change(PROP_ELECTRIC_SHUTTER, &e, 4);
}
static void eshutter_toggle(void* priv)
{
	set_eshutter(mod(eshutter + 1, 3));
}*/

CONFIG_INT("digital.zoom.shortcut", digital_zoom_shortcut, 1);

void digital_zoom_shortcut_display(
        void *                  priv,
        int                     x,
        int                     y,
        int                     selected
)
{
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
		"DigitalZoom Shortcut: %s",
		digital_zoom_shortcut ? "1x, 3x" : "3x...10x"
	);
}

struct menu_entry tweak_menus[] = {
/*	{
		.name = "Night Vision Mode",
		.priv = &night_vision, 
		.select = night_vision_toggle, 
		.display = night_vision_print,
		.help = "Maximize LV display gain for framing in darkness (photo)"
	},*/
	{
		.name = "DOF Preview", 
		.priv = &dofpreview_sticky, 
		.select = menu_binary_toggle, 
		.select_auto = dofp_lock,
		.display = dofp_display,
		.help = "Sticky = click DOF to toggle. Or, press [Q] to lock now.",
	},
	{
		.name		= "Shutter Half-press",
		.priv = &fake_halfshutter,
		.select		= menu_quinternary_toggle,
		.display	= fake_halfshutter_print,
		.help = "Emulates half-shutter press, or make half-shutter sticky."
	},
	/*{
		.name = "Electric Shutter",
		.priv = &eshutter,
		.select = eshutter_toggle,
		.display = eshutter_display,
		.help = "For enabling third-party flashes in LiveView."
	},*/
	{
		.name = "AF frame display",
		.priv = &af_frame_autohide, 
		.select = menu_binary_toggle,
		.display = af_frame_autohide_display,
		.help = "You can hide the AF frame (the little white rectangle).",
		.icon_type = IT_DISABLE_SOME_FEATURE,
	},
	#if defined(CONFIG_550D) || defined(CONFIG_500D)
	{
		.name = "LCD Sensor Shortcuts",
		.priv		= &lcd_sensor_shortcuts,
		.select		= menu_binary_toggle,
		.display	= lcd_sensor_shortcuts_print,
	},
	#endif
	#if !defined(CONFIG_60D) && !defined(CONFIG_50D) && !defined(CONFIG_5D2) // 60D doesn't need this
	{
		.name = "Auto BurstPicQuality",
		.priv = &auto_burst_pic_quality, 
		.select = menu_binary_toggle, 
		.display = auto_burst_pic_display,
	},
	#endif
	#if 0
	{
		.name = "HalfShutter in DLGs",
		.priv = &set_on_halfshutter, 
		.select = menu_binary_toggle, 
		.display = set_on_halfshutter_display,
		.help = "Half-shutter press in dialog boxes => OK (SET) or Cancel."
	},
	#endif
	{
		.name = "ISO selection",
		.priv = &iso_round_only,
		.display	= iso_round_only_display,
		.select		= menu_binary_toggle,
		.help = "You can enable only ISOs which are multiple of 100 and 160."
	},
	/*{
		.name = "PicSty->DISP preset",
		.priv = &picstyle_disppreset_enabled,
		.display	= picstyle_disppreset_display,
		.select		= menu_binary_toggle,
		.help = "PicStyle can be included in DISP preset for easy toggle."
	},*/
	#ifdef CONFIG_60D
	{
		.name = "Swap MENU <--> ERASE",
		.priv = &swap_menu,
		.display	= swap_menu_display,
		.select		= menu_binary_toggle,
		.help = "Swaps MENU and ERASE buttons."
	},
	#endif
	{
		.name = "LV Auto ISO (M mode)",
		.priv = &lv_metering,
		.select = menu_quinternary_toggle, 
		.display = lv_metering_print,
		.help = "Experimental LV metering (Auto ISO). Too slow for real use."
	},
	#ifdef CONFIG_600D
	{
		.name = "DigitalZoom Shortcut",
		.priv = &digital_zoom_shortcut,
		.display = digital_zoom_shortcut_display, 
		.select = menu_binary_toggle,
		.help = "Movie: DISP + Zoom In toggles between 1x and 3x modes."
	},
	#endif
};



extern int menu_upside_down;
static void menu_upside_down_print(
	void *			priv,
	int			x,
	int			y,
	int			selected
)
{
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
		"UpsideDown mode: %s",
		menu_upside_down ? "ON" : "OFF"
	);
}

void NormalDisplay();
void MirrorDisplay();
void ReverseDisplay();

// 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())
		{
			bmp_draw_to_idle(1);
			canon_gui_disable_front_buffer();
			BMP_LOCK(
				bmp_flip(bmp_vram_real(), bmp_vram_idle());
			)
		}
		//~ msleep(100);
	}
}

void screenshot_start();
void take_screenshot();

struct menu_entry expo_tweak_menus[] = {
	{
		.name = "LV Disp.Gain (NightVision)", 
		.priv = &display_gain,
		.select = display_gain_toggle, 
		.select_auto = display_gain_reset,
		.display = display_gain_print, 
		.help = "Boosts LV digital display gain (Photo, Movie w.AutoISO)",
		.show_liveview = 1,
	},
	{
		.name = "Exposure Simulation",
		.priv = &expsim_setting,
		.select = expsim_toggle,
		.display = expsim_display,
		.help = "ExpSim: LCD image reflects exposure settings (ISO+Tv+Av).",
		.show_liveview = 1,
	},
};

static struct menu_entry display_menus[] = {
	{
		.name = "UpsideDown mode",
		.priv = &menu_upside_down,
		.display = menu_upside_down_print,
		.select = menu_binary_toggle,
		.help = "Displays all graphics upside-down and flips arrow keys.",
	},
#if defined(CONFIG_60D) || defined(CONFIG_600D)
	{
		.name = "Auto Mirroring",
		.priv = &display_dont_mirror,
		.display = display_dont_mirror_display, 
		.select = menu_binary_toggle,
		.help = "Prevents display mirroring, which may reverse ML texts.",
		.icon_type = IT_DISABLE_SOME_FEATURE,
	},
#endif
#if defined(CONFIG_60D) || defined(CONFIG_600D)
	{
		.name = "Display: Normal/Reverse/Mirror",
		.priv		= "Display: Normal/Reverse/Mirror",
		.select		= NormalDisplay,
		.select_reverse = ReverseDisplay,
		.select_auto = MirrorDisplay,
		.display	= menu_print,
		.help = "Display image: Normal [SET] / Reverse [PLAY] / Mirror [Q]"
	},
#endif
	{
		.name = "Screenshot (10 s)",
		.priv		= "Screenshot (10 s)",
		.select		= screenshot_start,
		.select_auto = take_screenshot,
		.display	= menu_print,
		.help = "Take a screenshot after 10 seconds [SET] or right now [Q].",
	},
#ifdef CONFIG_KILL_FLICKER
	{
		.name		= "Kill Canon GUI",
		.priv		= &kill_canon_gui_mode,
		.select		= menu_ternary_toggle,
		.display	= kill_canon_gui_print,
		.help = "Workarounds for disabling Canon graphics elements."
	},
#endif
	#ifdef CONFIG_60D
	{
		.name = "DispOFF in PhotoMode",
		.priv = &display_off_by_halfshutter_enabled,
		.display = display_off_by_halfshutter_print, 
		.select = menu_binary_toggle,
		.help = "Outside LV, turn off display with long half-shutter press."
	},
	#endif
};

struct menu_entry play_menus[] = {
	{
		.name = "SET+MainDial (PLAY)",
		.priv = &play_set_wheel_action, 
		.max = 3,
		.display = play_set_wheel_display,
		.help = "What to do when you hold SET and turn MainDial (Wheel)",
		.essential = FOR_PLAYBACK,
		.icon_type = IT_DICE,
	},
	{
		.name = "Cropmarks (PLAY)",
		.priv = &cropmarks_play, 
		.select = menu_binary_toggle, 
		.display = cropmarks_play_display,
		.help = "Show cropmarks in PLAY mode",
		.essential = FOR_PLAYBACK,
	},
	{
		.name = "After taking a photo",
		.priv = &quick_review_allow_zoom, 
		.select = menu_binary_toggle, 
		.display = qrplay_display,
		.help = "When you set \"ImageReview: Hold\", it will go to Play mode.",
		.essential = FOR_PLAYBACK,
		.icon_type = IT_REPLACE_SOME_FEATURE,
	},
	{
		.name = "Zoom in PLAY mode",
		.priv = &quickzoom, 
		.max = 2,
		.display = quickzoom_display,
		.help = "Faster zoom in Play mode, for pixel peeping :)",
		.essential = FOR_PLAYBACK,
		.icon_type = IT_DICE,
	},
#if defined(CONFIG_60D) || defined(CONFIG_600D)
	{
		.name = "LV button (PLAY)",
		.priv = &play_lv_action, 
		.select = menu_binary_toggle, 
		.display = play_lv_display,
		.help = "You may use the LiveView button to protect images quickly.",
		.essential = FOR_PLAYBACK,
	},
#endif
	{
		.name = "Quick Erase",
		.priv = &quick_delete, 
		.select = menu_binary_toggle, 
		.display = quick_delete_print,
		.help = "Delete files quickly with SET+Erase (be careful!!!)",
		.essential = FOR_PLAYBACK,
	},
};

static void tweak_init()
{
	menu_add( "Tweaks", tweak_menus, COUNT(tweak_menus) );
	menu_add( "Play", play_menus, COUNT(play_menus) );
	menu_add( "Display", display_menus, COUNT(display_menus) );
}

INIT_FUNC(__FILE__, tweak_init);
back to top