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.
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 (¤t_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
}
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 (¤t_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;
}
}