Input untangled

Discuss programming topics for the various GPL'd game engine sources.
Post Reply
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Input untangled

Post by Baker »

Quake's input code is a quantum entangled mess. I have no idea why I bother untangling the mess --- well it was probably rage against the machine.

The short versions goes like this:

Code: Select all

void IN_Think (in_think_type action)
{
	switch (action)
	{
	case in_initialize:
		Cmd_AddCommand ("force_centerview", Force_CenterView_f);
		IN_Local_Keyboard_Startup (); // Stupid stickey keys
		in_initialized = true;
		break;

	case in_startup:
		if (!in_initialized)
			IN_Think (in_initialize);
		IN_Think (in_acquire_mouse);
		IN_Think (in_acquire_keyboard);
		break;

	case in_acquire_mouse:
		if (mouse_acquired == false)
		{
			IN_Local_Mouse_Clip_Cursor (window_rect); // GET New clip region + ClipCursor (&window_rect); + center_x
			IN_Local_Acquire_Mouse (); // ShowCursor (FALSE); + SetCapture (wplat.mainwindow);
			IN_Think (in_clear_mouse_movement_accum);
			IN_Think (in_center_mouse_cursor);			
		}
		mouse_acquired = true;
		break;

	case in_center_mouse_cursor:
		IN_Local_Mouse_Cursor (window_center_x, window_center_y); //SetCursorPos (window_center_x, window_center_y);
		break;

	case in_update_mouse_region:
		IN_Think (in_clear_mouse_accum);
		IN_Local_Update_Mouse_Region (&mouseregion);
		break;

	case in_clear_mouse_movement_accum:
		mouse_accum_x = mouse_accum_y = 0;
		break;

	case in_release_mouse_buttons:
		Key_Release_Mouse_Buttons ();
		break;

	case in_unacquire_mouse:
		IN_Think (in_release_mouse_buttons);		// Fire -aliases
		mouse_old_button_state = 0;					// Now clear the buttons
		IN_Think (in_clear_mouse_movement_accum);	// Now clear the movement
		if (mouse_acquired == false)
		{
			IN_Local_Release_Mouse (); // ClipCursor (NULL); ReleaseCapture (); ShowCursor (TRUE);
		}
		mouse_acquired = false;
		break;

	case in_unacquire_keyboard:
		IN_Keyboard_Release_Keys (); // Key ups
		IN_Local_Keyboard_Unacquire (); // Stupid Windows key
		break;

	case in_acquire_keyboard:
		IN_Local_Keyboard_Acquire (); // Stupid Windows key
		break;

	case in_unacquire_all:	// Lost focus
		IN_Think (in_unacquire_mouse);
		IN_Think (in_unacquire_keyboard); // Stupid windows key
		break;

	case in_acquire_all: // Got focus
		IN_Think (in_acquire_keyboard); // Stupid windows key
		IN_Think (in_acquire_mouse);
		break;

	case in_shutdown:
		IN_Think (in_unaquire_all); // About same deal.
		IN_Local_Keyboard_Restore (); // Stupid Stickey keys on Windows
		break;

}
The slight longer version is multiplatform involving ClipCursor and friends and other crapola I'm somewhat tired of reading at this point.. Quake's input code is truly terrible --- my only motivation was someone has to win and someone has to lose --- and although the battle is a Pyrrhic victory --- to do it right there has to be a loser to the battle and it's not gonna be me.

P.S: To clean up this mess I also had to rewrite all the co-conspirator code = all the video. Go figure --- it was easier to rewrite ALL OF IT, then clean it up. Seriously, the video code almost rivals the suck of the input code and that's saying something.
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
hogsy
Posts: 198
Joined: Wed Aug 03, 2011 3:44 pm
Location: UK
Contact:

Re: Input untangled

Post by hogsy »

I've found a lot of Quake's code to be considerably messy while working on OpenKatana and I agree sometimes it's easier to just rewrite all of it than to try cleaning it up. Despite the time it can take it seems there's usually more long-term benefits that way as you can then better document the code you write and it means you have to spend less time reading over the code to understand it which then in-turn obviously means you can make changes much more efficiently.

Just a shame it can sometimes take so much time and effort, but it's usually worth it... Usually :lol:
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Input untangled

Post by Baker »

hogsy wrote:it seems there's usually more long-term benefits ...
Just a shame it can sometimes take so much time and effort, but it's usually worth it... Usually :lol:
I hope it becomes worth it :D I wasn't wanting to do this at all. But after trying it out, I am bothered less by this side quest. But really I wanted to be doing something else than the input code --- but I don't think I'll need to worry about the input code in the future now.

Revision 2: Far more streamlined than even the above + short. Modelled in part from MH's ideas of one place where input is handled.

Code: Select all

#include "quakedef.h"

#define NUM_MOUSE_BUTTONS 5
#define MRECT_PRINT(_x) _x.left, _x.top, _x.right, _x.bottom, _x.center_x, _x.center_y
enum { GET_IT = 1, LOSE_IT = 2 };

keypair_t input_state_text [] = 
{ 
	KEYVALUE (input_none), 
	KEYVALUE (input_have_keyboard), 
	KEYVALUE (input_have_mouse_keyboard), 
NULL, 0 };  // NULL termination

static inp_info_t inp;

static void Force_CenterView_f (void) { cl.viewangles[PITCH] = 0; }

static void IN_Info_f (void)
{
	Con_Printf ("IN Info ...\n");
	Con_Printf ("current_state: %s\n", Keypair_String (input_state_text, inp.current_state) );
	Con_Printf ("initialized: %i\n", inp.initialized);
	Con_Printf ("have_mouse: %i\n", inp.have_mouse);
	Con_Printf ("have_keyboard: %i\n", inp.have_keyboard);
	Con_Printf ("mouse_clip_screen_rect: (%i, %i)-(%i, %i) center: %i, %i\n", MRECT_PRINT(inp.mouse_clip_screen_rect) );
	Con_Printf ("mouse_accum_x: %i\n", inp.mouse_accum_x);
	Con_Printf ("mouse_accum_y: %i\n", inp.mouse_accum_y);
	Con_Printf ("mouse_old_button_state: %i\n", inp.mouse_old_button_state);
}

void Input_Think (void)
{
	input_state_t	newstate = (inp.initialized && vid.ActiveApp && !vid.Minimized) ? input_have_keyboard : input_none;

	// newstate upgrades from should have "keyboard" to should have "mouse"
	// If the key_dest is game or we are binding keys in the menu
	if (newstate == input_have_keyboard && key_dest == key_game || (key_dest == key_menu && m_keys_bind_grab) )
		newstate = input_have_mouse_keyboard;
		
	if (newstate != inp.current_state)
	{ // New state.
		char	mouse_action	= ( newstate == input_have_mouse_keyboard && inp.have_mouse == false) ? GET_IT :  (( newstate != input_have_mouse_keyboard && inp.have_mouse == true) ? LOSE_IT : 0);
		char	keyboard_action = ( newstate != input_none && inp.have_keyboard == false) ? GET_IT :  (( newstate == input_none && inp.have_keyboard == true) ? LOSE_IT : 0);

		switch (mouse_action)
		{
		case GET_IT:
			// Sticky keys, Window key disabled
			IN_Local_Keyboard_Disable_Annoying_Keys (true);
			inp.have_keyboard = true;
			break;

		case LOSE_IT:
			// Key ups
			Key_Release_Keys (); 
#pragma message ("Note we still need our key ups when entering the console")
			// Sticky keys, Window key reenabled
			IN_Local_Keyboard_Disable_Annoying_Keys (false); 
			inp.have_keyboard = false;
			break;
		}

		switch (mouse_action)
		{
		case GET_IT:
			// Load window screen coords to mouse_clip_screen_rect
			// And clip the mouse cursor to that area
			IN_Local_Update_Mouse_Clip_Region (&inp.mouse_clip_screen_rect);

			// Hide the mouse cursor and attach it
			IN_Local_Capture_Mouse (true);

			// Center the mouse on-screen
			IN_Local_Mouse_Cursor_SetPos (inp.mouse_clip_screen_rect.center_x, inp.mouse_clip_screen_rect.center_y); //SetCursorPos (window_center_x, window_center_y);
			
			// Clear movement accumulation
			inp.mouse_accum_x = inp.mouse_accum_y = 0
				;
			inp.have_mouse = true;
			break;

		case LOSE_IT:
			// Release mouse buttons so "-alias" get triggered
			Key_Release_Mouse_Buttons ();

			// Unclip the mouse
			IN_Local_Unclip_Mouse_Movement ();

			// Release the mouse and show the cursor
			IN_Local_Capture_Mouse (false);

			// Clear movement accumulation and buttons
			inp.mouse_accum_x = inp.mouse_accum_y = inp.mouse_old_button_state = 0;

			inp.have_mouse = false;
			break;
		}
		inp.current_state = newstate;
	}
	// End of function
}

void Input_Mouse_Button_Event (int mstate)
{
	if (inp.have_mouse /*|| (key_dest == key_menu && m_keys_bind_grab)*/ )
	{  // perform button actions
		int i;
		for (i = 0 ; i < NUM_MOUSE_BUTTONS ; i ++)
		{
			int button_bit = (1 << i);
			qboolean button_pressed  =  (mstate & button_bit) && !(inp.mouse_old_button_state & button_bit);
			qboolean button_released = !(mstate & button_bit) &&  (inp.mouse_old_button_state & button_bit);

			if (button_pressed || button_released)
				Key_Event (K_MOUSE1 + i, button_pressed ? true : false);
		}
		inp.mouse_old_button_state = mstate;
	}
}

void Input_Mouse_Accumulate (void)
{
	int new_mouse_x, new_mouse_y;
#pragma message ("Need a clip region check here")	
	if (inp.have_mouse == false)
		return;

	IN_Local_Mouse_Cursor_GetPos (&new_mouse_x, &new_mouse_y); // GetCursorPos (&current_pos);

	inp.mouse_accum_x += new_mouse_x - inp.mouse_clip_screen_rect.center_x;
	inp.mouse_accum_y += new_mouse_y - inp.mouse_clip_screen_rect.center_y;

	// Re-center the mouse cursor
	IN_Local_Mouse_Cursor_SetPos (inp.mouse_clip_screen_rect.center_x, inp.mouse_clip_screen_rect.center_y);
}

void Input_Mouse_Move (usercmd_t *cmd)
{
	Input_Mouse_Accumulate ();

	if (inp.mouse_accum_x || inp.mouse_accum_y)
	{
		int	mouse_x = inp.mouse_accum_x *= sensitivity.value;
		int mouse_y = inp.mouse_accum_y *= sensitivity.value;
	// add mouse X/Y movement to cmd
		if ( (in_strafe.state & 1) || (lookstrafe.value && (in_mlook.state & 1) ))
			cmd->sidemove += m_side.value * mouse_x;
		else cl.viewangles[YAW] -= m_yaw.value * mouse_x;

		if (in_mlook.state & 1)
			V_StopPitchDrift ();

		if ( (in_mlook.state & 1) && !(in_strafe.state & 1))
		{
			cl.viewangles[PITCH] += m_pitch.value * mouse_y;
			cl.viewangles[PITCH] = CLAMP (cl_minpitch.value, cl.viewangles[PITCH], cl_maxpitch.value);
		}
		else
		{
			if ((in_strafe.state & 1) && cl.noclip_anglehack)
				cmd->upmove -= m_forward.value * mouse_y;
			else cmd->forwardmove -= m_forward.value * mouse_y;
		}
		inp.mouse_accum_x = inp.mouse_accum_y = 0;
	}
}

void Input_Initialized (void)
{
	Cmd_AddCommand ("in_info", IN_Info_f);
	Cmd_AddCommand ("force_centerview", Force_CenterView_f);
	inp.initialized = true;
}

void Input_Shutdown (void)
{
	inp.initialized = false;
	Input_Think (); // Will shut everything off
}
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Input untangled

Post by Baker »

Final answer: Tested, debugged, short, only checks once per frame (like what MH did in DirectQ). All the IN_HideMouse stuff all over the source code is gone.

Nightmares -1.

Code: Select all

#include "quakedef.h"

typedef enum
{
	input_none,
	input_have_keyboard,
	input_have_mouse_keyboard,
} input_state_t;

typedef struct
{
	input_state_t	current_state;
	qboolean		initialized, have_mouse, have_keyboard;

// Internals
	mrect_t			mouse_clip_screen_rect;
	int				mouse_accum_x, mouse_accum_y;
	int				mouse_old_button_state;
} inp_info_t;


#define NUM_MOUSE_BUTTONS 5
#define MRECT_PRINT(_x) _x.left, _x.top, _x.right, _x.bottom, _x.center_x, _x.center_y
enum { GET_IT = 1, LOSE_IT = 2 };

keypair_t input_state_text [] = 
{ 
	KEYVALUE (input_none), 
	KEYVALUE (input_have_keyboard), 
	KEYVALUE (input_have_mouse_keyboard), 
NULL, 0 };  // NULL termination

static inp_info_t inp;

static void Force_CenterView_f (void) { cl.viewangles[PITCH] = 0; }

static void IN_Info_f (void)
{
	Con_Printf ("IN Info ...\n");
	Con_Printf ("current_state: %s\n", Keypair_String (input_state_text, inp.current_state) );
	Con_Printf ("initialized: %i\n", inp.initialized);
	Con_Printf ("have_mouse: %i\n", inp.have_mouse);
	Con_Printf ("have_keyboard: %i\n", inp.have_keyboard);
	Con_Printf ("mouse_clip_screen_rect: (%i, %i)-(%i, %i) center: %i, %i\n", MRECT_PRINT(inp.mouse_clip_screen_rect) );
	Con_Printf ("mouse_accum_x: %i\n", inp.mouse_accum_x);
	Con_Printf ("mouse_accum_y: %i\n", inp.mouse_accum_y);
	Con_Printf ("mouse_old_button_state: %i\n", inp.mouse_old_button_state);
}

void Input_Think (void)
{
	input_state_t	newstate = (inp.initialized && vid.ActiveApp && !vid.Minimized && !vid.in_setmode) ? input_have_keyboard : input_none;

	// newstate upgrades from should have "keyboard" to should have "mouse"
	// If the key_dest is game or we are binding keys in the menu
	if (newstate == input_have_keyboard && (key_dest == key_game || (key_dest == key_menu && m_keys_bind_grab)) )
		newstate = input_have_mouse_keyboard;
		
//	Con_Printf ("current_state: %s (init %i active %i mini %i)\n", Keypair_String (input_state_text, inp.current_state), 
//		inp.initialized, vid.ActiveApp, vid.Minimized);

	if (newstate != inp.current_state)
	{ // New state.
		char	mouse_action	= ( newstate == input_have_mouse_keyboard && inp.have_mouse == false) ? GET_IT :  (( newstate != input_have_mouse_keyboard && inp.have_mouse == true) ? LOSE_IT : 0);
		char	keyboard_action = ( newstate != input_none && inp.have_keyboard == false) ? GET_IT :  (( newstate == input_none && inp.have_keyboard == true) ? LOSE_IT : 0);

//		Con_Printf ("State change\n");
		switch (keyboard_action)
		{
		case GET_IT:
			// Sticky keys, Window key disabled
			IN_Local_Keyboard_Disable_Annoying_Keys (true);
			inp.have_keyboard = true;
			break;

		case LOSE_IT:
			// Key ups
			Key_Release_Keys ();
#pragma message ("Note we still need our key ups when entering the console")
			// Sticky keys, Window key reenabled
			IN_Local_Keyboard_Disable_Annoying_Keys (false); 
			inp.have_keyboard = false;
			break;
		}

		switch (mouse_action)
		{
		case GET_IT:
			// Load window screen coords to mouse_clip_screen_rect
			// And clip the mouse cursor to that area
			IN_Local_Update_Mouse_Clip_Region_Think (&inp.mouse_clip_screen_rect);

			// Hide the mouse cursor and attach it
			IN_Local_Capture_Mouse (true);

			// Center the mouse on-screen
			IN_Local_Mouse_Cursor_SetPos (inp.mouse_clip_screen_rect.center_x, inp.mouse_clip_screen_rect.center_y); //SetCursorPos (window_center_x, window_center_y);
			
			// Clear movement accumulation
			inp.mouse_accum_x = inp.mouse_accum_y = 0
				;
			inp.have_mouse = true;
			break;

		case LOSE_IT:
			// Release mouse buttons so "-alias" get triggered.  Even the keyboard keys.
			// As if we lose the mouse, usually this is from entering the console.
			// The exceptions to this are weird and uninteresting (like changing a mouse cvar via a bind)
			Key_Release_Keys ();

			// Release the mouse and show the cursor.  Also unclips mouse.
			IN_Local_Capture_Mouse (false);

			// Clear movement accumulation and buttons
			inp.mouse_accum_x = inp.mouse_accum_y = inp.mouse_old_button_state = 0;

			inp.have_mouse = false;
			break;
		}
		inp.current_state = newstate;
	}

	if (inp.have_mouse && IN_Local_Update_Mouse_Clip_Region_Think (&inp.mouse_clip_screen_rect) == true)
	{
		// Re-center the mouse cursor and clear mouse accumulation
		IN_Local_Mouse_Cursor_SetPos (inp.mouse_clip_screen_rect.center_x, inp.mouse_clip_screen_rect.center_y);
		inp.mouse_accum_x = inp.mouse_accum_y = 0;
	}

	// End of function
}


void Input_Mouse_Button_Event (int mstate)
{
	if (inp.have_mouse /*|| (key_dest == key_menu && m_keys_bind_grab)*/ )
	{  // perform button actions
		int i;
		for (i = 0 ; i < NUM_MOUSE_BUTTONS ; i ++)
		{
			int button_bit = (1 << i);
			qboolean button_pressed  =  (mstate & button_bit) && !(inp.mouse_old_button_state & button_bit);
			qboolean button_released = !(mstate & button_bit) &&  (inp.mouse_old_button_state & button_bit);

			if (button_pressed || button_released)
				Key_Event (K_MOUSE1 + i, button_pressed ? true : false);
		}
		inp.mouse_old_button_state = mstate;
	}
}

void Input_Mouse_Accumulate (void)
{
	int new_mouse_x, new_mouse_y;
#pragma message ("Need a clip region check here")

	Input_Think ();
	if (inp.have_mouse == false)
		return;

	IN_Local_Mouse_Cursor_GetPos (&new_mouse_x, &new_mouse_y); // GetCursorPos (&current_pos);

	inp.mouse_accum_x += new_mouse_x - inp.mouse_clip_screen_rect.center_x;
	inp.mouse_accum_y += new_mouse_y - inp.mouse_clip_screen_rect.center_y;

	// Re-center the mouse cursor
	IN_Local_Mouse_Cursor_SetPos (inp.mouse_clip_screen_rect.center_x, inp.mouse_clip_screen_rect.center_y);
}

void Input_Mouse_Move (usercmd_t *cmd)
{
	Input_Mouse_Accumulate ();

	if (inp.mouse_accum_x || inp.mouse_accum_y)
	{
		int	mouse_x = inp.mouse_accum_x *= sensitivity.value;
		int mouse_y = inp.mouse_accum_y *= sensitivity.value;
	// add mouse X/Y movement to cmd
		if ( (in_strafe.state & 1) || (lookstrafe.value && (in_mlook.state & 1) ))
			cmd->sidemove += m_side.value * mouse_x;
		else cl.viewangles[YAW] -= m_yaw.value * mouse_x;

		if (in_mlook.state & 1)
			V_StopPitchDrift ();

		if ( (in_mlook.state & 1) && !(in_strafe.state & 1))
		{
			cl.viewangles[PITCH] += m_pitch.value * mouse_y;
			cl.viewangles[PITCH] = CLAMP (cl_minpitch.value, cl.viewangles[PITCH], cl_maxpitch.value);
		}
		else
		{
			if ((in_strafe.state & 1) && cl.noclip_anglehack)
				cmd->upmove -= m_forward.value * mouse_y;
			else cmd->forwardmove -= m_forward.value * mouse_y;
		}
		inp.mouse_accum_x = inp.mouse_accum_y = 0;
	}
}

void Input_Initialize (void)
{
	Cmd_AddCommand ("in_info", IN_Info_f);
	Cmd_AddCommand ("force_centerview", Force_CenterView_f);
	inp.initialized = true;
}

void Input_Shutdown (void)
{
	inp.initialized = false;
	Input_Think (); // Will shut everything off
}
Header look like:

Code: Select all

typedef struct mrect_s
{
	int				left, right, bottom, top;
	int				center_x, center_y;
	int				width, height;
} mrect_t;

void Input_Initialize (void);
void Input_Shutdown (void);
void Input_Think (void);
void Input_Mouse_Move (usercmd_t *cmd);
void Input_Mouse_Accumulate (void);
void Input_Mouse_Button_Event (int mstate);

// Platform
void IN_Local_Capture_Mouse (qboolean bDoCapture);
void IN_Local_Keyboard_Disable_Annoying_Keys (qboolean bDoDisable);
void IN_Local_Mouse_Cursor_SetPos (int x, int y);
void IN_Local_Mouse_Cursor_GetPos (int *x, int *y);
qboolean IN_Local_Update_Mouse_Clip_Region_Think (mrect_t* mouseregion);
in_win.c looks like:

Code: Select all

void IN_Local_Capture_Mouse (qboolean bDoCapture)
{
	static qboolean captured = false;

	if (bDoCapture && !captured)
	{
		ShowCursor (FALSE); // Hides mouse cursor
		SetCapture (mainwindow);	// Captures mouse events
		Con_DPrintf ("Mouse Captured\n");
		captured = true;
	}
	
	if (!bDoCapture && captured)
	{
		ShowCursor (TRUE); // Hides mouse cursor
		ReleaseCapture ();
		ClipCursor (NULL); // Can't hurt
		Con_DPrintf ("Mouse Released\n");
		captured = false;
	}

}


qboolean IN_Local_Update_Mouse_Clip_Region_Think (mrect_t* mouseregion)
{
	mrect_t oldregion = *mouseregion;
	WINDOWINFO windowinfo;
	windowinfo.cbSize = sizeof (WINDOWINFO);
	GetWindowInfo (mainwindow, &windowinfo);	// client_area screen coordinates

	// Fill in top left, bottom, right, center
	mouseregion->left = windowinfo.rcClient.left;
	mouseregion->right = windowinfo.rcClient.right;
	mouseregion->bottom = windowinfo.rcClient.bottom;
	mouseregion->top = windowinfo.rcClient.top;

	if (memcmp (mouseregion, &oldregion, sizeof(mrect_t) ) != 0)
	{  // Changed!	
		mouseregion->width = mouseregion->right - mouseregion->left;
		mouseregion->height = mouseregion->bottom - mouseregion->top;
		mouseregion->center_x = (mouseregion->left + mouseregion->right) / 2;
		mouseregion->center_y = (mouseregion->top + mouseregion->bottom) / 2;
		ClipCursor (&windowinfo.rcClient);
		return true;
	}
	return false;
}

void IN_Local_Mouse_Cursor_SetPos (int x, int y)
{
	SetCursorPos (x, y);
}

void IN_Local_Mouse_Cursor_GetPos (int *x, int *y)
{
	POINT current_pos;
	GetCursorPos (&current_pos);

	*x = current_pos.x;
	*y = current_pos.y;
}


STICKYKEYS StartupStickyKeys = {sizeof (STICKYKEYS), 0};
TOGGLEKEYS StartupToggleKeys = {sizeof (TOGGLEKEYS), 0};
FILTERKEYS StartupFilterKeys = {sizeof (FILTERKEYS), 0};


void AllowAccessibilityShortcutKeys (qboolean bAllowKeys)
{
	static qboolean initialized = false;

	if (!initialized)
	{	// Save the current sticky/toggle/filter key settings so they can be restored them later
		SystemParametersInfo (SPI_GETSTICKYKEYS, sizeof (STICKYKEYS), &StartupStickyKeys, 0);
		SystemParametersInfo (SPI_GETTOGGLEKEYS, sizeof (TOGGLEKEYS), &StartupToggleKeys, 0);
		SystemParametersInfo (SPI_GETFILTERKEYS, sizeof (FILTERKEYS), &StartupFilterKeys, 0);
		Con_DPrintf ("Accessibility key startup settings saved\n");
		initialized = true;
	}

	if (bAllowKeys)
	{
		// Restore StickyKeys/etc to original state
		// (note that this function is called "allow", not "enable"; if they were previously
		// disabled it will put them back that way too, it doesn't force them to be enabled.)
		SystemParametersInfo (SPI_SETSTICKYKEYS, sizeof (STICKYKEYS), &StartupStickyKeys, 0);
		SystemParametersInfo (SPI_SETTOGGLEKEYS, sizeof (TOGGLEKEYS), &StartupToggleKeys, 0);
		SystemParametersInfo (SPI_SETFILTERKEYS, sizeof (FILTERKEYS), &StartupFilterKeys, 0);

		Con_DPrintf ("Accessibility keys enabled\n");
	}
	else
	{
		// Disable StickyKeys/etc shortcuts but if the accessibility feature is on,
		// then leave the settings alone as its probably being usefully used
		STICKYKEYS skOff = StartupStickyKeys;
		TOGGLEKEYS tkOff = StartupToggleKeys;
		FILTERKEYS fkOff = StartupFilterKeys;

		if ((skOff.dwFlags & SKF_STICKYKEYSON) == 0)
		{
			// Disable the hotkey and the confirmation
			skOff.dwFlags &= ~SKF_HOTKEYACTIVE;
			skOff.dwFlags &= ~SKF_CONFIRMHOTKEY;

			SystemParametersInfo (SPI_SETSTICKYKEYS, sizeof (STICKYKEYS), &skOff, 0);
		}

		if ((tkOff.dwFlags & TKF_TOGGLEKEYSON) == 0)
		{
			// Disable the hotkey and the confirmation
			tkOff.dwFlags &= ~TKF_HOTKEYACTIVE;
			tkOff.dwFlags &= ~TKF_CONFIRMHOTKEY;

			SystemParametersInfo (SPI_SETTOGGLEKEYS, sizeof (TOGGLEKEYS), &tkOff, 0);
		}

		if ((fkOff.dwFlags & FKF_FILTERKEYSON) == 0)
		{
			// Disable the hotkey and the confirmation
			fkOff.dwFlags &= ~FKF_HOTKEYACTIVE;
			fkOff.dwFlags &= ~FKF_CONFIRMHOTKEY;

			SystemParametersInfo (SPI_SETFILTERKEYS, sizeof (FILTERKEYS), &fkOff, 0);
		}

		Con_DPrintf ("Accessibility keys disabled\n");
	}
}



LRESULT CALLBACK LLWinKeyHook(int Code, WPARAM wParam, LPARAM lParam)
{
	PKBDLLHOOKSTRUCT p;
	p = (PKBDLLHOOKSTRUCT) lParam;

	if (vid.ActiveApp)
	{
		switch(p->vkCode)
		{
		case VK_LWIN:	// Left Windows Key
		case VK_RWIN:	// Right Windows key
		case VK_APPS: 	// Context Menu key

			return 1; // Ignore these keys
		}
	}

	return CallNextHookEx(NULL, Code, wParam, lParam);
}



void AllowWindowsShortcutKeys (qboolean bAllowKeys)
{
	static qboolean WinKeyHook_isActive = false;
	static HHOOK WinKeyHook;

	if (!bAllowKeys)
	{
		// Disable if not already disabled
		if (!WinKeyHook_isActive)
		{
			if (!(WinKeyHook = SetWindowsHookEx(13, LLWinKeyHook, global_hInstance, 0)))
			{
				Con_Printf("Failed to install winkey hook.\n");
				Con_Printf("Microsoft Windows NT 4.0, 2000 or XP is required.\n");
				return;
			}

			WinKeyHook_isActive = true;
			Con_DPrintf ("Windows and context menu key disabled\n");
		}
	}

	if (bAllowKeys)
	{	// Keys allowed .. stop hook
		if (WinKeyHook_isActive)
		{
			UnhookWindowsHookEx(WinKeyHook);
			WinKeyHook_isActive = false;
			Con_DPrintf ("Windows and context menu key enabled\n");
		}
	}
}

void IN_Local_Keyboard_Disable_Annoying_Keys (qboolean bDoDisable)
{
	if (bDoDisable)
	{
		AllowAccessibilityShortcutKeys (false);
		AllowWindowsShortcutKeys (false);
	}
	else
	{
		AllowAccessibilityShortcutKeys (true);
		AllowWindowsShortcutKeys (true);
	}
}

/*
=======
MapKey

Map from windows to quake keynums
=======
*/
int IN_Local_MapKey (int windowskey)
{
	static byte scantokey[128] =
	{
		0 ,	27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', K_BACKSPACE, 
		9, 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', 13 , 
		K_CTRL, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 
		K_SHIFT,'\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', K_SHIFT,'*',K_ALT,' ', 0 , 
		K_F1, K_F2, K_F3, K_F4, K_F5,K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, 0, 
		K_HOME, K_UPARROW,K_PGUP,'-',K_LEFTARROW,'5',K_RIGHTARROW,'+',K_END,
		K_DOWNARROW,K_PGDN,K_INS,K_DEL,0,0, 0, K_F11, K_F12,0 , 0 , 0 , 0 , 0 , 0 , 0,
	};
	int key = (windowskey >> 16) & 255;

	if (key > 127)
		return 0;

	key = scantokey[key];
/*
	switch (key) 
	{
		case K_KP_STAR:		return '*';
		case K_KP_MINUS:	return '-';
		case K_KP_5:		return '5';
		case K_KP_PLUS:		return '+';
	}
*/
	return key;
}

qboolean IN_ReadInputMessages (HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
	int button_bits = 0;

	switch (Msg)
	{
	case WM_MOUSEWHEEL:
		if ((short) HIWORD(wParam) > 0) 
		{
			Key_Event(K_MWHEELUP, true);
			Key_Event(K_MWHEELUP, false);
		}
		else 
		{
			Key_Event(K_MWHEELDOWN, true);
			Key_Event(K_MWHEELDOWN, false);
		}
		return true;

	case WM_LBUTTONDOWN:
	case WM_LBUTTONUP:
	case WM_RBUTTONDOWN:
	case WM_RBUTTONUP:
	case WM_MBUTTONDOWN:
	case WM_MBUTTONUP:
	case WM_MOUSEMOVE:
		if (wParam & MK_LBUTTON)	button_bits |= 1;
		if (wParam & MK_RBUTTON)	button_bits |= 2;
		if (wParam & MK_MBUTTON)	button_bits |= 4;
		if (wParam & MK_XBUTTON1)	button_bits |= 8;
		if (wParam & MK_XBUTTON2)	button_bits |= 16;
		Input_Mouse_Button_Event (button_bits);

		return true;

	case WM_KEYDOWN:
	case WM_SYSKEYDOWN:
		Key_Event (IN_Local_MapKey (lParam), true);
		return true;

	case WM_KEYUP:
	case WM_SYSKEYUP:
		Key_Event (IN_Local_MapKey(lParam), false);
		return true;

	default:
		return false;
	}
}
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: Input untangled

Post by Spike »

dude, that's disgusting.
shift+2 should be a " char, not an @
fix it. fix it now.
before I vomit.

more seriously though, code that ignores your keymap is really insanely annoying. especially if you're an azerty user, or indeed any other european or other non-american.
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Input untangled

Post by Baker »

Spike wrote:dude, that's disgusting.
shift+2 should be a " char, not an @
fix it. fix it now.
before I vomit.

more seriously though, code that ignores your keymap is really insanely annoying. especially if you're an azerty user, or indeed any other european or other non-american.
I agree with that. Since it is my understanding that Quakespasm does this well, I may adopt the Quakespasm keyboard input code. I'm trying to solve things one step at a time. :D

p.s. I get the joke.
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: Input untangled

Post by Spike »

no joke, only melodrama.
Post Reply