diff --git a/app/streaming/input/input.cpp b/app/streaming/input/input.cpp index ddeebec4..a2a109e7 100644 --- a/app/streaming/input/input.cpp +++ b/app/streaming/input/input.cpp @@ -293,6 +293,15 @@ void SdlInputHandler::raiseAllKeys() m_KeysDown.clear(); } +void SdlInputHandler::raiseAllMouseButtons() +{ + LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_LEFT); + LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_MIDDLE); + LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_RIGHT); + LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_X1); + LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_X2); +} + void SdlInputHandler::notifyMouseLeave() { // SDL on Windows doesn't send the mouse button up until the mouse re-enters the window diff --git a/app/streaming/input/input.h b/app/streaming/input/input.h index 050f150c..753ff275 100644 --- a/app/streaming/input/input.h +++ b/app/streaming/input/input.h @@ -134,6 +134,8 @@ public: void raiseAllKeys(); + void raiseAllMouseButtons(); + void notifyMouseLeave(); void notifyFocusLost(); diff --git a/app/streaming/session.cpp b/app/streaming/session.cpp index aa18860f..69bf4d02 100644 --- a/app/streaming/session.cpp +++ b/app/streaming/session.cpp @@ -29,6 +29,7 @@ #define SDL_CODE_GAMECONTROLLER_SET_CONTROLLER_LED 104 #define SDL_CODE_GAMECONTROLLER_SET_ADAPTIVE_TRIGGERS 105 #define SDL_CODE_HIDE_STATUS_OVERLAY 106 +#define SDL_CODE_SET_INPUT_POLICY 107 #include @@ -61,7 +62,8 @@ CONNECTION_LISTENER_CALLBACKS Session::k_ConnCallbacks = { Session::clRumbleTriggers, Session::clSetMotionEventState, Session::clSetControllerLED, - Session::clSetAdaptiveTriggers + Session::clSetAdaptiveTriggers, + Session::clSetInputPolicy }; Session* Session::s_ActiveSession; @@ -277,6 +279,20 @@ void Session::clSetAdaptiveTriggers(uint16_t controllerNumber, uint8_t eventFlag SDL_PushEvent(&setControllerLEDEvent); } +void Session::clSetInputPolicy(uint8_t allowKeyboard, uint8_t allowMouse, uint8_t allowGamepad, uint8_t reason) +{ + SDL_Event setInputPolicyEvent = {}; + setInputPolicyEvent.type = SDL_USEREVENT; + setInputPolicyEvent.user.code = SDL_CODE_SET_INPUT_POLICY; + setInputPolicyEvent.user.data1 = nullptr; + setInputPolicyEvent.user.data2 = (void*)(uintptr_t)( + ((uintptr_t)allowKeyboard << 24) | + ((uintptr_t)allowMouse << 16) | + ((uintptr_t)allowGamepad << 8) | + (uintptr_t)reason); + SDL_PushEvent(&setInputPolicyEvent); +} + bool Session::chooseDecoder(StreamingPreferences::VideoDecoderSelection vds, SDL_Window* window, int videoFormat, int width, int height, @@ -562,7 +578,7 @@ Session::Session(NvComputer* computer, NvApp& app, StreamingPreferences *prefere m_ManualAudioMuted(false), m_AudioVolumeScalar(1.0f), m_AllowGamepadInput(true), - m_AllowKeyboardMouseInput(true), + m_AllowKeyboardMouseInput(false), m_StatusOverlayGeneration(0), m_QtWindow(nullptr), m_UnexpectedTermination(true), // Failure prior to streaming is unexpected @@ -582,6 +598,7 @@ Session::Session(NvComputer* computer, NvApp& app, StreamingPreferences *prefere void Session::setGamepadInputAllowed(bool allowed) { m_AllowGamepadInput.store(allowed, std::memory_order_relaxed); + sendInputPermissionStateToHost(LI_SESSION_INPUT_POLICY_REASON_USER_TOGGLE); notifyInputPermissionState(); } @@ -627,8 +644,10 @@ void Session::setKeyboardMouseInputAllowed(bool allowed) bool previous = m_AllowKeyboardMouseInput.exchange(allowed, std::memory_order_relaxed); if (previous && !allowed && m_InputHandler != nullptr) { m_InputHandler->raiseAllKeys(); + m_InputHandler->raiseAllMouseButtons(); } + sendInputPermissionStateToHost(LI_SESSION_INPUT_POLICY_REASON_USER_TOGGLE); notifyInputPermissionState(); } @@ -654,6 +673,22 @@ void Session::notifyInputPermissionState() showTemporaryStatusOverlay(buffer); } +void Session::sendInputPermissionStateToHost(uint8_t reason) +{ + const bool allowKeyboardMouse = isKeyboardMouseInputAllowed(); + const bool allowGamepad = isGamepadInputAllowed(); + const int err = LiSendSessionInputPolicy(allowKeyboardMouse, + allowKeyboardMouse, + allowGamepad, + reason); + + if (err != 0 && err != LI_ERR_UNSUPPORTED) { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, + "Failed to send input policy update to host: %d", + err); + } +} + Uint32 Session::statusOverlayTimeoutCallback(Uint32, void* param) { SDL_Event event = {}; @@ -678,6 +713,22 @@ void Session::showTemporaryStatusOverlay(const char* text, Uint32 timeoutMs) SDL_AddTimer(timeoutMs, statusOverlayTimeoutCallback, (void*)(uintptr_t)generation); } +void Session::applyHostInputPolicy(bool allowKeyboard, bool allowMouse, bool allowGamepad, uint8_t reason) +{ + const bool allowKeyboardMouse = allowKeyboard && allowMouse; + const bool previousKeyboardMouse = m_AllowKeyboardMouseInput.exchange(allowKeyboardMouse, std::memory_order_relaxed); + if (previousKeyboardMouse && !allowKeyboardMouse && m_InputHandler != nullptr) { + m_InputHandler->raiseAllKeys(); + m_InputHandler->raiseAllMouseButtons(); + } + + m_AllowGamepadInput.store(allowGamepad, std::memory_order_relaxed); + + if (reason != LI_SESSION_INPUT_POLICY_REASON_STREAM_START) { + notifyInputPermissionState(); + } +} + void Session::updateEffectiveAudioMuteState() { bool muted = m_ManualAudioMuted.load(std::memory_order_relaxed); @@ -1824,6 +1875,8 @@ bool Session::startConnectionAsync() return false; } + sendInputPermissionStateToHost(LI_SESSION_INPUT_POLICY_REASON_STREAM_START); + emit connectionStarted(); return true; } @@ -2160,6 +2213,15 @@ void Session::exec() m_InputHandler->setAdaptiveTriggers((uint16_t)(uintptr_t)event.user.data1, (DualSenseOutputReport *)event.user.data2); break; + case SDL_CODE_SET_INPUT_POLICY: { + const uintptr_t packed = (uintptr_t)event.user.data2; + const bool allowKeyboard = ((packed >> 24) & 0xFF) != 0; + const bool allowMouse = ((packed >> 16) & 0xFF) != 0; + const bool allowGamepad = ((packed >> 8) & 0xFF) != 0; + const uint8_t reason = (uint8_t)(packed & 0xFF); + applyHostInputPolicy(allowKeyboard, allowMouse, allowGamepad, reason); + break; + } case SDL_CODE_HIDE_STATUS_OVERLAY: if ((uint32_t)(uintptr_t)event.user.data1 == m_StatusOverlayGeneration.load(std::memory_order_relaxed) && m_MouseEmulationRefCount == 0) { diff --git a/app/streaming/session.h b/app/streaming/session.h index c0c01815..8e5bd35f 100644 --- a/app/streaming/session.h +++ b/app/streaming/session.h @@ -249,6 +249,9 @@ private: static void clSetAdaptiveTriggers(uint16_t controllerNumber, uint8_t eventFlags, uint8_t typeLeft, uint8_t typeRight, uint8_t *left, uint8_t *right); + static + void clSetInputPolicy(uint8_t allowKeyboard, uint8_t allowMouse, uint8_t allowGamepad, uint8_t reason); + static int arInit(int audioConfiguration, const POPUS_MULTISTREAM_CONFIGURATION opusConfig, @@ -273,6 +276,8 @@ private: Uint32 statusOverlayTimeoutCallback(Uint32 interval, void* param); void showTemporaryStatusOverlay(const char* text, Uint32 timeoutMs = 1500); + void applyHostInputPolicy(bool allowKeyboard, bool allowMouse, bool allowGamepad, uint8_t reason); + void sendInputPermissionStateToHost(uint8_t reason); void updateEffectiveAudioMuteState(); StreamingPreferences* m_Preferences; diff --git a/moonlight-common-c/moonlight-common-c b/moonlight-common-c/moonlight-common-c index 6250fa29..611a2e7f 160000 --- a/moonlight-common-c/moonlight-common-c +++ b/moonlight-common-c/moonlight-common-c @@ -1 +1 @@ -Subproject commit 6250fa29ee87873716045e3b64f1f229374324e8 +Subproject commit 611a2e7f8f6583d6d6aad30f0c8a02d6c07ab085