mirror of
https://github.com/moonlight-stream/moonlight-qt
synced 2024-11-10 13:44:17 +00:00
Add controller rumble support.
This commit is contained in:
parent
4b7200ae69
commit
7b0596d9df
4 changed files with 81 additions and 17 deletions
|
@ -81,6 +81,13 @@ SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs, NvComputer*, int s
|
|||
SDL_GetError());
|
||||
}
|
||||
|
||||
SDL_assert(!SDL_WasInit(SDL_INIT_HAPTIC));
|
||||
if (SDL_InitSubSystem(SDL_INIT_HAPTIC) != 0) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"SDL_InitSubSystem(SDL_INIT_HAPTIC) failed: %s",
|
||||
SDL_GetError());
|
||||
}
|
||||
|
||||
// Initialize the gamepad mask with currently attached gamepads to avoid
|
||||
// causing gamepads to unexpectedly disappear and reappear on the host
|
||||
// during stream startup as we detect currently attached gamepads one at a time.
|
||||
|
@ -99,6 +106,9 @@ SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs, NvComputer*, int s
|
|||
SdlInputHandler::~SdlInputHandler()
|
||||
{
|
||||
for (int i = 0; i < MAX_GAMEPADS; i++) {
|
||||
if (m_GamepadState[i].haptic != nullptr) {
|
||||
SDL_HapticClose(m_GamepadState[i].haptic);
|
||||
}
|
||||
if (m_GamepadState[i].controller != nullptr) {
|
||||
SDL_GameControllerClose(m_GamepadState[i].controller);
|
||||
}
|
||||
|
@ -109,6 +119,9 @@ SdlInputHandler::~SdlInputHandler()
|
|||
SDL_RemoveTimer(m_RightButtonReleaseTimer);
|
||||
SDL_RemoveTimer(m_DragTimer);
|
||||
|
||||
SDL_QuitSubSystem(SDL_INIT_HAPTIC);
|
||||
SDL_assert(!SDL_WasInit(SDL_INIT_HAPTIC));
|
||||
|
||||
SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER);
|
||||
SDL_assert(!SDL_WasInit(SDL_INIT_GAMECONTROLLER));
|
||||
|
||||
|
@ -706,6 +719,12 @@ void SdlInputHandler::handleControllerDeviceEvent(SDL_ControllerDeviceEvent* eve
|
|||
|
||||
state->controller = controller;
|
||||
state->jsId = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(state->controller));
|
||||
state->haptic = SDL_HapticOpenFromJoystick(SDL_GameControllerGetJoystick(state->controller));
|
||||
state->hapticEffectId = -1;
|
||||
if (state->haptic != nullptr && (SDL_HapticQuery(state->haptic) & SDL_HAPTIC_LEFTRIGHT) == 0) {
|
||||
SDL_HapticClose(state->haptic);
|
||||
state->haptic = nullptr;
|
||||
}
|
||||
|
||||
SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(SDL_GameControllerGetJoystick(state->controller)),
|
||||
guidStr, sizeof(guidStr));
|
||||
|
@ -740,6 +759,9 @@ void SdlInputHandler::handleControllerDeviceEvent(SDL_ControllerDeviceEvent* eve
|
|||
state = findStateForGamepad(event->which);
|
||||
if (state != NULL) {
|
||||
SDL_GameControllerClose(state->controller);
|
||||
if (state->haptic != nullptr) {
|
||||
SDL_HapticClose(state->haptic);
|
||||
}
|
||||
|
||||
// Remove this from the gamepad mask in MC-mode
|
||||
if (m_MultiController) {
|
||||
|
@ -793,6 +815,31 @@ void SdlInputHandler::handleJoystickArrivalEvent(SDL_JoyDeviceEvent* event)
|
|||
}
|
||||
}
|
||||
|
||||
void SdlInputHandler::rumble(unsigned short controllerNumber, unsigned short lowFreqMotor, unsigned short highFreqMotor)
|
||||
{
|
||||
SDL_Haptic* haptic = m_GamepadState[controllerNumber].haptic;
|
||||
SDL_HapticEffect effect;
|
||||
|
||||
if (haptic == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_memset(&effect, 0, sizeof(effect));
|
||||
effect.type = SDL_HAPTIC_LEFTRIGHT;
|
||||
effect.leftright.large_magnitude = lowFreqMotor;
|
||||
effect.leftright.small_magnitude = highFreqMotor;
|
||||
effect.leftright.length = 10000; // Choose 10 second max duration - can be cancelled sooner.
|
||||
|
||||
if (m_GamepadState[controllerNumber].hapticEffectId >= 0) {
|
||||
SDL_HapticDestroyEffect(haptic, m_GamepadState[controllerNumber].hapticEffectId);
|
||||
}
|
||||
|
||||
m_GamepadState[controllerNumber].hapticEffectId = SDL_HapticNewEffect(haptic, &effect);
|
||||
if (m_GamepadState[controllerNumber].hapticEffectId >= 0) {
|
||||
SDL_HapticRunEffect(haptic, m_GamepadState[controllerNumber].hapticEffectId, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void SdlInputHandler::handleTouchFingerEvent(SDL_TouchFingerEvent* event)
|
||||
{
|
||||
int fingerIndex = -1;
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
struct GamepadState {
|
||||
SDL_GameController* controller;
|
||||
SDL_JoystickID jsId;
|
||||
SDL_Haptic* haptic;
|
||||
int hapticEffectId;
|
||||
short index;
|
||||
|
||||
short buttons;
|
||||
|
@ -43,6 +45,8 @@ public:
|
|||
|
||||
void handleJoystickArrivalEvent(SDL_JoyDeviceEvent* event);
|
||||
|
||||
void rumble(unsigned short controllerNumber, unsigned short lowFreqMotor, unsigned short highFreqMotor);
|
||||
|
||||
void handleTouchFingerEvent(SDL_TouchFingerEvent* event);
|
||||
|
||||
int getAttachedGamepadMask();
|
||||
|
|
|
@ -38,7 +38,8 @@ CONNECTION_LISTENER_CALLBACKS Session::k_ConnCallbacks = {
|
|||
Session::clConnectionTerminated,
|
||||
nullptr,
|
||||
nullptr,
|
||||
Session::clLogMessage
|
||||
Session::clLogMessage,
|
||||
Session::clRumble
|
||||
};
|
||||
|
||||
AUDIO_RENDERER_CALLBACKS Session::k_AudioCallbacks = {
|
||||
|
@ -102,6 +103,11 @@ void Session::clLogMessage(const char* format, ...)
|
|||
va_end(ap);
|
||||
}
|
||||
|
||||
void Session::clRumble(unsigned short controllerNumber, unsigned short lowFreqMotor, unsigned short highFreqMotor)
|
||||
{
|
||||
s_ActiveSession->m_InputHandler->rumble(controllerNumber, lowFreqMotor, highFreqMotor);
|
||||
}
|
||||
|
||||
#define CALL_INITIALIZE(dec) (dec)->initialize(vds, window, videoFormat, width, height, frameRate, enableVsync, enableFramePacing)
|
||||
|
||||
bool Session::chooseDecoder(StreamingPreferences::VideoDecoderSelection vds,
|
||||
|
@ -295,7 +301,8 @@ Session::Session(NvComputer* computer, NvApp& app, StreamingPreferences *prefere
|
|||
m_DisplayOriginX(0),
|
||||
m_DisplayOriginY(0),
|
||||
m_PendingWindowedTransition(false),
|
||||
m_UnexpectedTermination(true), // Failure prior to streaming is unexpected
|
||||
m_UnexpectedTermination(true), // Failure prior to streaming is unexpected
|
||||
m_InputHandler(nullptr),
|
||||
m_OpusDecoder(nullptr),
|
||||
m_AudioRenderer(nullptr),
|
||||
m_AudioSampleCount(0)
|
||||
|
@ -308,6 +315,8 @@ Session::~Session()
|
|||
// and the object is deallocated.
|
||||
s_ActiveSessionSemaphore.acquire();
|
||||
s_ActiveSessionSemaphore.release();
|
||||
|
||||
delete m_InputHandler;
|
||||
}
|
||||
|
||||
void Session::initialize()
|
||||
|
@ -809,9 +818,9 @@ void Session::exec(int displayOriginX, int displayOriginY)
|
|||
|
||||
// Initialize the gamepad code with our preferences
|
||||
StreamingPreferences prefs;
|
||||
SdlInputHandler inputHandler(prefs, m_Computer,
|
||||
m_StreamConfig.width,
|
||||
m_StreamConfig.height);
|
||||
m_InputHandler = new SdlInputHandler(prefs, m_Computer,
|
||||
m_StreamConfig.width,
|
||||
m_StreamConfig.height);
|
||||
|
||||
// The UI should have ensured the old game was already quit
|
||||
// if we decide to stream a different game.
|
||||
|
@ -842,7 +851,7 @@ void Session::exec(int displayOriginX, int displayOriginY)
|
|||
http.launchApp(m_App.id, &m_StreamConfig,
|
||||
enableGameOptimizations,
|
||||
prefs.playAudioOnHost,
|
||||
inputHandler.getAttachedGamepadMask());
|
||||
m_InputHandler->getAttachedGamepadMask());
|
||||
}
|
||||
} catch (const GfeHttpResponseException& e) {
|
||||
emit displayLaunchError(e.toQString());
|
||||
|
@ -1070,7 +1079,7 @@ void Session::exec(int displayOriginX, int displayOriginY)
|
|||
|
||||
// Raise all keys that are currently pressed. If we don't do this, certain keys
|
||||
// used in shortcuts that cause focus loss (such as Alt+Tab) may get stuck down.
|
||||
inputHandler.raiseAllKeys();
|
||||
m_InputHandler->raiseAllKeys();
|
||||
}
|
||||
|
||||
// We want to recreate the decoder for resizes (full-screen toggles) and the initial shown event.
|
||||
|
@ -1160,31 +1169,31 @@ void Session::exec(int displayOriginX, int displayOriginY)
|
|||
|
||||
case SDL_KEYUP:
|
||||
case SDL_KEYDOWN:
|
||||
inputHandler.handleKeyEvent(&event.key);
|
||||
m_InputHandler->handleKeyEvent(&event.key);
|
||||
break;
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
inputHandler.handleMouseButtonEvent(&event.button);
|
||||
m_InputHandler->handleMouseButtonEvent(&event.button);
|
||||
break;
|
||||
case SDL_MOUSEMOTION:
|
||||
inputHandler.handleMouseMotionEvent(&event.motion);
|
||||
m_InputHandler->handleMouseMotionEvent(&event.motion);
|
||||
break;
|
||||
case SDL_MOUSEWHEEL:
|
||||
inputHandler.handleMouseWheelEvent(&event.wheel);
|
||||
m_InputHandler->handleMouseWheelEvent(&event.wheel);
|
||||
break;
|
||||
case SDL_CONTROLLERAXISMOTION:
|
||||
inputHandler.handleControllerAxisEvent(&event.caxis);
|
||||
m_InputHandler->handleControllerAxisEvent(&event.caxis);
|
||||
break;
|
||||
case SDL_CONTROLLERBUTTONDOWN:
|
||||
case SDL_CONTROLLERBUTTONUP:
|
||||
inputHandler.handleControllerButtonEvent(&event.cbutton);
|
||||
m_InputHandler->handleControllerButtonEvent(&event.cbutton);
|
||||
break;
|
||||
case SDL_CONTROLLERDEVICEADDED:
|
||||
case SDL_CONTROLLERDEVICEREMOVED:
|
||||
inputHandler.handleControllerDeviceEvent(&event.cdevice);
|
||||
m_InputHandler->handleControllerDeviceEvent(&event.cdevice);
|
||||
break;
|
||||
case SDL_JOYDEVICEADDED:
|
||||
inputHandler.handleJoystickArrivalEvent(&event.jdevice);
|
||||
m_InputHandler->handleJoystickArrivalEvent(&event.jdevice);
|
||||
break;
|
||||
|
||||
// SDL2 sends touch events from trackpads by default on
|
||||
|
@ -1194,7 +1203,7 @@ void Session::exec(int displayOriginX, int displayOriginY)
|
|||
case SDL_FINGERDOWN:
|
||||
case SDL_FINGERMOTION:
|
||||
case SDL_FINGERUP:
|
||||
inputHandler.handleTouchFingerEvent(&event.tfinger);
|
||||
m_InputHandler->handleTouchFingerEvent(&event.tfinger);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
@ -1208,7 +1217,7 @@ DispatchDeferredCleanup:
|
|||
SDL_SetHint(SDL_HINT_TIMER_RESOLUTION, "0");
|
||||
|
||||
// Raise any keys that are still down
|
||||
inputHandler.raiseAllKeys();
|
||||
m_InputHandler->raiseAllKeys();
|
||||
|
||||
// Destroy the decoder, since this must be done on the main thread
|
||||
SDL_AtomicLock(&m_DecoderLock);
|
||||
|
|
|
@ -97,6 +97,9 @@ private:
|
|||
static
|
||||
void clLogMessage(const char* format, ...);
|
||||
|
||||
static
|
||||
void clRumble(unsigned short controllerNumber, unsigned short lowFreqMotor, unsigned short highFreqMotor);
|
||||
|
||||
static
|
||||
int arInit(int audioConfiguration,
|
||||
const POPUS_MULTISTREAM_CONFIGURATION opusConfig,
|
||||
|
@ -132,6 +135,7 @@ private:
|
|||
int m_DisplayOriginY;
|
||||
bool m_PendingWindowedTransition;
|
||||
bool m_UnexpectedTermination;
|
||||
SdlInputHandler* m_InputHandler;
|
||||
|
||||
int m_ActiveVideoFormat;
|
||||
int m_ActiveVideoWidth;
|
||||
|
|
Loading…
Reference in a new issue