From 74a746281f2208e07a7680560fcb7ec57565228e Mon Sep 17 00:00:00 2001
From: Kuratius <[EMAIL REDACTED]>
Date: Sat, 25 Apr 2026 01:18:57 +0200
Subject: [PATCH] Expose Steam Controller touchpads in Gamepad API (#15378)
---
src/joystick/hidapi/SDL_hidapi_steam.c | 65 ++++++++++++++++++++++++++
1 file changed, 65 insertions(+)
diff --git a/src/joystick/hidapi/SDL_hidapi_steam.c b/src/joystick/hidapi/SDL_hidapi_steam.c
index ca5fe5b76f981..624e8b5dd517f 100644
--- a/src/joystick/hidapi/SDL_hidapi_steam.c
+++ b/src/joystick/hidapi/SDL_hidapi_steam.c
@@ -1011,6 +1011,13 @@ typedef struct
Uint64 sensor_timestamp;
Uint64 pairing_time;
+ bool left_touch_down;
+ float left_touch_x;
+ float left_touch_y;
+ bool right_touch_down;
+ float right_touch_x;
+ float right_touch_y;
+
SteamControllerPacketAssembler m_assembler;
SteamControllerStateInternal_t m_state;
SteamControllerStateInternal_t m_last_state;
@@ -1269,6 +1276,9 @@ static bool HIDAPI_DriverSteam_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joyst
SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, update_rate_in_hz);
SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, update_rate_in_hz);
+ SDL_PrivateJoystickAddTouchpad(joystick, 1);
+ SDL_PrivateJoystickAddTouchpad(joystick, 1);
+
SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_STEAM_HOME_LED,
SDL_HomeLEDHintChanged, ctx);
@@ -1348,6 +1358,21 @@ static bool ControllerConnected(SDL_HIDAPI_Device *device, SDL_Joystick **joysti
return true;
}
+static float FilterTouch(float newValue, float oldValue)
+{
+ const float jitter = 256.0f / (1 << 16);
+ if (newValue > (oldValue - jitter * 0.5f) && newValue < (oldValue + jitter * 0.5f)) {
+ return oldValue;
+ }
+ if (newValue > (oldValue - jitter) && newValue < (oldValue + jitter)) {
+ return oldValue * 0.75f + newValue * 0.25f;
+ }
+ if (newValue > (oldValue - jitter * 2.0f) && newValue < (oldValue + jitter * 2.0f)) {
+ return (oldValue + newValue) * 0.5f;
+ }
+ return newValue;
+}
+
static void ControllerDisconnected(SDL_HIDAPI_Device *device, SDL_Joystick **joystick)
{
SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)device->context;
@@ -1469,6 +1494,46 @@ static bool HIDAPI_DriverSteam_UpdateDevice(SDL_HIDAPI_Device *device)
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, ctx->m_state.sRightPadX);
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, ~ctx->m_state.sRightPadY);
+ // Note that the left pad is normally mapped to D-Pad, so you should ignore that input if you use the touchpad instead.
+ {
+ const bool down = (ctx->m_state.ulButtons & STEAM_LEFTPAD_FINGERDOWN_MASK) ? true : false;
+ if (down || ctx->left_touch_down) {
+ const bool clicked = (ctx->m_state.ulButtons & STEAM_BUTTON_LEFTPAD_CLICKED_MASK) ? true : false;
+ const float leftX = (float)ctx->m_state.sLeftPadX / (1 << 16) + 0.5f;
+ const float leftY = -(float)ctx->m_state.sLeftPadY / (1 << 16) + 0.5f;
+ float pressure = down ? 0.5f : 0.0f;
+ if (clicked) {
+ pressure += 0.5f;
+ }
+ if (down) {
+ ctx->left_touch_x = FilterTouch(leftX, ctx->left_touch_x);
+ ctx->left_touch_y = FilterTouch(leftY, ctx->left_touch_y);
+ }
+ SDL_SendJoystickTouchpad(timestamp, joystick, 0, 0, down, ctx->left_touch_x, ctx->left_touch_y, pressure);
+ ctx->left_touch_down = down;
+ }
+ }
+
+ // Note that the right pad is normally mapped to right thumbstick, so you should ignore that input if you use the touchpad instead.
+ {
+ const bool down = (ctx->m_state.ulButtons & STEAM_RIGHTPAD_FINGERDOWN_MASK) ? true : false;
+ if (down || ctx->right_touch_down) {
+ const bool clicked = (ctx->m_state.ulButtons & STEAM_BUTTON_RIGHTPAD_CLICKED_MASK) ? true : false;
+ const float rightX = (float)ctx->m_state.sRightPadX / (1 << 16) + 0.5f;
+ const float rightY = -(float)ctx->m_state.sRightPadY / (1 << 16) + 0.5f;
+ float pressure = down ? 0.5f : 0.0f;
+ if (clicked) {
+ pressure += 0.5f;
+ }
+ if (down) {
+ ctx->right_touch_x = FilterTouch(rightX, ctx->right_touch_x);
+ ctx->right_touch_y = FilterTouch(rightY, ctx->right_touch_y);
+ }
+ SDL_SendJoystickTouchpad(timestamp, joystick, 1, 0, down, ctx->right_touch_x, ctx->right_touch_y, pressure);
+ ctx->right_touch_down = down;
+ }
+ }
+
if (ctx->report_sensors) {
float values[3];