diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c index 2557d8628..acb52c3ce 100644 --- a/src/video/windows/SDL_windowsevents.c +++ b/src/video/windows/SDL_windowsevents.c @@ -428,6 +428,47 @@ static SDL_MOUSE_EVENT_SOURCE GetMouseMessageSource() return SDL_MOUSE_EVENT_SOURCE_MOUSE; } +LRESULT CALLBACK +WIN_KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam) +{ + if (nCode < 0 || nCode != HC_ACTION) { + return CallNextHookEx(NULL, nCode, wParam, lParam); + } + + KBDLLHOOKSTRUCT* hookData = (KBDLLHOOKSTRUCT*)lParam; + SDL_Scancode scanCode; + switch (hookData->vkCode) { + case VK_LWIN: + scanCode = SDL_SCANCODE_LGUI; + break; + case VK_RWIN: + scanCode = SDL_SCANCODE_RGUI; + break; + case VK_LMENU: + scanCode = SDL_SCANCODE_LALT; + break; + case VK_RMENU: + scanCode = SDL_SCANCODE_RALT; + break; + case VK_LCONTROL: + scanCode = SDL_SCANCODE_LCTRL; + break; + case VK_RCONTROL: + scanCode = SDL_SCANCODE_RCTRL; + break; + default: + return CallNextHookEx(NULL, nCode, wParam, lParam); + } + + if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) { + SDL_SendKeyboardKey(SDL_PRESSED, scanCode); + } else { + SDL_SendKeyboardKey(SDL_RELEASED, scanCode); + } + + return 1; +} + LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { diff --git a/src/video/windows/SDL_windowsevents.h b/src/video/windows/SDL_windowsevents.h index 9407f72bc..fcd716dcf 100644 --- a/src/video/windows/SDL_windowsevents.h +++ b/src/video/windows/SDL_windowsevents.h @@ -27,6 +27,7 @@ extern LPTSTR SDL_Appname; extern Uint32 SDL_Appstyle; extern HINSTANCE SDL_Instance; +extern LRESULT CALLBACK WIN_KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam); extern LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); extern void WIN_PumpEvents(_THIS); diff --git a/src/video/windows/SDL_windowswindow.c b/src/video/windows/SDL_windowswindow.c index 2a54eb2ab..cae93f769 100644 --- a/src/video/windows/SDL_windowswindow.c +++ b/src/video/windows/SDL_windowswindow.c @@ -738,11 +738,58 @@ WIN_GetWindowGammaRamp(_THIS, SDL_Window * window, Uint16 * ramp) return succeeded ? 0 : -1; } +static void WIN_GrabKeyboard(SDL_Window *window) +{ + SDL_WindowData *data = (SDL_WindowData *)window->driverdata; + HMODULE module; + + if (data->keyboard_hook) { + return; + } + + /* SetWindowsHookEx() needs to know which module contains the hook we + want to install. This is complicated by the fact that SDL can be + linked statically or dynamically. Fortunately XP and later provide + this nice API that will go through the loaded modules and find the + one containing our code. + */ + if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, + (LPTSTR)WIN_KeyboardHookProc, + &module)) { + return; + } + + /* To grab the keyboard, we have to install a low-level keyboard hook to + intercept keys that would normally be captured by the OS. Intercepting + all key events on the system is rather invasive, but it's what Microsoft + actually documents that you do to capture these. + */ + data->keyboard_hook = SetWindowsHookEx(WH_KEYBOARD_LL, WIN_KeyboardHookProc, module, 0); +} + +void WIN_UngrabKeyboard(SDL_Window *window) +{ + SDL_WindowData *data = (SDL_WindowData *)window->driverdata; + + if (data->keyboard_hook) { + UnhookWindowsHookEx(data->keyboard_hook); + data->keyboard_hook = NULL; + } +} + void WIN_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed) { WIN_UpdateClipCursor(window); + if (grabbed) { + if (SDL_GetHintBoolean(SDL_HINT_GRAB_KEYBOARD, SDL_FALSE)) { + WIN_GrabKeyboard(window); + } + } else { + WIN_UngrabKeyboard(window); + } + if (window->flags & SDL_WINDOW_FULLSCREEN) { UINT flags = SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOSIZE; @@ -759,6 +806,9 @@ WIN_DestroyWindow(_THIS, SDL_Window * window) SDL_WindowData *data = (SDL_WindowData *) window->driverdata; if (data) { + if (data->keyboard_hook) { + UnhookWindowsHookEx(data->keyboard_hook); + } ReleaseDC(data->hwnd, data->hdc); RemoveProp(data->hwnd, TEXT("SDL_WindowData")); if (data->created) { diff --git a/src/video/windows/SDL_windowswindow.h b/src/video/windows/SDL_windowswindow.h index 69ae4a7f3..cbadc858a 100644 --- a/src/video/windows/SDL_windowswindow.h +++ b/src/video/windows/SDL_windowswindow.h @@ -37,6 +37,7 @@ typedef struct HINSTANCE hinstance; HBITMAP hbm; WNDPROC wndproc; + HHOOK keyboard_hook; SDL_bool created; WPARAM mouse_button_flags; LPARAM last_pointer_update;