diff --git a/app/streaming/input/gamepad.cpp b/app/streaming/input/gamepad.cpp index 435c9d2f..e0d7f004 100644 --- a/app/streaming/input/gamepad.cpp +++ b/app/streaming/input/gamepad.cpp @@ -900,6 +900,22 @@ void SdlInputHandler::setControllerLED(uint16_t controllerNumber, uint8_t r, uin #endif } +void SdlInputHandler::setAdaptiveTriggers(uint16_t controllerNumber, DualSenseOutputReport *report){ + +#if SDL_VERSION_ATLEAST(2, 0, 16) + // Make sure the controller number is within our supported count + if (controllerNumber <= MAX_GAMEPADS && + // and we have a valid controller + m_GamepadState[controllerNumber].controller != nullptr && + // and it's a PS5 controller + SDL_GameControllerGetType(m_GamepadState[controllerNumber].controller) == SDL_CONTROLLER_TYPE_PS5) { + SDL_GameControllerSendEffect(m_GamepadState[controllerNumber].controller, report, sizeof(*report)); + } +#endif + + SDL_free(report); +} + QString SdlInputHandler::getUnmappedGamepads() { QString ret; diff --git a/app/streaming/input/input.h b/app/streaming/input/input.h index 701e19cf..baa949fd 100644 --- a/app/streaming/input/input.h +++ b/app/streaming/input/input.h @@ -38,6 +38,37 @@ struct GamepadState { unsigned char lt, rt; }; + +struct DualSenseOutputReport{ + uint8_t validFlag0; + uint8_t validFlag1; + + /* For DualShock 4 compatibility mode. */ + uint8_t motorRight; + uint8_t motorLeft; + + /* Audio controls */ + uint8_t reserved[4]; + uint8_t muteButtonLed; + + uint8_t powerSaveControl; + uint8_t rightTriggerEffectType; + uint8_t rightTriggerEffect[DS_EFFECT_PAYLOAD_SIZE]; + uint8_t leftTriggerEffectType; + uint8_t leftTriggerEffect[DS_EFFECT_PAYLOAD_SIZE]; + uint8_t reserved2[6]; + + /* LEDs and lightbar */ + uint8_t validFlag2; + uint8_t reserved3[2]; + uint8_t lightbarSetup; + uint8_t ledBrightness; + uint8_t playerLeds; + uint8_t lightbarRed; + uint8_t lightbarGreen; + uint8_t lightbarBlue; +}; + // activeGamepadMask is a short, so we're bounded by the number of mask bits #define MAX_GAMEPADS 16 @@ -95,6 +126,8 @@ public: void setControllerLED(uint16_t controllerNumber, uint8_t r, uint8_t g, uint8_t b); + void setAdaptiveTriggers(uint16_t controllerNumber, DualSenseOutputReport *report); + void handleTouchFingerEvent(SDL_TouchFingerEvent* event); int getAttachedGamepadMask(); diff --git a/app/streaming/session.cpp b/app/streaming/session.cpp index 6da51f16..f7a1b5f2 100644 --- a/app/streaming/session.cpp +++ b/app/streaming/session.cpp @@ -40,6 +40,7 @@ #define SDL_CODE_GAMECONTROLLER_RUMBLE_TRIGGERS 102 #define SDL_CODE_GAMECONTROLLER_SET_MOTION_EVENT_STATE 103 #define SDL_CODE_GAMECONTROLLER_SET_CONTROLLER_LED 104 +#define SDL_CODE_GAMECONTROLLER_SET_ADAPTIVE_TRIGGERS 105 #include @@ -69,6 +70,7 @@ CONNECTION_LISTENER_CALLBACKS Session::k_ConnCallbacks = { Session::clRumbleTriggers, Session::clSetMotionEventState, Session::clSetControllerLED, + Session::clSetAdaptiveTriggers }; Session* Session::s_ActiveSession; @@ -259,6 +261,30 @@ void Session::clSetControllerLED(uint16_t controllerNumber, uint8_t r, uint8_t g SDL_PushEvent(&setControllerLEDEvent); } +void Session::clSetAdaptiveTriggers(uint16_t controllerNumber, uint8_t eventFlags, uint8_t typeLeft, uint8_t typeRight, uint8_t *left, uint8_t *right){ + // We push an event for the main thread to handle in order to properly synchronize + // with the removal of game controllers that could result in our game controller + // going away during this callback. + SDL_Event setControllerLEDEvent = {}; + setControllerLEDEvent.type = SDL_USEREVENT; + setControllerLEDEvent.user.code = SDL_CODE_GAMECONTROLLER_SET_ADAPTIVE_TRIGGERS; + setControllerLEDEvent.user.data1 = (void*)(uintptr_t)controllerNumber; + + // Based on the following SDL code: + // https://github.com/libsdl-org/SDL/blob/120c76c84bbce4c1bfed4e9eb74e10678bd83120/test/testgamecontroller.c#L286-L307 + DualSenseOutputReport *state = (DualSenseOutputReport *) SDL_malloc(sizeof(DualSenseOutputReport)); + SDL_zero(*state); + state->validFlag0 = (eventFlags & DS_EFFECT_RIGHT_TRIGGER) | (eventFlags & DS_EFFECT_LEFT_TRIGGER); + state->rightTriggerEffectType = typeRight; + SDL_memcpy(state->rightTriggerEffect, right, sizeof(state->rightTriggerEffect)); + state->leftTriggerEffectType = typeLeft; + SDL_memcpy(state->leftTriggerEffect, left, sizeof(state->leftTriggerEffect)); + + setControllerLEDEvent.user.data2 = (void *) state; + SDL_PushEvent(&setControllerLEDEvent); +} + + bool Session::chooseDecoder(StreamingPreferences::VideoDecoderSelection vds, SDL_Window* window, int videoFormat, int width, int height, int frameRate, bool enableVsync, bool enableFramePacing, bool testOnly, IVideoDecoder*& chosenDecoder) @@ -2041,6 +2067,10 @@ void Session::execInternal() (uint8_t)((uintptr_t)event.user.data2 >> 8), (uint8_t)((uintptr_t)event.user.data2)); break; + case SDL_CODE_GAMECONTROLLER_SET_ADAPTIVE_TRIGGERS: + m_InputHandler->setAdaptiveTriggers((uint16_t)(uintptr_t)event.user.data1, + (DualSenseOutputReport *)event.user.data2); + break; default: SDL_assert(false); } diff --git a/app/streaming/session.h b/app/streaming/session.h index 95858437..2a0e77e0 100644 --- a/app/streaming/session.h +++ b/app/streaming/session.h @@ -220,6 +220,9 @@ private: static void clSetControllerLED(uint16_t controllerNumber, uint8_t r, uint8_t g, uint8_t b); + static + void clSetAdaptiveTriggers(uint16_t controllerNumber, uint8_t eventFlags, uint8_t typeLeft, uint8_t typeRight, uint8_t *left, uint8_t *right); + static int arInit(int audioConfiguration, const POPUS_MULTISTREAM_CONFIGURATION opusConfig, diff --git a/moonlight-common-c/moonlight-common-c b/moonlight-common-c/moonlight-common-c index 8599b604..e95feaf4 160000 --- a/moonlight-common-c/moonlight-common-c +++ b/moonlight-common-c/moonlight-common-c @@ -1 +1 @@ -Subproject commit 8599b6042a4ba27749b0f94134dd614b4328a9bc +Subproject commit e95feaf4951b8dc774671a5d6a1c31d76d78e3ac