mirror of
https://github.com/moonlight-stream/moonlight-qt.git
synced 2025-07-01 23:35:55 +00:00
Add controller rumble support.
This commit is contained in:
parent
4b7200ae69
commit
7b0596d9df
@ -81,6 +81,13 @@ SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs, NvComputer*, int s
|
|||||||
SDL_GetError());
|
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
|
// Initialize the gamepad mask with currently attached gamepads to avoid
|
||||||
// causing gamepads to unexpectedly disappear and reappear on the host
|
// causing gamepads to unexpectedly disappear and reappear on the host
|
||||||
// during stream startup as we detect currently attached gamepads one at a time.
|
// 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()
|
SdlInputHandler::~SdlInputHandler()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < MAX_GAMEPADS; i++) {
|
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) {
|
if (m_GamepadState[i].controller != nullptr) {
|
||||||
SDL_GameControllerClose(m_GamepadState[i].controller);
|
SDL_GameControllerClose(m_GamepadState[i].controller);
|
||||||
}
|
}
|
||||||
@ -109,6 +119,9 @@ SdlInputHandler::~SdlInputHandler()
|
|||||||
SDL_RemoveTimer(m_RightButtonReleaseTimer);
|
SDL_RemoveTimer(m_RightButtonReleaseTimer);
|
||||||
SDL_RemoveTimer(m_DragTimer);
|
SDL_RemoveTimer(m_DragTimer);
|
||||||
|
|
||||||
|
SDL_QuitSubSystem(SDL_INIT_HAPTIC);
|
||||||
|
SDL_assert(!SDL_WasInit(SDL_INIT_HAPTIC));
|
||||||
|
|
||||||
SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER);
|
SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER);
|
||||||
SDL_assert(!SDL_WasInit(SDL_INIT_GAMECONTROLLER));
|
SDL_assert(!SDL_WasInit(SDL_INIT_GAMECONTROLLER));
|
||||||
|
|
||||||
@ -706,6 +719,12 @@ void SdlInputHandler::handleControllerDeviceEvent(SDL_ControllerDeviceEvent* eve
|
|||||||
|
|
||||||
state->controller = controller;
|
state->controller = controller;
|
||||||
state->jsId = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(state->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)),
|
SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(SDL_GameControllerGetJoystick(state->controller)),
|
||||||
guidStr, sizeof(guidStr));
|
guidStr, sizeof(guidStr));
|
||||||
@ -740,6 +759,9 @@ void SdlInputHandler::handleControllerDeviceEvent(SDL_ControllerDeviceEvent* eve
|
|||||||
state = findStateForGamepad(event->which);
|
state = findStateForGamepad(event->which);
|
||||||
if (state != NULL) {
|
if (state != NULL) {
|
||||||
SDL_GameControllerClose(state->controller);
|
SDL_GameControllerClose(state->controller);
|
||||||
|
if (state->haptic != nullptr) {
|
||||||
|
SDL_HapticClose(state->haptic);
|
||||||
|
}
|
||||||
|
|
||||||
// Remove this from the gamepad mask in MC-mode
|
// Remove this from the gamepad mask in MC-mode
|
||||||
if (m_MultiController) {
|
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)
|
void SdlInputHandler::handleTouchFingerEvent(SDL_TouchFingerEvent* event)
|
||||||
{
|
{
|
||||||
int fingerIndex = -1;
|
int fingerIndex = -1;
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
struct GamepadState {
|
struct GamepadState {
|
||||||
SDL_GameController* controller;
|
SDL_GameController* controller;
|
||||||
SDL_JoystickID jsId;
|
SDL_JoystickID jsId;
|
||||||
|
SDL_Haptic* haptic;
|
||||||
|
int hapticEffectId;
|
||||||
short index;
|
short index;
|
||||||
|
|
||||||
short buttons;
|
short buttons;
|
||||||
@ -43,6 +45,8 @@ public:
|
|||||||
|
|
||||||
void handleJoystickArrivalEvent(SDL_JoyDeviceEvent* event);
|
void handleJoystickArrivalEvent(SDL_JoyDeviceEvent* event);
|
||||||
|
|
||||||
|
void rumble(unsigned short controllerNumber, unsigned short lowFreqMotor, unsigned short highFreqMotor);
|
||||||
|
|
||||||
void handleTouchFingerEvent(SDL_TouchFingerEvent* event);
|
void handleTouchFingerEvent(SDL_TouchFingerEvent* event);
|
||||||
|
|
||||||
int getAttachedGamepadMask();
|
int getAttachedGamepadMask();
|
||||||
|
@ -38,7 +38,8 @@ CONNECTION_LISTENER_CALLBACKS Session::k_ConnCallbacks = {
|
|||||||
Session::clConnectionTerminated,
|
Session::clConnectionTerminated,
|
||||||
nullptr,
|
nullptr,
|
||||||
nullptr,
|
nullptr,
|
||||||
Session::clLogMessage
|
Session::clLogMessage,
|
||||||
|
Session::clRumble
|
||||||
};
|
};
|
||||||
|
|
||||||
AUDIO_RENDERER_CALLBACKS Session::k_AudioCallbacks = {
|
AUDIO_RENDERER_CALLBACKS Session::k_AudioCallbacks = {
|
||||||
@ -102,6 +103,11 @@ void Session::clLogMessage(const char* format, ...)
|
|||||||
va_end(ap);
|
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)
|
#define CALL_INITIALIZE(dec) (dec)->initialize(vds, window, videoFormat, width, height, frameRate, enableVsync, enableFramePacing)
|
||||||
|
|
||||||
bool Session::chooseDecoder(StreamingPreferences::VideoDecoderSelection vds,
|
bool Session::chooseDecoder(StreamingPreferences::VideoDecoderSelection vds,
|
||||||
@ -295,7 +301,8 @@ Session::Session(NvComputer* computer, NvApp& app, StreamingPreferences *prefere
|
|||||||
m_DisplayOriginX(0),
|
m_DisplayOriginX(0),
|
||||||
m_DisplayOriginY(0),
|
m_DisplayOriginY(0),
|
||||||
m_PendingWindowedTransition(false),
|
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_OpusDecoder(nullptr),
|
||||||
m_AudioRenderer(nullptr),
|
m_AudioRenderer(nullptr),
|
||||||
m_AudioSampleCount(0)
|
m_AudioSampleCount(0)
|
||||||
@ -308,6 +315,8 @@ Session::~Session()
|
|||||||
// and the object is deallocated.
|
// and the object is deallocated.
|
||||||
s_ActiveSessionSemaphore.acquire();
|
s_ActiveSessionSemaphore.acquire();
|
||||||
s_ActiveSessionSemaphore.release();
|
s_ActiveSessionSemaphore.release();
|
||||||
|
|
||||||
|
delete m_InputHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::initialize()
|
void Session::initialize()
|
||||||
@ -809,9 +818,9 @@ void Session::exec(int displayOriginX, int displayOriginY)
|
|||||||
|
|
||||||
// Initialize the gamepad code with our preferences
|
// Initialize the gamepad code with our preferences
|
||||||
StreamingPreferences prefs;
|
StreamingPreferences prefs;
|
||||||
SdlInputHandler inputHandler(prefs, m_Computer,
|
m_InputHandler = new SdlInputHandler(prefs, m_Computer,
|
||||||
m_StreamConfig.width,
|
m_StreamConfig.width,
|
||||||
m_StreamConfig.height);
|
m_StreamConfig.height);
|
||||||
|
|
||||||
// The UI should have ensured the old game was already quit
|
// The UI should have ensured the old game was already quit
|
||||||
// if we decide to stream a different game.
|
// 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,
|
http.launchApp(m_App.id, &m_StreamConfig,
|
||||||
enableGameOptimizations,
|
enableGameOptimizations,
|
||||||
prefs.playAudioOnHost,
|
prefs.playAudioOnHost,
|
||||||
inputHandler.getAttachedGamepadMask());
|
m_InputHandler->getAttachedGamepadMask());
|
||||||
}
|
}
|
||||||
} catch (const GfeHttpResponseException& e) {
|
} catch (const GfeHttpResponseException& e) {
|
||||||
emit displayLaunchError(e.toQString());
|
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
|
// 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.
|
// 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.
|
// 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_KEYUP:
|
||||||
case SDL_KEYDOWN:
|
case SDL_KEYDOWN:
|
||||||
inputHandler.handleKeyEvent(&event.key);
|
m_InputHandler->handleKeyEvent(&event.key);
|
||||||
break;
|
break;
|
||||||
case SDL_MOUSEBUTTONDOWN:
|
case SDL_MOUSEBUTTONDOWN:
|
||||||
case SDL_MOUSEBUTTONUP:
|
case SDL_MOUSEBUTTONUP:
|
||||||
inputHandler.handleMouseButtonEvent(&event.button);
|
m_InputHandler->handleMouseButtonEvent(&event.button);
|
||||||
break;
|
break;
|
||||||
case SDL_MOUSEMOTION:
|
case SDL_MOUSEMOTION:
|
||||||
inputHandler.handleMouseMotionEvent(&event.motion);
|
m_InputHandler->handleMouseMotionEvent(&event.motion);
|
||||||
break;
|
break;
|
||||||
case SDL_MOUSEWHEEL:
|
case SDL_MOUSEWHEEL:
|
||||||
inputHandler.handleMouseWheelEvent(&event.wheel);
|
m_InputHandler->handleMouseWheelEvent(&event.wheel);
|
||||||
break;
|
break;
|
||||||
case SDL_CONTROLLERAXISMOTION:
|
case SDL_CONTROLLERAXISMOTION:
|
||||||
inputHandler.handleControllerAxisEvent(&event.caxis);
|
m_InputHandler->handleControllerAxisEvent(&event.caxis);
|
||||||
break;
|
break;
|
||||||
case SDL_CONTROLLERBUTTONDOWN:
|
case SDL_CONTROLLERBUTTONDOWN:
|
||||||
case SDL_CONTROLLERBUTTONUP:
|
case SDL_CONTROLLERBUTTONUP:
|
||||||
inputHandler.handleControllerButtonEvent(&event.cbutton);
|
m_InputHandler->handleControllerButtonEvent(&event.cbutton);
|
||||||
break;
|
break;
|
||||||
case SDL_CONTROLLERDEVICEADDED:
|
case SDL_CONTROLLERDEVICEADDED:
|
||||||
case SDL_CONTROLLERDEVICEREMOVED:
|
case SDL_CONTROLLERDEVICEREMOVED:
|
||||||
inputHandler.handleControllerDeviceEvent(&event.cdevice);
|
m_InputHandler->handleControllerDeviceEvent(&event.cdevice);
|
||||||
break;
|
break;
|
||||||
case SDL_JOYDEVICEADDED:
|
case SDL_JOYDEVICEADDED:
|
||||||
inputHandler.handleJoystickArrivalEvent(&event.jdevice);
|
m_InputHandler->handleJoystickArrivalEvent(&event.jdevice);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// SDL2 sends touch events from trackpads by default on
|
// 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_FINGERDOWN:
|
||||||
case SDL_FINGERMOTION:
|
case SDL_FINGERMOTION:
|
||||||
case SDL_FINGERUP:
|
case SDL_FINGERUP:
|
||||||
inputHandler.handleTouchFingerEvent(&event.tfinger);
|
m_InputHandler->handleTouchFingerEvent(&event.tfinger);
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -1208,7 +1217,7 @@ DispatchDeferredCleanup:
|
|||||||
SDL_SetHint(SDL_HINT_TIMER_RESOLUTION, "0");
|
SDL_SetHint(SDL_HINT_TIMER_RESOLUTION, "0");
|
||||||
|
|
||||||
// Raise any keys that are still down
|
// Raise any keys that are still down
|
||||||
inputHandler.raiseAllKeys();
|
m_InputHandler->raiseAllKeys();
|
||||||
|
|
||||||
// Destroy the decoder, since this must be done on the main thread
|
// Destroy the decoder, since this must be done on the main thread
|
||||||
SDL_AtomicLock(&m_DecoderLock);
|
SDL_AtomicLock(&m_DecoderLock);
|
||||||
|
@ -97,6 +97,9 @@ private:
|
|||||||
static
|
static
|
||||||
void clLogMessage(const char* format, ...);
|
void clLogMessage(const char* format, ...);
|
||||||
|
|
||||||
|
static
|
||||||
|
void clRumble(unsigned short controllerNumber, unsigned short lowFreqMotor, unsigned short highFreqMotor);
|
||||||
|
|
||||||
static
|
static
|
||||||
int arInit(int audioConfiguration,
|
int arInit(int audioConfiguration,
|
||||||
const POPUS_MULTISTREAM_CONFIGURATION opusConfig,
|
const POPUS_MULTISTREAM_CONFIGURATION opusConfig,
|
||||||
@ -132,6 +135,7 @@ private:
|
|||||||
int m_DisplayOriginY;
|
int m_DisplayOriginY;
|
||||||
bool m_PendingWindowedTransition;
|
bool m_PendingWindowedTransition;
|
||||||
bool m_UnexpectedTermination;
|
bool m_UnexpectedTermination;
|
||||||
|
SdlInputHandler* m_InputHandler;
|
||||||
|
|
||||||
int m_ActiveVideoFormat;
|
int m_ActiveVideoFormat;
|
||||||
int m_ActiveVideoWidth;
|
int m_ActiveVideoWidth;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user