mirror of
https://github.com/moonlight-stream/moonlight-qt.git
synced 2025-07-03 00:06:09 +00:00
Move clipboard processing to a separate thread to avoid blocking the main thread during pastes
This commit is contained in:
parent
8372717a09
commit
05e4c5b507
@ -2,6 +2,34 @@
|
|||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
|
int SdlInputHandler::clipboardThreadProc(void *ptr)
|
||||||
|
{
|
||||||
|
auto me = (SdlInputHandler*)ptr;
|
||||||
|
|
||||||
|
while (!SDL_AtomicGet(&me->m_ShutdownClipboardThread)) {
|
||||||
|
QString clipboardData;
|
||||||
|
|
||||||
|
SDL_LockMutex(me->m_ClipboardLock);
|
||||||
|
for (;;) {
|
||||||
|
clipboardData = me->m_ClipboardData;
|
||||||
|
me->m_ClipboardData.clear();
|
||||||
|
if (!clipboardData.isEmpty() || SDL_AtomicGet(&me->m_ShutdownClipboardThread)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
SDL_CondWait(me->m_ClipboardHasData, me->m_ClipboardLock);
|
||||||
|
}
|
||||||
|
SDL_UnlockMutex(me->m_ClipboardLock);
|
||||||
|
|
||||||
|
// We might get here on shutdown, so don't send text if there's
|
||||||
|
// nothing to send.
|
||||||
|
if (!clipboardData.isEmpty()) {
|
||||||
|
me->sendText(clipboardData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#define MAP_KEY(c, sc) \
|
#define MAP_KEY(c, sc) \
|
||||||
case c: \
|
case c: \
|
||||||
event.key.keysym.scancode = sc; \
|
event.key.keysym.scancode = sc; \
|
||||||
@ -13,14 +41,18 @@
|
|||||||
event.key.keysym.mod = KMOD_SHIFT; \
|
event.key.keysym.mod = KMOD_SHIFT; \
|
||||||
break
|
break
|
||||||
|
|
||||||
void SdlInputHandler::sendText(const char* text)
|
void SdlInputHandler::sendText(QString& string)
|
||||||
{
|
{
|
||||||
QString string = QString::fromUtf8(text);
|
|
||||||
|
|
||||||
for (int i = 0; i < string.size(); i++) {
|
for (int i = 0; i < string.size(); i++) {
|
||||||
char16_t c = string[i].unicode();
|
char16_t c = string[i].unicode();
|
||||||
SDL_Event event = {};
|
SDL_Event event = {};
|
||||||
|
|
||||||
|
// If we're sending a very long string, we might get a termination request
|
||||||
|
// while we're in the middle of sending a string. In that case, just bail.
|
||||||
|
if (SDL_AtomicGet(&m_ShutdownClipboardThread)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (c >= 'A' && c <= 'Z') {
|
if (c >= 'A' && c <= 'Z') {
|
||||||
event.key.keysym.scancode = (SDL_Scancode)((c - 'A') + SDL_SCANCODE_A);
|
event.key.keysym.scancode = (SDL_Scancode)((c - 'A') + SDL_SCANCODE_A);
|
||||||
event.key.keysym.mod = KMOD_SHIFT;
|
event.key.keysym.mod = KMOD_SHIFT;
|
||||||
@ -125,6 +157,16 @@ void SdlInputHandler::sendText(const char* text)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (event.key.keysym.mod & KMOD_SHIFT) {
|
||||||
|
SDL_Event modifierEvent = {};
|
||||||
|
modifierEvent.type = SDL_KEYDOWN;
|
||||||
|
modifierEvent.key.state = SDL_PRESSED;
|
||||||
|
modifierEvent.key.keysym.scancode = SDL_SCANCODE_LSHIFT;
|
||||||
|
handleKeyEvent(&modifierEvent.key);
|
||||||
|
|
||||||
|
SDL_Delay(10);
|
||||||
|
}
|
||||||
|
|
||||||
event.type = SDL_KEYDOWN;
|
event.type = SDL_KEYDOWN;
|
||||||
event.key.state = SDL_PRESSED;
|
event.key.state = SDL_PRESSED;
|
||||||
handleKeyEvent(&event.key);
|
handleKeyEvent(&event.key);
|
||||||
@ -134,5 +176,15 @@ void SdlInputHandler::sendText(const char* text)
|
|||||||
event.type = SDL_KEYUP;
|
event.type = SDL_KEYUP;
|
||||||
event.key.state = SDL_RELEASED;
|
event.key.state = SDL_RELEASED;
|
||||||
handleKeyEvent(&event.key);
|
handleKeyEvent(&event.key);
|
||||||
|
|
||||||
|
if (event.key.keysym.mod & KMOD_SHIFT) {
|
||||||
|
SDL_Event modifierEvent = {};
|
||||||
|
modifierEvent.type = SDL_KEYUP;
|
||||||
|
modifierEvent.key.state = SDL_RELEASED;
|
||||||
|
modifierEvent.key.keysym.scancode = SDL_SCANCODE_LSHIFT;
|
||||||
|
handleKeyEvent(&modifierEvent.key);
|
||||||
|
|
||||||
|
SDL_Delay(10);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,8 @@ SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs, NvComputer*, int s
|
|||||||
m_RightButtonReleaseTimer(0),
|
m_RightButtonReleaseTimer(0),
|
||||||
m_DragTimer(0),
|
m_DragTimer(0),
|
||||||
m_DragButton(0),
|
m_DragButton(0),
|
||||||
m_NumFingersDown(0)
|
m_NumFingersDown(0),
|
||||||
|
m_ClipboardData()
|
||||||
{
|
{
|
||||||
// Allow gamepad input when the app doesn't have focus if requested
|
// Allow gamepad input when the app doesn't have focus if requested
|
||||||
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, prefs.backgroundGamepad ? "1" : "0");
|
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, prefs.backgroundGamepad ? "1" : "0");
|
||||||
@ -196,6 +197,13 @@ SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs, NvComputer*, int s
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_MouseMoveTimer = SDL_AddTimer(pollingInterval, SdlInputHandler::mouseMoveTimerCallback, this);
|
m_MouseMoveTimer = SDL_AddTimer(pollingInterval, SdlInputHandler::mouseMoveTimerCallback, this);
|
||||||
|
|
||||||
|
// Initialize state used by the clipboard thread before we start it
|
||||||
|
SDL_AtomicSet(&m_ShutdownClipboardThread, 0);
|
||||||
|
m_ClipboardHasData = SDL_CreateCond();
|
||||||
|
m_ClipboardLock = SDL_CreateMutex();
|
||||||
|
|
||||||
|
m_ClipboardThread = SDL_CreateThread(SdlInputHandler::clipboardThreadProc, "Clipboard Sender", this);
|
||||||
}
|
}
|
||||||
|
|
||||||
SdlInputHandler::~SdlInputHandler()
|
SdlInputHandler::~SdlInputHandler()
|
||||||
@ -221,6 +229,17 @@ SdlInputHandler::~SdlInputHandler()
|
|||||||
SDL_RemoveTimer(m_RightButtonReleaseTimer);
|
SDL_RemoveTimer(m_RightButtonReleaseTimer);
|
||||||
SDL_RemoveTimer(m_DragTimer);
|
SDL_RemoveTimer(m_DragTimer);
|
||||||
|
|
||||||
|
// Wake up the clipboard thread to terminate it
|
||||||
|
SDL_AtomicSet(&m_ShutdownClipboardThread, 1);
|
||||||
|
SDL_CondBroadcast(m_ClipboardHasData);
|
||||||
|
|
||||||
|
// Wait for it to terminate
|
||||||
|
SDL_WaitThread(m_ClipboardThread, nullptr);
|
||||||
|
|
||||||
|
// Now we can safely clean up its resources
|
||||||
|
SDL_DestroyCond(m_ClipboardHasData);
|
||||||
|
SDL_DestroyMutex(m_ClipboardLock);
|
||||||
|
|
||||||
#if !SDL_VERSION_ATLEAST(2, 0, 9)
|
#if !SDL_VERSION_ATLEAST(2, 0, 9)
|
||||||
SDL_QuitSubSystem(SDL_INIT_HAPTIC);
|
SDL_QuitSubSystem(SDL_INIT_HAPTIC);
|
||||||
SDL_assert(!SDL_WasInit(SDL_INIT_HAPTIC));
|
SDL_assert(!SDL_WasInit(SDL_INIT_HAPTIC));
|
||||||
|
@ -65,7 +65,7 @@ public:
|
|||||||
|
|
||||||
void handleJoystickArrivalEvent(SDL_JoyDeviceEvent* event);
|
void handleJoystickArrivalEvent(SDL_JoyDeviceEvent* event);
|
||||||
|
|
||||||
void sendText(const char* text);
|
void sendText(QString& string);
|
||||||
|
|
||||||
void rumble(unsigned short controllerNumber, unsigned short lowFreqMotor, unsigned short highFreqMotor);
|
void rumble(unsigned short controllerNumber, unsigned short lowFreqMotor, unsigned short highFreqMotor);
|
||||||
|
|
||||||
@ -136,6 +136,9 @@ private:
|
|||||||
static
|
static
|
||||||
Uint32 dragTimerCallback(Uint32 interval, void* param);
|
Uint32 dragTimerCallback(Uint32 interval, void* param);
|
||||||
|
|
||||||
|
static
|
||||||
|
int clipboardThreadProc(void *ptr);
|
||||||
|
|
||||||
SDL_Window* m_Window;
|
SDL_Window* m_Window;
|
||||||
bool m_MultiController;
|
bool m_MultiController;
|
||||||
bool m_GamepadMouse;
|
bool m_GamepadMouse;
|
||||||
@ -187,5 +190,11 @@ private:
|
|||||||
char m_DragButton;
|
char m_DragButton;
|
||||||
int m_NumFingersDown;
|
int m_NumFingersDown;
|
||||||
|
|
||||||
|
SDL_Thread* m_ClipboardThread;
|
||||||
|
SDL_atomic_t m_ShutdownClipboardThread;
|
||||||
|
QString m_ClipboardData;
|
||||||
|
SDL_cond* m_ClipboardHasData;
|
||||||
|
SDL_mutex* m_ClipboardLock;
|
||||||
|
|
||||||
static const int k_ButtonMap[];
|
static const int k_ButtonMap[];
|
||||||
};
|
};
|
||||||
|
@ -94,12 +94,13 @@ void SdlInputHandler::performPendingSpecialKeyCombo()
|
|||||||
"Detected paste text combo");
|
"Detected paste text combo");
|
||||||
const char* text;
|
const char* text;
|
||||||
if (SDL_HasClipboardText() && (text = SDL_GetClipboardText()) != nullptr) {
|
if (SDL_HasClipboardText() && (text = SDL_GetClipboardText()) != nullptr) {
|
||||||
// Reset pending key combo before pasting,
|
// Append this data to the clipboard data string for the thread to process
|
||||||
// otherwise it will ignore our keypresses.
|
SDL_LockMutex(m_ClipboardLock);
|
||||||
m_PendingKeyCombo = KeyComboMax;
|
m_ClipboardData.append(text);
|
||||||
|
SDL_CondSignal(m_ClipboardHasData);
|
||||||
|
SDL_UnlockMutex(m_ClipboardLock);
|
||||||
|
|
||||||
// Send the text and free it as required by SDL
|
// SDL_GetClipboardText() allocates, so we must free
|
||||||
sendText(text);
|
|
||||||
SDL_free((void*)text);
|
SDL_free((void*)text);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -117,13 +118,17 @@ void SdlInputHandler::performPendingSpecialKeyCombo()
|
|||||||
m_PendingKeyCombo = KeyComboMax;
|
m_PendingKeyCombo = KeyComboMax;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define IS_SYNTHETIC_KEY_EVENT(x) ((x)->timestamp == 0)
|
||||||
|
|
||||||
void SdlInputHandler::handleKeyEvent(SDL_KeyboardEvent* event)
|
void SdlInputHandler::handleKeyEvent(SDL_KeyboardEvent* event)
|
||||||
{
|
{
|
||||||
short keyCode;
|
short keyCode;
|
||||||
char modifiers;
|
char modifiers;
|
||||||
|
|
||||||
// Check for our special key combos
|
// Check for our special key combos
|
||||||
if ((event->state == SDL_PRESSED) &&
|
// Ignore timestamp == 0 which are sent from our keyboard code.
|
||||||
|
if (!IS_SYNTHETIC_KEY_EVENT(event) &&
|
||||||
|
(event->state == SDL_PRESSED) &&
|
||||||
(event->keysym.mod & KMOD_CTRL) &&
|
(event->keysym.mod & KMOD_CTRL) &&
|
||||||
(event->keysym.mod & KMOD_ALT) &&
|
(event->keysym.mod & KMOD_ALT) &&
|
||||||
(event->keysym.mod & KMOD_SHIFT)) {
|
(event->keysym.mod & KMOD_SHIFT)) {
|
||||||
@ -157,7 +162,7 @@ void SdlInputHandler::handleKeyEvent(SDL_KeyboardEvent* event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event->state == SDL_PRESSED && m_PendingKeyCombo != KeyComboMax) {
|
if (!IS_SYNTHETIC_KEY_EVENT(event) && event->state == SDL_PRESSED && m_PendingKeyCombo != KeyComboMax) {
|
||||||
// Ignore further key presses until the special combo is raised
|
// Ignore further key presses until the special combo is raised
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -401,6 +406,9 @@ void SdlInputHandler::handleKeyEvent(SDL_KeyboardEvent* event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If this is a synthetic keypress from the clipboard code,
|
||||||
|
// this will be on a non-main thread, so don't touch m_KeysDown.
|
||||||
|
if (!IS_SYNTHETIC_KEY_EVENT(event)) {
|
||||||
// Track the key state so we always know which keys are down
|
// Track the key state so we always know which keys are down
|
||||||
if (event->state == SDL_PRESSED) {
|
if (event->state == SDL_PRESSED) {
|
||||||
m_KeysDown.insert(keyCode);
|
m_KeysDown.insert(keyCode);
|
||||||
@ -408,13 +416,14 @@ void SdlInputHandler::handleKeyEvent(SDL_KeyboardEvent* event)
|
|||||||
else {
|
else {
|
||||||
m_KeysDown.remove(keyCode);
|
m_KeysDown.remove(keyCode);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LiSendKeyboardEvent(0x8000 | 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);
|
||||||
|
|
||||||
if (m_PendingKeyCombo != KeyComboMax && m_KeysDown.isEmpty()) {
|
if (!IS_SYNTHETIC_KEY_EVENT(event) && m_PendingKeyCombo != KeyComboMax && m_KeysDown.isEmpty()) {
|
||||||
int keys;
|
int keys;
|
||||||
const Uint8 *keyState = SDL_GetKeyboardState(&keys);
|
const Uint8 *keyState = SDL_GetKeyboardState(&keys);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user