mirror of
https://github.com/moonlight-stream/moonlight-qt.git
synced 2025-07-02 15:55:39 +00:00
Start implementation of system key capture
This commit is contained in:
parent
bedea4a093
commit
30e3b02867
@ -9,6 +9,10 @@
|
|||||||
|
|
||||||
#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),
|
||||||
@ -20,6 +24,7 @@ SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs, NvComputer*, int s
|
|||||||
m_MouseWasInVideoRegion(false),
|
m_MouseWasInVideoRegion(false),
|
||||||
m_PendingMouseButtonsAllUpOnVideoRegionLeave(false),
|
m_PendingMouseButtonsAllUpOnVideoRegionLeave(false),
|
||||||
m_FakeCaptureActive(false),
|
m_FakeCaptureActive(false),
|
||||||
|
m_CaptureSystemKeysEnabled(false),
|
||||||
m_LongPressTimer(0),
|
m_LongPressTimer(0),
|
||||||
m_StreamWidth(streamWidth),
|
m_StreamWidth(streamWidth),
|
||||||
m_StreamHeight(streamHeight),
|
m_StreamHeight(streamHeight),
|
||||||
@ -43,6 +48,11 @@ 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
|
||||||
|
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
|
// 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
|
||||||
// trigger a click on the host if the Moonlight window is not focused. In
|
// 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);
|
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;
|
||||||
|
}
|
||||||
|
#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);
|
||||||
@ -195,6 +220,132 @@ 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
void SdlInputHandler::raiseAllKeys()
|
||||||
{
|
{
|
||||||
if (m_KeysDown.isEmpty()) {
|
if (m_KeysDown.isEmpty()) {
|
||||||
@ -255,11 +406,29 @@ bool SdlInputHandler::isCaptureActive()
|
|||||||
return m_FakeCaptureActive;
|
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)
|
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 ((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);
|
SDL_SetWindowGrab(m_Window, SDL_TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,11 @@
|
|||||||
|
|
||||||
#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
|
||||||
|
|
||||||
@ -78,6 +83,8 @@ public:
|
|||||||
|
|
||||||
bool isCaptureActive();
|
bool isCaptureActive();
|
||||||
|
|
||||||
|
bool isSystemKeyCaptureActive();
|
||||||
|
|
||||||
void setCaptureActive(bool active);
|
void setCaptureActive(bool active);
|
||||||
|
|
||||||
bool isMouseInVideoRegion(int mouseX, int mouseY, int windowWidth = -1, int windowHeight = -1);
|
bool isMouseInVideoRegion(int mouseX, int mouseY, int windowWidth = -1, int windowHeight = -1);
|
||||||
@ -117,6 +124,11 @@ 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;
|
||||||
@ -142,6 +154,7 @@ private:
|
|||||||
bool m_FakeCaptureActive;
|
bool m_FakeCaptureActive;
|
||||||
QString m_OldIgnoreDevices;
|
QString m_OldIgnoreDevices;
|
||||||
QString m_OldIgnoreDevicesExcept;
|
QString m_OldIgnoreDevicesExcept;
|
||||||
|
bool m_CaptureSystemKeysEnabled;
|
||||||
|
|
||||||
SDL_TouchFingerEvent m_LastTouchDownEvent;
|
SDL_TouchFingerEvent m_LastTouchDownEvent;
|
||||||
SDL_TouchFingerEvent m_LastTouchUpEvent;
|
SDL_TouchFingerEvent m_LastTouchUpEvent;
|
||||||
|
@ -5,10 +5,6 @@
|
|||||||
|
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
|
|
||||||
// Until we can fully capture these on all platforms (without conflicting with
|
|
||||||
// OS-provided shortcuts), we should avoid passing them through to the host.
|
|
||||||
//#define ENABLE_META
|
|
||||||
|
|
||||||
#define VK_0 0x30
|
#define VK_0 0x30
|
||||||
#define VK_A 0x41
|
#define VK_A 0x41
|
||||||
|
|
||||||
@ -215,11 +211,11 @@ void SdlInputHandler::handleKeyEvent(SDL_KeyboardEvent* event)
|
|||||||
if (event->keysym.mod & KMOD_SHIFT) {
|
if (event->keysym.mod & KMOD_SHIFT) {
|
||||||
modifiers |= MODIFIER_SHIFT;
|
modifiers |= MODIFIER_SHIFT;
|
||||||
}
|
}
|
||||||
#ifdef ENABLE_META
|
|
||||||
if (event->keysym.mod & KMOD_GUI) {
|
if (event->keysym.mod & KMOD_GUI) {
|
||||||
modifiers |= MODIFIER_META;
|
if (isSystemKeyCaptureActive()) {
|
||||||
|
modifiers |= MODIFIER_META;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// Set keycode. We explicitly use scancode here because GFE will try to correct
|
// Set keycode. We explicitly use scancode here because GFE will try to correct
|
||||||
// for AZERTY layouts on the host but it depends on receiving VK_ values matching
|
// for AZERTY layouts on the host but it depends on receiving VK_ values matching
|
||||||
@ -360,14 +356,18 @@ void SdlInputHandler::handleKeyEvent(SDL_KeyboardEvent* event)
|
|||||||
case SDL_SCANCODE_RALT:
|
case SDL_SCANCODE_RALT:
|
||||||
keyCode = 0xA5;
|
keyCode = 0xA5;
|
||||||
break;
|
break;
|
||||||
#ifdef ENABLE_META
|
|
||||||
case SDL_SCANCODE_LGUI:
|
case SDL_SCANCODE_LGUI:
|
||||||
|
if (!isSystemKeyCaptureActive()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
keyCode = 0x5B;
|
keyCode = 0x5B;
|
||||||
break;
|
break;
|
||||||
case SDL_SCANCODE_RGUI:
|
case SDL_SCANCODE_RGUI:
|
||||||
|
if (!isSystemKeyCaptureActive()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
keyCode = 0x5C;
|
keyCode = 0x5C;
|
||||||
break;
|
break;
|
||||||
#endif
|
|
||||||
case SDL_SCANCODE_AC_BACK:
|
case SDL_SCANCODE_AC_BACK:
|
||||||
keyCode = 0xA6;
|
keyCode = 0xA6;
|
||||||
break;
|
break;
|
||||||
@ -441,7 +441,7 @@ void SdlInputHandler::handleKeyEvent(SDL_KeyboardEvent* event)
|
|||||||
m_KeysDown.remove(keyCode);
|
m_KeysDown.remove(keyCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
LiSendKeyboardEvent(keyCode,
|
LiSendKeyboardEvent(0x8000 | keyCode,
|
||||||
event->state == SDL_PRESSED ?
|
event->state == SDL_PRESSED ?
|
||||||
KEY_ACTION_DOWN : KEY_ACTION_UP,
|
KEY_ACTION_DOWN : KEY_ACTION_UP,
|
||||||
modifiers);
|
modifiers);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user