From 6dd68273438e979590e8211d8a85cd785c9d4bd3 Mon Sep 17 00:00:00 2001 From: Max Maisel Date: Thu, 7 Sep 2023 17:18:12 +0200 Subject: [PATCH] Translate steam deck HID reports to SDL events. --- src/joystick/hidapi/SDL_hidapi_steamdeck.c | 121 +++++++++++++++++- .../hidapi/steam/controller_constants.h | 31 +++++ .../hidapi/steam/controller_structs.h | 62 +++++++++ 3 files changed, 213 insertions(+), 1 deletion(-) diff --git a/src/joystick/hidapi/SDL_hidapi_steamdeck.c b/src/joystick/hidapi/SDL_hidapi_steamdeck.c index 02566e94b..a8bce66bc 100644 --- a/src/joystick/hidapi/SDL_hidapi_steamdeck.c +++ b/src/joystick/hidapi/SDL_hidapi_steamdeck.c @@ -31,9 +31,21 @@ #include +#define bool SDL_bool +#define true SDL_TRUE +#define false SDL_FALSE + +typedef uint32_t uint32; +typedef uint64_t uint64; + +#include "steam/controller_constants.h" +#include "steam/controller_structs.h" + typedef struct { Uint32 update_rate_us; + Uint32 sensor_timestamp_us; + Uint64 last_button_state; } SDL_DriverSteamDeck_Context; /*****************************************************************************************************/ @@ -109,7 +121,114 @@ static void HIDAPI_DriverSteamDeck_SetDevicePlayerIndex(SDL_HIDAPI_Device *devic static SDL_bool HIDAPI_DriverSteamDeck_UpdateDevice(SDL_HIDAPI_Device *device) { - return SDL_FALSE; + SDL_DriverSteamDeck_Context *ctx = (SDL_DriverSteamDeck_Context *)device->context; + SDL_Joystick *joystick = NULL; + int r; + uint8_t data[64]; + float values[3]; + ValveInReport_t *pInReport = (ValveInReport_t *)data; + + if (device->num_joysticks > 0) { + joystick = SDL_GetJoystickFromInstanceID(device->joysticks[0]); + if (joystick == NULL) { + return SDL_FALSE; + } + } else { + return SDL_FALSE; + } + + SDL_memset(data, 0, sizeof(data)); + r = SDL_hid_read(device->dev, data, sizeof(data)); + if (r == 0) { + return SDL_FALSE; + } else if (r <= 0) { + /* Failed to read from controller */ + HIDAPI_JoystickDisconnected(device, device->joysticks[0]); + return SDL_FALSE; + } + + if (!(r == 64 && pInReport->header.unReportVersion == k_ValveInReportMsgVersion && pInReport->header.ucType == ID_CONTROLLER_DECK_STATE && pInReport->header.ucLength == 64)) { + return SDL_FALSE; + } + + Uint64 timestamp = SDL_GetTicksNS(); + + if (pInReport->payload.deckState.ulButtons != ctx->last_button_state) { + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SOUTH, + (pInReport->payload.deckState.ulButtonsL & STEAMDECK_LBUTTON_A) ? SDL_PRESSED : SDL_RELEASED); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_EAST, + (pInReport->payload.deckState.ulButtonsL & STEAMDECK_LBUTTON_B) ? SDL_PRESSED : SDL_RELEASED); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_WEST, + (pInReport->payload.deckState.ulButtonsL & STEAMDECK_LBUTTON_X) ? SDL_PRESSED : SDL_RELEASED); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_NORTH, + (pInReport->payload.deckState.ulButtonsL & STEAMDECK_LBUTTON_Y) ? SDL_PRESSED : SDL_RELEASED); + + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, + (pInReport->payload.deckState.ulButtonsL & STEAMDECK_LBUTTON_LT) ? SDL_PRESSED : SDL_RELEASED); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, + (pInReport->payload.deckState.ulButtonsL & STEAMDECK_LBUTTON_RT) ? SDL_PRESSED : SDL_RELEASED); + + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, + (pInReport->payload.deckState.ulButtonsL & STEAMDECK_LBUTTON_SELECT) ? SDL_PRESSED : SDL_RELEASED); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, + (pInReport->payload.deckState.ulButtonsL & STEAMDECK_LBUTTON_START) ? SDL_PRESSED : SDL_RELEASED); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, + (pInReport->payload.deckState.ulButtonsL & STEAMDECK_LBUTTON_MODE) ? SDL_PRESSED : SDL_RELEASED); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_MISC1, + (pInReport->payload.deckState.ulButtonsH & STEAMDECK_HBUTTON_BASE) ? SDL_PRESSED : SDL_RELEASED); + + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, + (pInReport->payload.deckState.ulButtonsL & STEAMDECK_LBUTTON_STICKL) ? SDL_PRESSED : SDL_RELEASED); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, + (pInReport->payload.deckState.ulButtonsL & STEAMDECK_LBUTTON_STICKR) ? SDL_PRESSED : SDL_RELEASED); + + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1, + (pInReport->payload.deckState.ulButtonsH & STEAMDECK_HBUTTON_PADDLE1) ? SDL_PRESSED : SDL_RELEASED); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_PADDLE1, + (pInReport->payload.deckState.ulButtonsH & STEAMDECK_HBUTTON_PADDLE2) ? SDL_PRESSED : SDL_RELEASED); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2, + (pInReport->payload.deckState.ulButtonsL & STEAMDECK_LBUTTON_PADDLE3) ? SDL_PRESSED : SDL_RELEASED); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_PADDLE2, + (pInReport->payload.deckState.ulButtonsL & STEAMDECK_LBUTTON_PADDLE4) ? SDL_PRESSED : SDL_RELEASED); + + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_DPAD_UP, + (pInReport->payload.deckState.ulButtonsL & STEAMDECK_LBUTTON_DPAD_UP) ? SDL_PRESSED : SDL_RELEASED); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_DPAD_DOWN, + (pInReport->payload.deckState.ulButtonsL & STEAMDECK_LBUTTON_DPAD_DOWN) ? SDL_PRESSED : SDL_RELEASED); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_DPAD_LEFT, + (pInReport->payload.deckState.ulButtonsL & STEAMDECK_LBUTTON_DPAD_LEFT) ? SDL_PRESSED : SDL_RELEASED); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_DPAD_RIGHT, + (pInReport->payload.deckState.ulButtonsL & STEAMDECK_LBUTTON_DPAD_RIGHT) ? SDL_PRESSED : SDL_RELEASED); + ctx->last_button_state = pInReport->payload.deckState.ulButtons; + } + + SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, + (int)pInReport->payload.deckState.sLeftTrigger * 2 - 32768); + SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, + (int)pInReport->payload.deckState.sRightTrigger * 2 - 32768); + + SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, + pInReport->payload.deckState.sLeftStickX); + SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, + -pInReport->payload.deckState.sLeftStickY); + SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, + pInReport->payload.deckState.sRightStickX); + SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, + -pInReport->payload.deckState.sRightStickY); + + ctx->sensor_timestamp_us += ctx->update_rate_us; + + values[0] = (pInReport->payload.deckState.sGyroX / 32768.0f) * (2000.0f * (SDL_PI_F / 180.0f)); + values[1] = (pInReport->payload.deckState.sGyroZ / 32768.0f) * (2000.0f * (SDL_PI_F / 180.0f)); + values[2] = (-pInReport->payload.deckState.sGyroY / 32768.0f) * (2000.0f * (SDL_PI_F / 180.0f)); + SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_GYRO, ctx->sensor_timestamp_us, values, 3); + + values[0] = (pInReport->payload.deckState.sAccelX / 32768.0f) * 2.0f * SDL_STANDARD_GRAVITY; + values[1] = (pInReport->payload.deckState.sAccelZ / 32768.0f) * 2.0f * SDL_STANDARD_GRAVITY; + values[2] = (-pInReport->payload.deckState.sAccelY / 32768.0f) * 2.0f * SDL_STANDARD_GRAVITY; + SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_ACCEL, ctx->sensor_timestamp_us, values, 3); + + return SDL_TRUE; } static SDL_bool HIDAPI_DriverSteamDeck_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) diff --git a/src/joystick/hidapi/steam/controller_constants.h b/src/joystick/hidapi/steam/controller_constants.h index d0d568851..42c84f247 100644 --- a/src/joystick/hidapi/steam/controller_constants.h +++ b/src/joystick/hidapi/steam/controller_constants.h @@ -315,6 +315,37 @@ enum GamepadButtons GAMEPAD_BTN_COUNT }; +typedef enum +{ + // Low word button bits + STEAMDECK_LBUTTON_RT2 = (1 << 0), + STEAMDECK_LBUTTON_LT2 = (1 << 1), + STEAMDECK_LBUTTON_RT = (1 << 2), + STEAMDECK_LBUTTON_LT = (1 << 3), + STEAMDECK_LBUTTON_Y = (1 << 4), + STEAMDECK_LBUTTON_B = (1 << 5), + STEAMDECK_LBUTTON_X = (1 << 6), + STEAMDECK_LBUTTON_A = (1 << 7), + STEAMDECK_LBUTTON_DPAD_UP = (1 << 8), + STEAMDECK_LBUTTON_DPAD_RIGHT = (1 << 9), + STEAMDECK_LBUTTON_DPAD_LEFT = (1 << 10), + STEAMDECK_LBUTTON_DPAD_DOWN = (1 << 11), + STEAMDECK_LBUTTON_SELECT = (1 << 12), + STEAMDECK_LBUTTON_MODE = (1 << 13), + STEAMDECK_LBUTTON_START = (1 << 14), + STEAMDECK_LBUTTON_PADDLE3 = (1 << 15), + STEAMDECK_LBUTTON_PADDLE4 = (1 << 16), + STEAMDECK_LBUTTON_PADL = (1 << 17), + STEAMDECK_LBUTTON_PADR = (1 << 18), + STEAMDECK_LBUTTON_STICKL = (1 << 22), + STEAMDECK_LBUTTON_STICKR = (1 << 26), + + // High word button bits + STEAMDECK_HBUTTON_PADDLE1 = (1 << 9), + STEAMDECK_HBUTTON_PADDLE2 = (1 << 10), + STEAMDECK_HBUTTON_BASE = (1 << 18) +} DeckButtons; + // Mode adjust enum ModeAdjustModes { diff --git a/src/joystick/hidapi/steam/controller_structs.h b/src/joystick/hidapi/steam/controller_structs.h index e60428e26..6e86426d7 100644 --- a/src/joystick/hidapi/steam/controller_structs.h +++ b/src/joystick/hidapi/steam/controller_structs.h @@ -77,6 +77,7 @@ typedef enum ID_CONTROLLER_DEBUG2 = 5, ID_CONTROLLER_SECONDARY_STATE = 6, ID_CONTROLLER_BLE_STATE = 7, + ID_CONTROLLER_DECK_STATE = 9, ID_CONTROLLER_MSG_COUNT } ValveInReportMessageIDs; @@ -258,6 +259,66 @@ typedef struct unsigned char ucBatteryLevel; } SteamControllerStatusEvent_t; +// Deck State payload +typedef struct +{ + // If packet num matches that on your prior call, then the controller + // state hasn't been changed since your last call and there is no need to + // process it + uint32 unPacketNum; + + // Button bitmask and trigger data. + union + { + uint64 ulButtons; + struct + { + uint32 ulButtonsL; + uint32 ulButtonsH; + }; + }; + + // Left pad coordinates + short sLeftPadX; + short sLeftPadY; + + // Right pad coordinates + short sRightPadX; + short sRightPadY; + + // Accelerometer values + short sAccelX; + short sAccelY; + short sAccelZ; + + // Gyroscope values + short sGyroX; + short sGyroY; + short sGyroZ; + + // Gyro quaternions + short sGyroQuatW; + short sGyroQuatX; + short sGyroQuatY; + short sGyroQuatZ; + + // Uncalibrated trigger values + short sLeftTrigger; + short sRightTrigger; + + // Left stick values + short sLeftStickX; + short sLeftStickY; + + // Right stick values + short sRightStickX; + short sRightStickY; + + // Touchpad pressures + short sLeftPadPressure; + short sRightPadPressure; +} SteamDeckStatePacket_t; + typedef struct { ValveInReportHeader_t header; @@ -271,6 +332,7 @@ typedef struct ValveControllerRawTrackpadImage_t rawPadImage; SteamControllerWirelessEvent_t wirelessEvent; SteamControllerStatusEvent_t statusEvent; + SteamDeckStatePacket_t deckState; } payload; } ValveInReport_t;