win32: read keyboard modifiers via GetKeyboardState

Five copies of the same block queried GetKeyState once per modifier and
tested the result with 0x80 (down) and 0x81 (down or toggled).

Neither mask is documented. GetKeyState returns a SHORT in which only
bit 15 is contractual; 0x80 happens to work because user32 builds the
down value as 0xFF80, and on the slow path the encoding comes straight
out of win32k and is not guaranteed at all. GetKeyboardState fills the
documented 256-byte form of the same per-thread table, 0x80 down and
0x01 toggled, which is stable across every Windows and under Wine.

0x81 also conflated the two states: holding CapsLock while the lock was
off reported RETROKMOD_CAPSLOCK. The toggle keys now test 0x01 only.

The single call is cheaper as well. user32's GetKeyState cache covers
virtual-key codes below 0x20 and nothing else, so VK_NUMLOCK, VK_SCROLL,
VK_LWIN and VK_RWIN each took an unconditional kernel transition on
every query - four syscalls per keyboard event, in every copy.
GetKeyboardState is a bare thunk that returns all 256 keys at once.

Consolidated into win32_get_keyboard_mods() in win32_common.c and used
from wnd_proc_common, wnd_proc_common_internal,
wnd_proc_common_dinput_internal, winraw_callback and dinput_poll.
dinput_get_active_keyboard_mods() is removed as a duplicate.

The GetAsyncKeyState calls in dinput_poll are left alone: they read the
global physical state, which GetKeyboardState does not expose.
This commit is contained in:
LibretroAdmin authored and libretroadmin committed 2026-09-01 04:49:50 +02:00
1 parent 7877be2951
commit 8d6bfc4b29
4 files changed
+49 -80

No files matched your search

