Start implementation of system key capture
This commit is contained in:
parent
bedea4a093
commit
30e3b02867
3 changed files with 193 additions and 11 deletions
|
|
@ -9,6 +9,10 @@
|
|||
|
||||
#define MOUSE_POLLING_INTERVAL 5
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
HHOOK g_KeyboardHook;
|
||||
#endif
|
||||
|
||||
SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs, NvComputer*, int streamWidth, int streamHeight)
|
||||
: m_MultiController(prefs.multiController),
|
||||
m_GamepadMouse(prefs.gamepadMouse),
|
||||
|
|
@ -20,6 +24,7 @@ SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs, NvComputer*, int s
|
|||
m_MouseWasInVideoRegion(false),
|
||||
m_PendingMouseButtonsAllUpOnVideoRegionLeave(false),
|
||||
m_FakeCaptureActive(false),
|
||||
m_CaptureSystemKeysEnabled(false),
|
||||
m_LongPressTimer(0),
|
||||
m_StreamWidth(streamWidth),
|
||||
m_StreamHeight(streamHeight),
|
||||
|
|
@ -43,6 +48,11 @@ SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs, NvComputer*, int s
|
|||
prefs.absoluteMouseMode ? "1" : "0",
|
||||
SDL_HINT_OVERRIDE);
|
||||
|
||||
// If we're grabbing system keys, enable the X11 keyboard grab in SDL
|
||||
SDL_SetHintWithPriority(SDL_HINT_GRAB_KEYBOARD,
|
||||
m_CaptureSystemKeysEnabled ? "1" : "0",
|
||||
SDL_HINT_OVERRIDE);
|
||||
|
||||
// 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
|
||||
// trigger a click on the host if the Moonlight window is not focused. In
|
||||
|
|
@ -138,10 +148,25 @@ SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs, NvComputer*, int s
|
|||
}
|
||||
|
||||
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()
|
||||
{
|
||||
#ifdef Q_OS_WIN32
|
||||
if (g_KeyboardHook != nullptr) {
|
||||
UnhookWindowsHookEx(g_KeyboardHook);
|
||||
g_KeyboardHook = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < MAX_GAMEPADS; i++) {
|
||||
if (m_GamepadState[i].mouseEmulationTimer != 0) {
|
||||
Session::get()->notifyMouseEmulationMode(false);
|
||||
|
|
@ -195,6 +220,132 @@ void SdlInputHandler::setWindow(SDL_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);
|
||||
}
|
||||
|
||||
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 || !session->m_InputHandler->isSystemKeyCaptureActive()) {
|
||||
goto NextHook;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
event.key.timestamp = SDL_GetTicks();
|
||||
event.key.windowID = SDL_GetWindowID(session->m_Window);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
return 1;
|
||||
|
||||
NextHook:
|
||||
return CallNextHookEx(g_KeyboardHook, nCode, wParam, lParam);
|
||||
}
|
||||
#endif
|
||||
|
||||
void SdlInputHandler::raiseAllKeys()
|
||||
{
|
||||
if (m_KeysDown.isEmpty()) {
|
||||
|
|
@ -255,11 +406,29 @@ bool SdlInputHandler::isCaptureActive()
|
|||
return m_FakeCaptureActive;
|
||||
}
|
||||
|
||||
bool SdlInputHandler::isSystemKeyCaptureActive()
|
||||
{
|
||||
if (!m_CaptureSystemKeysEnabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_Window == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Uint32 windowFlags = SDL_GetWindowFlags(m_Window);
|
||||
return (windowFlags & SDL_WINDOW_INPUT_FOCUS) &&
|
||||
(windowFlags & SDL_WINDOW_INPUT_GRABBED) &&
|
||||
(windowFlags & SDL_WINDOW_FULLSCREEN_DESKTOP);
|
||||
}
|
||||
|
||||
void SdlInputHandler::setCaptureActive(bool active)
|
||||
{
|
||||
if (active) {
|
||||
// If we're in full-screen exclusive mode, grab the cursor so it can't accidentally leave our window.
|
||||
if ((SDL_GetWindowFlags(m_Window) & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN) {
|
||||
// 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) != 0 && m_CaptureSystemKeysEnabled) ||
|
||||
(SDL_GetWindowFlags(m_Window) & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN) {
|
||||
SDL_SetWindowGrab(m_Window, SDL_TRUE);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue