From 674220087fa9f85279a4e5e6f2f6b46971fb7157 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sun, 19 May 2019 13:10:42 -0700 Subject: [PATCH] Finish mouse emulation support with overlay and bugfixes --- app/streaming/input.cpp | 58 +++++++++++++++++++++++---------------- app/streaming/session.cpp | 22 +++++++++++++++ app/streaming/session.h | 3 ++ 3 files changed, 59 insertions(+), 24 deletions(-) diff --git a/app/streaming/input.cpp b/app/streaming/input.cpp index d6f67513..a0c4a567 100644 --- a/app/streaming/input.cpp +++ b/app/streaming/input.cpp @@ -5,6 +5,7 @@ #include "path.h" #include +#include #include #define VK_0 0x30 @@ -35,10 +36,10 @@ #define MOUSE_EMULATION_POLLING_INTERVAL 50 // Determines how fast the mouse will move each interval -#define MOUSE_EMULATION_MOTION_MULTIPLIER 6 +#define MOUSE_EMULATION_MOTION_MULTIPLIER 4 // Determines the maximum motion amount before allowing movement -#define MOUSE_EMULATION_DEADZONE 3 +#define MOUSE_EMULATION_DEADZONE 2 const int SdlInputHandler::k_ButtonMap[] = { A_FLAG, B_FLAG, X_FLAG, Y_FLAG, @@ -127,7 +128,10 @@ SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs, NvComputer*, int s SdlInputHandler::~SdlInputHandler() { for (int i = 0; i < MAX_GAMEPADS; i++) { - SDL_RemoveTimer(m_GamepadState[i].mouseEmulationTimer); + if (m_GamepadState[i].mouseEmulationTimer != 0) { + Session::get()->notifyMouseEmulationMode(false); + SDL_RemoveTimer(m_GamepadState[i].mouseEmulationTimer); + } if (m_GamepadState[i].haptic != nullptr) { SDL_HapticClose(m_GamepadState[i].haptic); } @@ -691,13 +695,9 @@ Uint32 SdlInputHandler::mouseEmulationTimerCallback(Uint32 interval, void *param float deltaX; float deltaY; - // Produce a base vector for mouse movement - deltaX = rawX / 32766.0f * MOUSE_EMULATION_MOTION_MULTIPLIER; - deltaY = rawY / 32766.0f * MOUSE_EMULATION_MOTION_MULTIPLIER; - - // Move faster as the stick moves further from center - deltaX *= qAbs(deltaX); - deltaY *= qAbs(deltaY); + // Produce a base vector for mouse movement with increased speed as we deviate further from center + deltaX = qPow(rawX / 32766.0f * MOUSE_EMULATION_MOTION_MULTIPLIER, 3); + deltaY = qPow(rawY / 32766.0f * MOUSE_EMULATION_MOTION_MULTIPLIER, 3); // Enforce deadzones deltaX = qAbs(deltaX) > MOUSE_EMULATION_DEADZONE ? deltaX - MOUSE_EMULATION_DEADZONE : 0; @@ -768,7 +768,10 @@ void SdlInputHandler::handleControllerAxisEvent(SDL_ControllerAxisEvent* event) SDL_PeepEvents(&nextEvent, 1, SDL_GETEVENT, SDL_CONTROLLERAXISMOTION, SDL_CONTROLLERAXISMOTION); } - sendGamepadState(state); + // Only send the gamepad state to the host if it's not in mouse emulation mode + if (state->mouseEmulationTimer == 0) { + sendGamepadState(state); + } } void SdlInputHandler::handleControllerButtonEvent(SDL_ControllerButtonEvent* event) @@ -779,6 +782,8 @@ void SdlInputHandler::handleControllerButtonEvent(SDL_ControllerButtonEvent* eve } if (event->state == SDL_PRESSED) { + state->buttons |= k_ButtonMap[event->button]; + if (event->button == SDL_CONTROLLER_BUTTON_START) { state->lastStartDownTime = SDL_GetTicks(); } @@ -804,23 +809,30 @@ void SdlInputHandler::handleControllerButtonEvent(SDL_ControllerButtonEvent* eve else if (event->button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) { LiSendScrollEvent(-1); } - - return; } } else { + state->buttons &= ~k_ButtonMap[event->button]; + if (event->button == SDL_CONTROLLER_BUTTON_START) { if (SDL_GetTicks() - state->lastStartDownTime > MOUSE_EMULATION_LONG_PRESS_TIME) { if (state->mouseEmulationTimer != 0) { SDL_RemoveTimer(state->mouseEmulationTimer); state->mouseEmulationTimer = 0; + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Mouse emulation deactivated"); + Session::get()->notifyMouseEmulationMode(false); } else { + // Send the start button up event to the host, since we won't do it below + sendGamepadState(state); + state->mouseEmulationTimer = SDL_AddTimer(MOUSE_EMULATION_POLLING_INTERVAL, SdlInputHandler::mouseEmulationTimerCallback, state); + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Mouse emulation active"); + Session::get()->notifyMouseEmulationMode(true); } } } @@ -840,18 +852,9 @@ void SdlInputHandler::handleControllerButtonEvent(SDL_ControllerButtonEvent* eve else if (event->button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) { LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_X2); } - - return; } } - if (event->state == SDL_PRESSED) { - state->buttons |= k_ButtonMap[event->button]; - } - else { - state->buttons &= ~k_ButtonMap[event->button]; - } - // Handle Start+Select+L1+R1 as a gamepad quit combo if (state->buttons == (PLAY_FLAG | BACK_FLAG | LB_FLAG | RB_FLAG)) { SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, @@ -869,7 +872,10 @@ void SdlInputHandler::handleControllerButtonEvent(SDL_ControllerButtonEvent* eve return; } - sendGamepadState(state); + // Only send the gamepad state to the host if it's not in mouse emulation mode + if (state->mouseEmulationTimer == 0) { + sendGamepadState(state); + } } void SdlInputHandler::handleControllerDeviceEvent(SDL_ControllerDeviceEvent* event) @@ -974,7 +980,11 @@ void SdlInputHandler::handleControllerDeviceEvent(SDL_ControllerDeviceEvent* eve else if (event->type == SDL_CONTROLLERDEVICEREMOVED) { state = findStateForGamepad(event->which); if (state != NULL) { - SDL_RemoveTimer(state->mouseEmulationTimer); + if (state->mouseEmulationTimer != 0) { + Session::get()->notifyMouseEmulationMode(false); + SDL_RemoveTimer(state->mouseEmulationTimer); + } + SDL_GameControllerClose(state->controller); if (state->haptic != nullptr) { SDL_HapticClose(state->haptic); diff --git a/app/streaming/session.cpp b/app/streaming/session.cpp index 0abcfc00..d8532b69 100644 --- a/app/streaming/session.cpp +++ b/app/streaming/session.cpp @@ -130,6 +130,11 @@ void Session::clConnectionStatusUpdate(int connectionStatus) return; } + if (s_ActiveSession->m_MouseEmulationRefCount > 0) { + // Don't display the overlay if mouse emulation is already using it + return; + } + switch (connectionStatus) { case CONN_STATUS_POOR: @@ -307,6 +312,7 @@ Session::Session(NvComputer* computer, NvApp& app, StreamingPreferences *prefere m_UnexpectedTermination(true), // Failure prior to streaming is unexpected m_InputHandler(nullptr), m_InputHandlerLock(0), + m_MouseEmulationRefCount(0), m_OpusDecoder(nullptr), m_AudioRenderer(nullptr), m_AudioSampleCount(0), @@ -825,6 +831,22 @@ void Session::toggleFullscreen() } } +void Session::notifyMouseEmulationMode(bool enabled) +{ + m_MouseEmulationRefCount += enabled ? 1 : -1; + SDL_assert(m_MouseEmulationRefCount >= 0); + + // We re-use the status update overlay for mouse mode notification + if (m_MouseEmulationRefCount > 0) { + strcpy(m_OverlayManager.getOverlayText(Overlay::OverlayStatusUpdate), "Gamepad mouse mode active\nLong press Start to deactivate"); + m_OverlayManager.setOverlayTextUpdated(Overlay::OverlayStatusUpdate); + m_OverlayManager.setOverlayState(Overlay::OverlayStatusUpdate, true); + } + else { + m_OverlayManager.setOverlayState(Overlay::OverlayStatusUpdate, false); + } +} + void Session::exec(int displayOriginX, int displayOriginY) { m_DisplayOriginX = displayOriginX; diff --git a/app/streaming/session.h b/app/streaming/session.h index 15d116c9..16805c56 100644 --- a/app/streaming/session.h +++ b/app/streaming/session.h @@ -77,6 +77,8 @@ private: void toggleFullscreen(); + void notifyMouseEmulationMode(bool enabled); + void updateOptimalWindowDisplayMode(); static @@ -141,6 +143,7 @@ private: bool m_UnexpectedTermination; SdlInputHandler* m_InputHandler; SDL_SpinLock m_InputHandlerLock; + int m_MouseEmulationRefCount; int m_ActiveVideoFormat; int m_ActiveVideoWidth;