+40 -42
View File
@@ -504,6 +504,43 @@ static void win32_get_av_info_geometry(unsigned *width, unsigned *height)
*height = video_st->av_info.geometry.base_height;
}
/* GetKeyState and GetKeyboardState read the same per-thread
* synchronous key table, but only GetKeyboardState exposes it in its
* documented form: a 256-byte array with 0x80 for down and 0x01 for
* toggled. GetKeyState returns those bits repacked into a SHORT in
* which only bit 15 is contractual, so the 0x80 and 0x81 masks the
* callers below used to apply were reading undocumented territory.
*
* One call is also cheaper. user32's GetKeyState cache only covers
* virtual-key codes below 0x20, so VK_NUMLOCK, VK_SCROLL, VK_LWIN and
* VK_RWIN each took an unconditional kernel transition on every query;
* GetKeyboardState is a single thunk for all 256 keys. */
uint16_t win32_get_keyboard_mods(void)
{
BYTE ks[256];
uint16_t mod = 0;
if (!GetKeyboardState(ks))
return 0;
if (ks[VK_SHIFT] & 0x80)
mod |= RETROKMOD_SHIFT;
if (ks[VK_CONTROL] & 0x80)
mod |= RETROKMOD_CTRL;
if (ks[VK_MENU] & 0x80)
mod |= RETROKMOD_ALT;
if (ks[VK_CAPITAL] & 0x01)
mod |= RETROKMOD_CAPSLOCK;
if (ks[VK_SCROLL] & 0x01)
mod |= RETROKMOD_SCROLLOCK;
if (ks[VK_NUMLOCK] & 0x01)
mod |= RETROKMOD_NUMLOCK;
if ((ks[VK_LWIN] | ks[VK_RWIN]) & 0x80)
mod |= RETROKMOD_META;
return mod;
}
static LRESULT CALLBACK wnd_proc_common(
bool *quit, HWND hwnd, UINT message,
WPARAM wparam, LPARAM lparam)
@@ -537,20 +574,7 @@ static LRESULT CALLBACK wnd_proc_common(
{
uint16_t mod = 0;
if (GetKeyState(VK_SHIFT) & 0x80)
mod |= RETROKMOD_SHIFT;
if (GetKeyState(VK_CONTROL) & 0x80)
mod |= RETROKMOD_CTRL;
if (GetKeyState(VK_MENU) & 0x80)
mod |= RETROKMOD_ALT;
if (GetKeyState(VK_CAPITAL) & 0x81)
mod |= RETROKMOD_CAPSLOCK;
if (GetKeyState(VK_SCROLL) & 0x81)
mod |= RETROKMOD_SCROLLOCK;
if (GetKeyState(VK_NUMLOCK) & 0x81)
mod |= RETROKMOD_NUMLOCK;
if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & 0x80)
mod |= RETROKMOD_META;
mod = win32_get_keyboard_mods();
/* Seems to be hard to synchronize
* WM_CHAR and WM_KEYDOWN properly.
@@ -749,20 +773,7 @@ static LRESULT CALLBACK wnd_proc_common_internal(HWND hwnd,
keycode = input_keymaps_translate_keysym_to_rk(keysym);
if (GetKeyState(VK_SHIFT) & 0x80)
mod |= RETROKMOD_SHIFT;
if (GetKeyState(VK_CONTROL) & 0x80)
mod |= RETROKMOD_CTRL;
if (GetKeyState(VK_MENU) & 0x80)
mod |= RETROKMOD_ALT;
if (GetKeyState(VK_CAPITAL) & 0x81)
mod |= RETROKMOD_CAPSLOCK;
if (GetKeyState(VK_SCROLL) & 0x81)
mod |= RETROKMOD_SCROLLOCK;
if (GetKeyState(VK_NUMLOCK) & 0x81)
mod |= RETROKMOD_NUMLOCK;
if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & 0x80)
mod |= RETROKMOD_META;
mod = win32_get_keyboard_mods();
input_keyboard_event(keydown, keycode,
0, mod, RETRO_DEVICE_KEYBOARD);
@@ -1050,20 +1061,7 @@ static LRESULT CALLBACK wnd_proc_common_dinput_internal(HWND hwnd,
keycode = input_keymaps_translate_keysym_to_rk(keysym);
if (GetKeyState(VK_SHIFT) & 0x80)
mod |= RETROKMOD_SHIFT;
if (GetKeyState(VK_CONTROL) & 0x80)
mod |= RETROKMOD_CTRL;
if (GetKeyState(VK_MENU) & 0x80)
mod |= RETROKMOD_ALT;
if (GetKeyState(VK_CAPITAL) & 0x81)
mod |= RETROKMOD_CAPSLOCK;
if (GetKeyState(VK_SCROLL) & 0x81)
mod |= RETROKMOD_SCROLLOCK;
if (GetKeyState(VK_NUMLOCK) & 0x81)
mod |= RETROKMOD_NUMLOCK;
if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & 0x80)
mod |= RETROKMOD_META;
mod = win32_get_keyboard_mods();
input_keyboard_event(keydown, keycode,
0, mod, RETRO_DEVICE_KEYBOARD);
+4
View File
@@ -134,6 +134,10 @@ void win32_destroy_window(void);
uint8_t win32_get_flags(void);
/* Returns the RETROKMOD_* mask for the calling thread's
* synchronous keyboard state. */
uint16_t win32_get_keyboard_mods(void);
#if defined(HAVE_D3D8) || defined(HAVE_D3D9) || defined (HAVE_D3D10) || defined (HAVE_D3D11) || defined (HAVE_D3D12)
LRESULT CALLBACK wnd_proc_d3d_dinput(HWND hwnd, UINT message,
WPARAM wparam, LPARAM lparam);
+4 -24
View File
@@ -201,26 +201,6 @@ static void *dinput_init(const char *joypad_driver)
return di;
}
static uint16_t dinput_get_active_keyboard_mods()
{
uint16_t mod = 0;
if (GetKeyState(VK_SHIFT) & 0x80)
mod |= RETROKMOD_SHIFT;
if (GetKeyState(VK_CONTROL) & 0x80)
mod |= RETROKMOD_CTRL;
if (GetKeyState(VK_MENU) & 0x80)
mod |= RETROKMOD_ALT;
if (GetKeyState(VK_CAPITAL) & 0x81)
mod |= RETROKMOD_CAPSLOCK;
if (GetKeyState(VK_SCROLL) & 0x81)
mod |= RETROKMOD_SCROLLOCK;
if (GetKeyState(VK_NUMLOCK) & 0x81)
mod |= RETROKMOD_NUMLOCK;
if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & 0x80)
mod |= RETROKMOD_META;
return mod;
}
static void dinput_poll(void *data)
{
struct dinput_input *di = (struct dinput_input*)data;
@@ -262,13 +242,13 @@ static void dinput_poll(void *data)
if ((di->flags & DINP_FLAG_SHIFT_L) && !(GetAsyncKeyState(VK_LSHIFT) >> 1))
{
input_keyboard_event(false, RETROK_LSHIFT, 0,
dinput_get_active_keyboard_mods(), RETRO_DEVICE_KEYBOARD);
win32_get_keyboard_mods(), RETRO_DEVICE_KEYBOARD);
di->flags &= ~DINP_FLAG_SHIFT_L;
}
if ((di->flags & DINP_FLAG_SHIFT_R) && !(GetAsyncKeyState(VK_RSHIFT) >> 1))
{
input_keyboard_event(false, RETROK_RSHIFT, 0,
dinput_get_active_keyboard_mods(), RETRO_DEVICE_KEYBOARD);
win32_get_keyboard_mods(), RETRO_DEVICE_KEYBOARD);
di->flags &= ~DINP_FLAG_SHIFT_R;
}
@@ -277,13 +257,13 @@ static void dinput_poll(void *data)
if ((di->flags & DINP_FLAG_ALT_L) && !(GetAsyncKeyState(VK_LMENU) >> 1))
{
input_keyboard_event(false, RETROK_LALT, 0,
dinput_get_active_keyboard_mods(), RETRO_DEVICE_KEYBOARD);
win32_get_keyboard_mods(), RETRO_DEVICE_KEYBOARD);
di->flags &= ~DINP_FLAG_ALT_L;
}
if ((di->flags & DINP_FLAG_ALT_R) && !(GetAsyncKeyState(VK_RMENU) >> 1))
{
input_keyboard_event(false, RETROK_RALT, 0,
dinput_get_active_keyboard_mods(), RETRO_DEVICE_KEYBOARD);
win32_get_keyboard_mods(), RETRO_DEVICE_KEYBOARD);
di->flags &= ~DINP_FLAG_ALT_R;
}
}
+1 -14
View File
@@ -658,20 +658,7 @@ static LRESULT CALLBACK winraw_callback(
return 0;
}
if (GetKeyState(VK_SHIFT) & 0x80)
mod |= RETROKMOD_SHIFT;
if (GetKeyState(VK_CONTROL) & 0x80)
mod |= RETROKMOD_CTRL;
if (GetKeyState(VK_MENU) & 0x80)
mod |= RETROKMOD_ALT;
if (GetKeyState(VK_CAPITAL) & 0x81)
mod |= RETROKMOD_CAPSLOCK;
if (GetKeyState(VK_SCROLL) & 0x81)
mod |= RETROKMOD_SCROLLOCK;
if (GetKeyState(VK_NUMLOCK) & 0x81)
mod |= RETROKMOD_NUMLOCK;
if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & 0x80)
mod |= RETROKMOD_META;
mod = win32_get_keyboard_mods();
wr->kb_keys[mcode] = down;
input_keyboard_event(down,