mirror of
https://github.com/moonlight-stream/moonlight-qt.git
synced 2026-06-17 22:23:31 +00:00
Use SDL's keyboard grab support now that it's been upstreamed
This commit is contained in:
@@ -68,6 +68,12 @@ SystemProperties::SystemProperties()
|
|||||||
hasDiscordIntegration = false;
|
hasDiscordIntegration = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef Q_OS_DARWIN
|
||||||
|
supportsWindowedSystemKeyCapture = false;
|
||||||
|
#else
|
||||||
|
supportsWindowedSystemKeyCapture = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
unmappedGamepads = SdlInputHandler::getUnmappedGamepads();
|
unmappedGamepads = SdlInputHandler::getUnmappedGamepads();
|
||||||
|
|
||||||
// Populate data that requires talking to SDL. We do it all in one shot
|
// Populate data that requires talking to SDL. We do it all in one shot
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ public:
|
|||||||
Q_PROPERTY(bool hasWindowManager MEMBER hasWindowManager CONSTANT)
|
Q_PROPERTY(bool hasWindowManager MEMBER hasWindowManager CONSTANT)
|
||||||
Q_PROPERTY(bool hasBrowser MEMBER hasBrowser CONSTANT)
|
Q_PROPERTY(bool hasBrowser MEMBER hasBrowser CONSTANT)
|
||||||
Q_PROPERTY(bool hasDiscordIntegration MEMBER hasDiscordIntegration CONSTANT)
|
Q_PROPERTY(bool hasDiscordIntegration MEMBER hasDiscordIntegration CONSTANT)
|
||||||
|
Q_PROPERTY(bool supportsWindowedSystemKeyCapture MEMBER supportsWindowedSystemKeyCapture CONSTANT)
|
||||||
Q_PROPERTY(QString unmappedGamepads MEMBER unmappedGamepads NOTIFY unmappedGamepadsChanged)
|
Q_PROPERTY(QString unmappedGamepads MEMBER unmappedGamepads NOTIFY unmappedGamepadsChanged)
|
||||||
Q_PROPERTY(int maximumStreamingFrameRate MEMBER maximumStreamingFrameRate CONSTANT)
|
Q_PROPERTY(int maximumStreamingFrameRate MEMBER maximumStreamingFrameRate CONSTANT)
|
||||||
Q_PROPERTY(QSize maximumResolution MEMBER maximumResolution CONSTANT)
|
Q_PROPERTY(QSize maximumResolution MEMBER maximumResolution CONSTANT)
|
||||||
@@ -43,6 +44,7 @@ private:
|
|||||||
bool hasWindowManager;
|
bool hasWindowManager;
|
||||||
bool hasBrowser;
|
bool hasBrowser;
|
||||||
bool hasDiscordIntegration;
|
bool hasDiscordIntegration;
|
||||||
|
bool supportsWindowedSystemKeyCapture;
|
||||||
QString unmappedGamepads;
|
QString unmappedGamepads;
|
||||||
int maximumStreamingFrameRate;
|
int maximumStreamingFrameRate;
|
||||||
QSize maximumResolution;
|
QSize maximumResolution;
|
||||||
|
|||||||
@@ -909,7 +909,8 @@ Flickable {
|
|||||||
id: captureSysKeysCheck
|
id: captureSysKeysCheck
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
width: parent.width
|
width: parent.width
|
||||||
text: qsTr("Capture system keyboard shortcuts while streaming in fullscreen")
|
text: qsTr("Capture system keyboard shortcuts") + (SystemProperties.supportsWindowedSystemKeyCapture ?
|
||||||
|
"" : qsTr(" while streaming in fullscreen mode"))
|
||||||
font.pointSize: 12
|
font.pointSize: 12
|
||||||
enabled: SystemProperties.hasWindowManager
|
enabled: SystemProperties.hasWindowManager
|
||||||
checked: StreamingPreferences.captureSysKeys && SystemProperties.hasWindowManager
|
checked: StreamingPreferences.captureSysKeys && SystemProperties.hasWindowManager
|
||||||
@@ -920,7 +921,7 @@ Flickable {
|
|||||||
ToolTip.delay: 1000
|
ToolTip.delay: 1000
|
||||||
ToolTip.timeout: 10000
|
ToolTip.timeout: 10000
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
ToolTip.text: qsTr("This enables the capture of system-wide keyboard shortcuts like Alt+Tab that would normally be handled by the client OS while streaming in fullscreen.") + "\n\n" +
|
ToolTip.text: qsTr("This enables the capture of system-wide keyboard shortcuts like Alt+Tab that would normally be handled by the client OS while streaming.") + "\n\n" +
|
||||||
qsTr("NOTE: Certain keyboard shortcuts like Ctrl+Alt+Del on Windows cannot be intercepted by any application, including Moonlight.")
|
qsTr("NOTE: Certain keyboard shortcuts like Ctrl+Alt+Del on Windows cannot be intercepted by any application, including Moonlight.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+36
-189
@@ -11,10 +11,6 @@
|
|||||||
|
|
||||||
#define MOUSE_POLLING_INTERVAL 5
|
#define MOUSE_POLLING_INTERVAL 5
|
||||||
|
|
||||||
#ifdef Q_OS_WIN32
|
|
||||||
HHOOK g_KeyboardHook;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs, NvComputer*, int streamWidth, int streamHeight)
|
SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs, NvComputer*, int streamWidth, int streamHeight)
|
||||||
: m_MultiController(prefs.multiController),
|
: m_MultiController(prefs.multiController),
|
||||||
m_GamepadMouse(prefs.gamepadMouse),
|
m_GamepadMouse(prefs.gamepadMouse),
|
||||||
@@ -52,10 +48,18 @@ SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs, NvComputer*, int s
|
|||||||
prefs.absoluteMouseMode ? "1" : "0",
|
prefs.absoluteMouseMode ? "1" : "0",
|
||||||
SDL_HINT_OVERRIDE);
|
SDL_HINT_OVERRIDE);
|
||||||
|
|
||||||
// If we're grabbing system keys, enable the X11 keyboard grab in SDL
|
#if !SDL_VERSION_ATLEAST(2, 0, 15)
|
||||||
|
// For older versions of SDL (2.0.14 and earlier), use SDL_HINT_GRAB_KEYBOARD
|
||||||
SDL_SetHintWithPriority(SDL_HINT_GRAB_KEYBOARD,
|
SDL_SetHintWithPriority(SDL_HINT_GRAB_KEYBOARD,
|
||||||
m_CaptureSystemKeysEnabled ? "1" : "0",
|
m_CaptureSystemKeysEnabled ? "1" : "0",
|
||||||
SDL_HINT_OVERRIDE);
|
SDL_HINT_OVERRIDE);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Opt-out of SDL's built-in Alt+Tab handling while keyboard grab is enabled
|
||||||
|
SDL_SetHint("SDL_ALLOW_ALT_TAB_WHILE_GRABBED", "0");
|
||||||
|
|
||||||
|
// Don't close the window on Alt+F4 when keyboard grab is enabled
|
||||||
|
SDL_SetHint(SDL_HINT_WINDOWS_NO_CLOSE_ON_ALT_F4, m_CaptureSystemKeysEnabled ? "1" : "0");
|
||||||
|
|
||||||
// Allow clicks to pass through to us when focusing the window. If we're in
|
// Allow clicks to pass through to us when focusing the window. If we're in
|
||||||
// absolute mouse mode, this will avoid the user having to click twice to
|
// absolute mouse mode, this will avoid the user having to click twice to
|
||||||
@@ -188,38 +192,10 @@ SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs, NvComputer*, int s
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_MouseMoveTimer = SDL_AddTimer(pollingInterval, SdlInputHandler::mouseMoveTimerCallback, this);
|
m_MouseMoveTimer = SDL_AddTimer(pollingInterval, SdlInputHandler::mouseMoveTimerCallback, this);
|
||||||
|
|
||||||
#ifdef Q_OS_WIN32
|
|
||||||
if (m_CaptureSystemKeysEnabled) {
|
|
||||||
// If system key capture is enabled, install the window hook required to intercept
|
|
||||||
// these key presses and block them from the OS itself.
|
|
||||||
g_KeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, SdlInputHandler::keyboardHookProc, GetModuleHandle(NULL), 0);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SdlInputHandler::~SdlInputHandler()
|
SdlInputHandler::~SdlInputHandler()
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_WIN32
|
|
||||||
if (g_KeyboardHook != nullptr) {
|
|
||||||
UnhookWindowsHookEx(g_KeyboardHook);
|
|
||||||
g_KeyboardHook = nullptr;
|
|
||||||
|
|
||||||
// If we're terminating because of the user pressing the quit combo,
|
|
||||||
// we won't have a chance to inform SDL as the modifier keys raise.
|
|
||||||
// This will leave stale modifier flags in the SDL_Keyboard global
|
|
||||||
// inside SDL which will show up on our next key event if the user
|
|
||||||
// streams again. Avoid this by explicitly zeroing mod state when
|
|
||||||
// ending the stream.
|
|
||||||
//
|
|
||||||
// This is only needed for the case where we're hooking the keyboard
|
|
||||||
// because we're generating synthetic SDL_KEYDOWN/SDL_KEYUP events
|
|
||||||
// which don't properly maintain SDL's internal keyboard state,
|
|
||||||
// so we're forced to invoke SDL_SetModState() ourselves to set mods.
|
|
||||||
SDL_SetModState(KMOD_NONE);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (int i = 0; i < MAX_GAMEPADS; i++) {
|
for (int i = 0; i < MAX_GAMEPADS; i++) {
|
||||||
if (m_GamepadState[i].mouseEmulationTimer != 0) {
|
if (m_GamepadState[i].mouseEmulationTimer != 0) {
|
||||||
Session::get()->notifyMouseEmulationMode(false);
|
Session::get()->notifyMouseEmulationMode(false);
|
||||||
@@ -273,157 +249,6 @@ void SdlInputHandler::setWindow(SDL_Window *window)
|
|||||||
m_Window = window;
|
m_Window = window;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef Q_OS_WIN32
|
|
||||||
LRESULT CALLBACK SdlInputHandler::keyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam)
|
|
||||||
{
|
|
||||||
if (nCode < 0 || nCode != HC_ACTION) {
|
|
||||||
return CallNextHookEx(g_KeyboardHook, nCode, wParam, lParam);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool keyCaptureActive;
|
|
||||||
SDL_Event event = {};
|
|
||||||
KBDLLHOOKSTRUCT* hookData = (KBDLLHOOKSTRUCT*)lParam;
|
|
||||||
switch (hookData->vkCode) {
|
|
||||||
case VK_LWIN:
|
|
||||||
event.key.keysym.sym = SDLK_LGUI;
|
|
||||||
event.key.keysym.scancode = SDL_SCANCODE_LGUI;
|
|
||||||
break;
|
|
||||||
case VK_RWIN:
|
|
||||||
event.key.keysym.sym = SDLK_RGUI;
|
|
||||||
event.key.keysym.scancode = SDL_SCANCODE_RGUI;
|
|
||||||
break;
|
|
||||||
case VK_LMENU:
|
|
||||||
event.key.keysym.sym = SDLK_LALT;
|
|
||||||
event.key.keysym.scancode = SDL_SCANCODE_LALT;
|
|
||||||
break;
|
|
||||||
case VK_RMENU:
|
|
||||||
event.key.keysym.sym = SDLK_RALT;
|
|
||||||
event.key.keysym.scancode = SDL_SCANCODE_RALT;
|
|
||||||
break;
|
|
||||||
case VK_LCONTROL:
|
|
||||||
event.key.keysym.sym = SDLK_LCTRL;
|
|
||||||
event.key.keysym.scancode = SDL_SCANCODE_LCTRL;
|
|
||||||
break;
|
|
||||||
case VK_RCONTROL:
|
|
||||||
event.key.keysym.sym = SDLK_RCTRL;
|
|
||||||
event.key.keysym.scancode = SDL_SCANCODE_RCTRL;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// Bail quickly if it's not a key we care about
|
|
||||||
goto NextHook;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure we're in a state where we actually want to steal this event (and it is safe to do so)
|
|
||||||
Session* session = Session::get();
|
|
||||||
if (session == nullptr || session->m_InputHandler == nullptr) {
|
|
||||||
goto NextHook;
|
|
||||||
}
|
|
||||||
|
|
||||||
keyCaptureActive = session->m_InputHandler->isSystemKeyCaptureActive();
|
|
||||||
|
|
||||||
// If this is a key we're going to intercept, create the synthetic SDL event.
|
|
||||||
// This is necessary because we are also hiding this key event from ourselves too.
|
|
||||||
if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) {
|
|
||||||
event.type = SDL_KEYDOWN;
|
|
||||||
event.key.state = SDL_PRESSED;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
event.type = SDL_KEYUP;
|
|
||||||
event.key.state = SDL_RELEASED;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Drop the event if it's a key down and capture is not active.
|
|
||||||
// For key up, we'll need to do a little more work to determine if we need to send this
|
|
||||||
// event to SDL in order to keep its internal keyboard modifier state consistent.
|
|
||||||
if (event.type == SDL_KEYDOWN && !keyCaptureActive) {
|
|
||||||
goto NextHook;
|
|
||||||
}
|
|
||||||
|
|
||||||
event.key.keysym.mod = SDL_GetModState();
|
|
||||||
switch (hookData->vkCode) {
|
|
||||||
case VK_LWIN:
|
|
||||||
if (event.key.state == SDL_PRESSED) {
|
|
||||||
event.key.keysym.mod |= KMOD_LGUI;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
event.key.keysym.mod &= ~KMOD_LGUI;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case VK_RWIN:
|
|
||||||
if (event.key.state == SDL_PRESSED) {
|
|
||||||
event.key.keysym.mod |= KMOD_RGUI;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
event.key.keysym.mod &= ~KMOD_RGUI;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case VK_LMENU:
|
|
||||||
if (event.key.state == SDL_PRESSED) {
|
|
||||||
event.key.keysym.mod |= KMOD_LALT;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
event.key.keysym.mod &= ~KMOD_LALT;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case VK_RMENU:
|
|
||||||
if (event.key.state == SDL_PRESSED) {
|
|
||||||
event.key.keysym.mod |= KMOD_RALT;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
event.key.keysym.mod &= ~KMOD_RALT;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case VK_LCONTROL:
|
|
||||||
if (event.key.state == SDL_PRESSED) {
|
|
||||||
event.key.keysym.mod |= KMOD_LCTRL;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
event.key.keysym.mod &= ~KMOD_LCTRL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case VK_RCONTROL:
|
|
||||||
if (event.key.state == SDL_PRESSED) {
|
|
||||||
event.key.keysym.mod |= KMOD_RCTRL;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
event.key.keysym.mod &= ~KMOD_RCTRL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the modifier state is unchanged and we're not capturing system keys,
|
|
||||||
// drop the event. If the modifier state is changed, we need to send the
|
|
||||||
// event to update SDL's state even if system key capture is now inactive
|
|
||||||
// (due to focus loss, mouse capture toggled off, etc.) otherwise SDL won't
|
|
||||||
// know that the modifier key has been lifted.
|
|
||||||
if (event.key.keysym.mod == SDL_GetModState() && !keyCaptureActive) {
|
|
||||||
goto NextHook;
|
|
||||||
}
|
|
||||||
|
|
||||||
event.key.timestamp = SDL_GetTicks();
|
|
||||||
event.key.windowID = SDL_GetWindowID(session->m_Window);
|
|
||||||
|
|
||||||
// NOTE: event.key.repeat is not populated in this path!
|
|
||||||
SDL_PushEvent(&event);
|
|
||||||
|
|
||||||
// Synchronize SDL's modifier state with the current state.
|
|
||||||
// SDL won't do this on its own because it will never see the
|
|
||||||
// WM_KEYUP/WM_KEYDOWN events.
|
|
||||||
SDL_SetModState((SDL_Keymod)event.key.keysym.mod);
|
|
||||||
|
|
||||||
// Eat the event only if key capture is active.
|
|
||||||
// If capture is not active and we're just resyncing SDL's modifier state,
|
|
||||||
// we need to ensure the key event is still delivered normally to the
|
|
||||||
// window in focus.
|
|
||||||
if (keyCaptureActive) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
NextHook:
|
|
||||||
return CallNextHookEx(g_KeyboardHook, nCode, wParam, lParam);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void SdlInputHandler::raiseAllKeys()
|
void SdlInputHandler::raiseAllKeys()
|
||||||
{
|
{
|
||||||
if (m_KeysDown.isEmpty()) {
|
if (m_KeysDown.isEmpty()) {
|
||||||
@@ -496,19 +321,36 @@ bool SdlInputHandler::isSystemKeyCaptureActive()
|
|||||||
|
|
||||||
Uint32 windowFlags = SDL_GetWindowFlags(m_Window);
|
Uint32 windowFlags = SDL_GetWindowFlags(m_Window);
|
||||||
return (windowFlags & SDL_WINDOW_INPUT_FOCUS) &&
|
return (windowFlags & SDL_WINDOW_INPUT_FOCUS) &&
|
||||||
(windowFlags & SDL_WINDOW_INPUT_GRABBED) &&
|
#if SDL_VERSION_ATLEAST(2, 0, 15)
|
||||||
(windowFlags & SDL_WINDOW_FULLSCREEN_DESKTOP);
|
(windowFlags & SDL_WINDOW_KEYBOARD_GRABBED)
|
||||||
|
#else
|
||||||
|
(windowFlags & SDL_WINDOW_INPUT_GRABBED)
|
||||||
|
#endif
|
||||||
|
#ifdef Q_OS_DARWIN
|
||||||
|
// Darwin only supports full-screen system key capture
|
||||||
|
(windowFlags & SDL_WINDOW_FULLSCREEN)
|
||||||
|
#endif
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SdlInputHandler::setCaptureActive(bool active)
|
void SdlInputHandler::setCaptureActive(bool active)
|
||||||
{
|
{
|
||||||
if (active) {
|
if (active) {
|
||||||
// If we're in full-screen exclusive mode, grab the cursor so it can't accidentally leave our window.
|
// If we're in full-screen exclusive mode, grab the cursor so it can't accidentally leave our window.
|
||||||
// If we're in full-screen desktop mode but system key capture is enabled, also grab the cursor (will grab the keyboard too on X11).
|
if ((SDL_GetWindowFlags(m_Window) & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN) {
|
||||||
if (((SDL_GetWindowFlags(m_Window) & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0 && m_CaptureSystemKeysEnabled) ||
|
|
||||||
(SDL_GetWindowFlags(m_Window) & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN) {
|
|
||||||
SDL_SetWindowGrab(m_Window, SDL_TRUE);
|
SDL_SetWindowGrab(m_Window, SDL_TRUE);
|
||||||
}
|
}
|
||||||
|
else if (m_CaptureSystemKeysEnabled) {
|
||||||
|
#if SDL_VERSION_ATLEAST(2, 0, 15) && !defined(Q_OS_DARWIN)
|
||||||
|
// On SDL 2.0.15, we can get keyboard-only grab on Win32, X11, and Wayland
|
||||||
|
SDL_SetWindowKeyboardGrab(m_Window, SDL_TRUE);
|
||||||
|
#else
|
||||||
|
// If we're in full-screen desktop mode but system key capture is enabled, also grab the cursor (will grab the keyboard too on X11).
|
||||||
|
if (SDL_GetWindowFlags(m_Window) & SDL_WINDOW_FULLSCREEN) {
|
||||||
|
SDL_SetWindowGrab(m_Window, SDL_TRUE);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
if (!m_AbsoluteMouseMode) {
|
if (!m_AbsoluteMouseMode) {
|
||||||
// If our window is occluded when mouse is captured, the mouse may
|
// If our window is occluded when mouse is captured, the mouse may
|
||||||
@@ -556,6 +398,11 @@ void SdlInputHandler::setCaptureActive(bool active)
|
|||||||
|
|
||||||
// Allow the cursor to leave the bounds of our window again.
|
// Allow the cursor to leave the bounds of our window again.
|
||||||
SDL_SetWindowGrab(m_Window, SDL_FALSE);
|
SDL_SetWindowGrab(m_Window, SDL_FALSE);
|
||||||
|
|
||||||
|
#if SDL_VERSION_ATLEAST(2, 0, 15)
|
||||||
|
// Allow the keyboard to leave the window
|
||||||
|
SDL_SetWindowKeyboardGrab(m_Window, SDL_FALSE);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,11 +5,6 @@
|
|||||||
|
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
|
|
||||||
#ifdef Q_OS_WIN32
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#include <Windows.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define SDL_CODE_HIDE_CURSOR 1
|
#define SDL_CODE_HIDE_CURSOR 1
|
||||||
#define SDL_CODE_SHOW_CURSOR 2
|
#define SDL_CODE_SHOW_CURSOR 2
|
||||||
|
|
||||||
@@ -137,11 +132,6 @@ private:
|
|||||||
static
|
static
|
||||||
Uint32 dragTimerCallback(Uint32 interval, void* param);
|
Uint32 dragTimerCallback(Uint32 interval, void* param);
|
||||||
|
|
||||||
#ifdef Q_OS_WIN32
|
|
||||||
static
|
|
||||||
LRESULT CALLBACK keyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
SDL_Window* m_Window;
|
SDL_Window* m_Window;
|
||||||
bool m_MultiController;
|
bool m_MultiController;
|
||||||
bool m_GamepadMouse;
|
bool m_GamepadMouse;
|
||||||
|
|||||||
Reference in New Issue
Block a user