diff --git a/include/SDL3/SDL_events.h b/include/SDL3/SDL_events.h index ed6950ad3b..dd822ecc71 100644 --- a/include/SDL3/SDL_events.h +++ b/include/SDL3/SDL_events.h @@ -212,6 +212,8 @@ typedef enum SDL_EventType SDL_EVENT_GAMEPAD_SENSOR_UPDATE, /**< Gamepad sensor was updated */ SDL_EVENT_GAMEPAD_UPDATE_COMPLETE, /**< Gamepad update is complete */ SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED, /**< Gamepad Steam handle has changed */ + SDL_EVENT_GAMEPAD_CAPSENSE_TOUCH, /**< Gamepad capsense was touched */ + SDL_EVENT_GAMEPAD_CAPSENSE_RELEASE, /**< Gamepad capsense was released */ /* Touch events */ SDL_EVENT_FINGER_DOWN = 0x700, @@ -711,6 +713,23 @@ typedef struct SDL_GamepadSensorEvent Uint64 sensor_timestamp; /**< The timestamp of the sensor reading in nanoseconds, not necessarily synchronized with the system clock */ } SDL_GamepadSensorEvent; +/** + * Gamepad capsense event structure (event.gcapsense.*) + * + * \since This struct is available since SDL 3.6.0. + */ +typedef struct SDL_GamepadCapSenseEvent +{ + SDL_EventType type; /**< SDL_EVENT_GAMEPAD_CAPSENSE_TOUCH or SDL_EVENT_GAMEPAD_CAPSENSE_RELEASE */ + Uint32 reserved; + Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */ + SDL_JoystickID which; /**< The joystick instance id */ + Uint8 capsense; /**< The capsense type (SDL_GamepadCapSenseType) */ + bool down; /**< true if the capsense is touched */ + Uint8 padding1; + Uint8 padding2; +} SDL_GamepadCapSenseEvent; + /** * Audio device event structure (event.adevice.*) * @@ -1040,6 +1059,7 @@ typedef union SDL_Event SDL_GamepadButtonEvent gbutton; /**< Gamepad button event data */ SDL_GamepadTouchpadEvent gtouchpad; /**< Gamepad touchpad event data */ SDL_GamepadSensorEvent gsensor; /**< Gamepad sensor event data */ + SDL_GamepadCapSenseEvent gcapsense; /**< Gamepad capsense event data */ SDL_AudioDeviceEvent adevice; /**< Audio device event data */ SDL_CameraDeviceEvent cdevice; /**< Camera device event data */ SDL_SensorEvent sensor; /**< Sensor event data */ diff --git a/include/SDL3/SDL_gamepad.h b/include/SDL3/SDL_gamepad.h index 9c88a770ae..2d0e83440a 100644 --- a/include/SDL3/SDL_gamepad.h +++ b/include/SDL3/SDL_gamepad.h @@ -231,6 +231,24 @@ typedef enum SDL_GamepadAxis SDL_GAMEPAD_AXIS_COUNT } SDL_GamepadAxis; +/** + * The list of capsense types on a gamepad + * + * \since This enum is available since SDL 3.6.0. + * + * \sa SDL_GamepadHasCapSense + * \sa SDL_GetGamepadCapSense + */ +typedef enum SDL_GamepadCapSenseType +{ + SDL_GAMEPAD_CAPSENSE_INVALID = -1, + SDL_GAMEPAD_CAPSENSE_LEFT_STICK, /**< Activated by touching the top of the left thumbstick */ + SDL_GAMEPAD_CAPSENSE_RIGHT_STICK, /**< Activated by touching the top of the right thumbstick */ + SDL_GAMEPAD_CAPSENSE_LEFT_GRIP, /**< Activated by gripping the left handle of the controller */ + SDL_GAMEPAD_CAPSENSE_RIGHT_GRIP, /**< Activated by gripping the right handle of the controller */ + SDL_GAMEPAD_CAPSENSE_COUNT +} SDL_GamepadCapSenseType; + /** * Types of gamepad control bindings. * @@ -1510,6 +1528,36 @@ extern SDL_DECLSPEC float SDLCALL SDL_GetGamepadSensorDataRate(SDL_Gamepad *game */ extern SDL_DECLSPEC bool SDLCALL SDL_GetGamepadSensorData(SDL_Gamepad *gamepad, SDL_SensorType type, float *data, int num_values); +/** + * Return whether a gamepad has a particular capsense. + * + * \param gamepad the gamepad to query. + * \param type the type of capsense to query. + * \returns true if the capsense exists, false otherwise. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.6.0. + * + * \sa SDL_GetGamepadCapSense + */ +extern SDL_DECLSPEC bool SDLCALL SDL_GamepadHasCapSense(SDL_Gamepad *gamepad, SDL_GamepadCapSenseType type); + +/** + * Get the current state of a capsense on a gamepad. + * + * \param gamepad a gamepad. + * \param type the type of capsense to query. + * \returns true if the capsense is touched, false otherwise. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.6.0. + * + * \sa SDL_GamepadHasCapSense + */ +extern SDL_DECLSPEC bool SDLCALL SDL_GetGamepadCapSense(SDL_Gamepad *gamepad, SDL_GamepadCapSenseType type); + /** * Start a rumble effect on a gamepad. * diff --git a/src/dynapi/SDL_dynapi.exports b/src/dynapi/SDL_dynapi.exports index 32e9fbff86..9864557071 100644 --- a/src/dynapi/SDL_dynapi.exports +++ b/src/dynapi/SDL_dynapi.exports @@ -1288,3 +1288,5 @@ _SDL_IsPhone _SDL_LoadJPG_IO _SDL_LoadJPG _SDL_HasSVE2 +_SDL_GamepadHasCapSense +_SDL_GetGamepadCapSense diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index ca1a1c97d9..3958a52aa6 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -1289,6 +1289,8 @@ SDL3_0.0.0 { SDL_LoadJPG_IO; SDL_LoadJPG; SDL_HasSVE2; + SDL_GamepadHasCapSense; + SDL_GetGamepadCapSense; # extra symbols go here (don't modify this line) local: *; }; diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index 677768ff2f..b54d32ae6d 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -1315,3 +1315,5 @@ #define SDL_LoadJPG_IO SDL_LoadJPG_IO_REAL #define SDL_LoadJPG SDL_LoadJPG_REAL #define SDL_HasSVE2 SDL_HasSVE2_REAL +#define SDL_GamepadHasCapSense SDL_GamepadHasCapSense_REAL +#define SDL_GetGamepadCapSense SDL_GetGamepadCapSense_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 99899b346e..4f8ac0ba0c 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -1323,3 +1323,5 @@ SDL_DYNAPI_PROC(bool,SDL_IsPhone,(void),(),return) SDL_DYNAPI_PROC(SDL_Surface*,SDL_LoadJPG_IO,(SDL_IOStream *a,bool b),(a,b),return) SDL_DYNAPI_PROC(SDL_Surface*,SDL_LoadJPG,(const char *a),(a),return) SDL_DYNAPI_PROC(bool,SDL_HasSVE2,(void),(),return) +SDL_DYNAPI_PROC(bool,SDL_GamepadHasCapSense,(SDL_Gamepad *a,SDL_GamepadCapSenseType b),(a,b),return) +SDL_DYNAPI_PROC(bool,SDL_GetGamepadCapSense,(SDL_Gamepad *a,SDL_GamepadCapSenseType b),(a,b),return) diff --git a/src/events/SDL_categories.c b/src/events/SDL_categories.c index fa120775b2..9d7722923b 100644 --- a/src/events/SDL_categories.c +++ b/src/events/SDL_categories.c @@ -134,6 +134,10 @@ SDL_EventCategory SDL_GetEventCategory(Uint32 type) case SDL_EVENT_GAMEPAD_SENSOR_UPDATE: return SDL_EVENTCATEGORY_GSENSOR; + case SDL_EVENT_GAMEPAD_CAPSENSE_TOUCH: + case SDL_EVENT_GAMEPAD_CAPSENSE_RELEASE: + return SDL_EVENTCATEGORY_GCAPSENSE; + case SDL_EVENT_FINGER_DOWN: case SDL_EVENT_FINGER_UP: case SDL_EVENT_FINGER_CANCELED: diff --git a/src/events/SDL_categories_c.h b/src/events/SDL_categories_c.h index a1259e0e90..a3762746d5 100644 --- a/src/events/SDL_categories_c.h +++ b/src/events/SDL_categories_c.h @@ -49,6 +49,7 @@ typedef enum SDL_EventCategory SDL_EVENTCATEGORY_GBUTTON, SDL_EVENTCATEGORY_GTOUCHPAD, SDL_EVENTCATEGORY_GSENSOR, + SDL_EVENTCATEGORY_GCAPSENSE, SDL_EVENTCATEGORY_ADEVICE, SDL_EVENTCATEGORY_CDEVICE, SDL_EVENTCATEGORY_SENSOR, diff --git a/src/events/SDL_events.c b/src/events/SDL_events.c index 315a8b6d90..19f5a85adc 100644 --- a/src/events/SDL_events.c +++ b/src/events/SDL_events.c @@ -758,6 +758,18 @@ int SDL_GetEventDescription(const SDL_Event *event, char *buf, int buflen) event->gsensor.data[0], event->gsensor.data[1], event->gsensor.data[2]); break; +#define PRINT_CAPSENSE_EVENT(event) \ + (void)SDL_snprintf(details, sizeof(details), " (timestamp=%" SDL_PRIu64 " which=%d capsense=%u state=%s)", \ + event->gcapsense.timestamp, (int)event->gcapsense.which, \ + event->gcapsense.capsense, event->gcapsense.down ? "touch" : "release") + SDL_EVENT_CASE(SDL_EVENT_GAMEPAD_CAPSENSE_TOUCH) + PRINT_CAPSENSE_EVENT(event); + break; + SDL_EVENT_CASE(SDL_EVENT_GAMEPAD_CAPSENSE_RELEASE) + PRINT_CAPSENSE_EVENT(event); + break; +#undef PRINT_CAPSENSE_EVENT + #define PRINT_FINGER_EVENT(event) \ (void)SDL_snprintf(details, sizeof(details), " (timestamp=%" SDL_PRIu64 " touchid=%" SDL_PRIu64 " fingerid=%" SDL_PRIu64 " x=%f y=%f dx=%f dy=%f pressure=%f)", \ event->tfinger.timestamp, event->tfinger.touchID, \ diff --git a/src/joystick/SDL_gamepad.c b/src/joystick/SDL_gamepad.c index 3b0b86fdfb..b9c3ade4f0 100644 --- a/src/joystick/SDL_gamepad.c +++ b/src/joystick/SDL_gamepad.c @@ -1254,10 +1254,10 @@ static GamepadMapping_t *SDL_CreateMappingForHIDAPIGamepad(SDL_GUID guid) SDL_strlcat(mapping_string, "paddle1:b11,paddle2:b12,", sizeof(mapping_string)); } else if (SDL_IsJoystickSteamDeck(vendor, product)) { // The Steam Deck's built-in controller has QAM, 4 back buttons, L/R trackpads, and L/R capacitive touch sticks - SDL_strlcat(mapping_string, "misc1:b11,paddle1:b12,paddle2:b13,paddle3:b14,paddle4:b15,touchpad:b17,misc2:b16,misc3:b19,misc4:b18", sizeof(mapping_string)); + SDL_strlcat(mapping_string, "misc1:b11,paddle1:b12,paddle2:b13,paddle3:b14,paddle4:b15,touchpad:b17,misc2:b16", sizeof(mapping_string)); } else if (SDL_IsJoystickSteamTriton(vendor, product)) { // Second generation Steam controllers have 4 back paddle buttons - SDL_strlcat(mapping_string, "misc1:b11,paddle1:b12,paddle2:b13,paddle3:b14,paddle4:b15,touchpad:b17,misc2:b16,misc3:b19,misc4:b18,misc5:b21,misc6:b20", sizeof(mapping_string)); + SDL_strlcat(mapping_string, "misc1:b11,paddle1:b12,paddle2:b13,paddle3:b14,paddle4:b15,touchpad:b17,misc2:b16", sizeof(mapping_string)); } else if (SDL_IsJoystickNintendoSwitchPro(vendor, product) || SDL_IsJoystickNintendoSwitchProInputOnly(vendor, product)) { // Nintendo Switch Pro controllers have a screenshot button @@ -1281,7 +1281,7 @@ static GamepadMapping_t *SDL_CreateMappingForHIDAPIGamepad(SDL_GUID guid) } } else if (SDL_IsJoystickHoriSteamController(vendor, product)) { /* The Wireless HORIPad for Steam has QAM, Steam, Capsense L/R Sticks, 2 rear buttons, and 2 misc buttons */ - SDL_strlcat(mapping_string, "paddle1:b13,paddle2:b12,paddle3:b15,paddle4:b14,misc1:b11,misc3:b16,misc4:b17", sizeof(mapping_string)); + SDL_strlcat(mapping_string, "paddle1:b13,paddle2:b12,paddle3:b15,paddle4:b14,misc1:b11", sizeof(mapping_string)); } else if (SDL_IsJoystickFlydigiController(vendor, product)) { SDL_strlcat(mapping_string, "paddle1:b11,paddle2:b12,paddle3:b13,paddle4:b14,", sizeof(mapping_string)); if (guid.data[15] >= SDL_FLYDIGI_VADER2) { @@ -3952,6 +3952,48 @@ bool SDL_GetGamepadSensorData(SDL_Gamepad *gamepad, SDL_SensorType type, float * return SDL_Unsupported(); } +bool SDL_GamepadHasCapSense(SDL_Gamepad *gamepad, SDL_GamepadCapSenseType type) +{ + bool result = false; + + SDL_LockJoysticks(); + { + SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad); + if (joystick) { + for (int i = 0; i < joystick->ncapsenses; ++i) { + if (joystick->capsenses[i].type == type) { + result = true; + break; + } + } + } + } + SDL_UnlockJoysticks(); + + return result; +} + +bool SDL_GetGamepadCapSense(SDL_Gamepad *gamepad, SDL_GamepadCapSenseType type) +{ + bool result = false; + + SDL_LockJoysticks(); + { + SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad); + if (joystick) { + for (int i = 0; i < joystick->ncapsenses; ++i) { + if (joystick->capsenses[i].type == type) { + result = joystick->capsenses[i].down; + break; + } + } + } + } + SDL_UnlockJoysticks(); + + return result; +} + SDL_JoystickID SDL_GetGamepadID(SDL_Gamepad *gamepad) { SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad); @@ -4470,6 +4512,8 @@ static const Uint32 SDL_gamepad_event_list[] = { SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION, SDL_EVENT_GAMEPAD_TOUCHPAD_UP, SDL_EVENT_GAMEPAD_SENSOR_UPDATE, + SDL_EVENT_GAMEPAD_CAPSENSE_TOUCH, + SDL_EVENT_GAMEPAD_CAPSENSE_RELEASE, }; void SDL_SetGamepadEventsEnabled(bool enabled) diff --git a/src/joystick/SDL_gamepad_db.h b/src/joystick/SDL_gamepad_db.h index 5859e6d373..6b1a954098 100644 --- a/src/joystick/SDL_gamepad_db.h +++ b/src/joystick/SDL_gamepad_db.h @@ -723,7 +723,7 @@ static const char *s_GamepadMappings[] = { "05000000de2800000212000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,", "05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,", "05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,", - "03000000de2800000512000000016800,Steam Deck Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,misc1:b11,paddle1:b12,paddle2:b13,paddle3:b14,paddle4:b15,touchpad:b17,misc2:b16,misc3:b19,misc4:b18,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", + "03000000de2800000512000000016800,Steam Deck Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,misc1:b11,paddle1:b12,paddle2:b13,paddle3:b14,paddle4:b15,touchpad:b17,misc2:b16,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", "03000000de2800000512000011010000,Steam Deck,a:b3,b:b4,back:b11,dpdown:b17,dpleft:b18,dpright:b19,dpup:b16,guide:b13,leftshoulder:b7,leftstick:b14,lefttrigger:a9,leftx:a0,lefty:a1,misc1:b2,paddle1:b21,paddle2:b20,paddle3:b23,paddle4:b22,rightshoulder:b8,rightstick:b15,righttrigger:a8,rightx:a2,righty:a3,start:b12,x:b5,y:b6,", "03000000de280000ff11000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,", "0500000011010000311400001b010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b32,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,", diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c index f052e2d04a..746cb04690 100644 --- a/src/joystick/SDL_joystick.c +++ b/src/joystick/SDL_joystick.c @@ -2441,6 +2441,26 @@ void SDL_PrivateJoystickSensorRate(SDL_Joystick *joystick, SDL_SensorType type, } } +void SDL_PrivateJoystickAddCapSense(SDL_Joystick *joystick, SDL_GamepadCapSenseType type) +{ + int ncapsenses; + SDL_JoystickCapSenseInfo *capsenses; + + SDL_AssertJoysticksLocked(); + + ncapsenses = joystick->ncapsenses + 1; + capsenses = (SDL_JoystickCapSenseInfo *)SDL_realloc(joystick->capsenses, (ncapsenses * sizeof(SDL_JoystickCapSenseInfo))); + if (capsenses) { + SDL_JoystickCapSenseInfo *capsense = &capsenses[ncapsenses - 1]; + + capsense->type = type; + capsense->down = false; + + joystick->ncapsenses = ncapsenses; + joystick->capsenses = capsenses; + } +} + void SDL_PrivateJoystickAdded(SDL_JoystickID instance_id) { SDL_JoystickDriver *driver; @@ -3952,6 +3972,53 @@ void SDL_SendJoystickSensor(Uint64 timestamp, SDL_Joystick *joystick, SDL_Sensor } } +void SDL_SendJoystickCapSense(Uint64 timestamp, SDL_Joystick *joystick, SDL_GamepadCapSenseType type, bool down) +{ + SDL_AssertJoysticksLocked(); + + // We ignore events if we don't have keyboard focus, except for button + // (capsense) release + if (SDL_PrivateJoystickShouldIgnoreEvent()) { + if (down) { + return; + } + } + + for (int i = 0; i < joystick->ncapsenses; ++i) { + SDL_JoystickCapSenseInfo *capsense = &joystick->capsenses[i]; + + if (capsense->type == type) { + SDL_Event event; + + // Ignore duplicate events + if (down == capsense->down) { + return; + } + + // Update internal joystick state + capsense->down = down; + joystick->update_complete = timestamp; + + if (down) { + event.type = SDL_EVENT_GAMEPAD_CAPSENSE_TOUCH; + } else { + event.type = SDL_EVENT_GAMEPAD_CAPSENSE_RELEASE; + } + + // Post the event, if desired + if (SDL_EventEnabled(event.type)) { + event.common.timestamp = timestamp; + event.gcapsense.which = joystick->instance_id; + event.gcapsense.capsense = type; + event.gcapsense.down = down; + SDL_PushEvent(&event); + } + + break; + } + } +} + static void SDL_LoadVIDPIDListFromHint(const char *hint, int *num_entries, int *max_entries, Uint32 **entries) { Uint32 entry; diff --git a/src/joystick/SDL_joystick_c.h b/src/joystick/SDL_joystick_c.h index 62e1f9f865..b80fb6b651 100644 --- a/src/joystick/SDL_joystick_c.h +++ b/src/joystick/SDL_joystick_c.h @@ -181,6 +181,7 @@ extern bool SDL_ShouldIgnoreJoystick(Uint16 vendor_id, Uint16 product_id, Uint16 extern void SDL_PrivateJoystickAddTouchpad(SDL_Joystick *joystick, int nfingers); extern void SDL_PrivateJoystickAddSensor(SDL_Joystick *joystick, SDL_SensorType type, float rate); extern void SDL_PrivateJoystickSensorRate(SDL_Joystick *joystick, SDL_SensorType type, float rate); +extern void SDL_PrivateJoystickAddCapSense(SDL_Joystick *joystick, SDL_GamepadCapSenseType type); extern void SDL_PrivateJoystickAdded(SDL_JoystickID instance_id); extern bool SDL_IsJoystickBeingAdded(void); extern void SDL_PrivateJoystickRemoved(SDL_JoystickID instance_id); @@ -191,6 +192,7 @@ extern void SDL_SendJoystickHat(Uint64 timestamp, SDL_Joystick *joystick, Uint8 extern void SDL_SendJoystickButton(Uint64 timestamp, SDL_Joystick *joystick, Uint8 button, bool down); extern void SDL_SendJoystickTouchpad(Uint64 timestamp, SDL_Joystick *joystick, int touchpad, int finger, bool down, float x, float y, float pressure); extern void SDL_SendJoystickSensor(Uint64 timestamp, SDL_Joystick *joystick, SDL_SensorType type, Uint64 sensor_timestamp, const float *data, int num_values); +extern void SDL_SendJoystickCapSense(Uint64 timestamp, SDL_Joystick *joystick, SDL_GamepadCapSenseType type, bool down); extern void SDL_SendJoystickPowerInfo(SDL_Joystick *joystick, SDL_PowerState state, int percent); // Function to get the Steam virtual gamepad info for a joystick diff --git a/src/joystick/SDL_sysjoystick.h b/src/joystick/SDL_sysjoystick.h index 15659b8cc0..76bce9006d 100644 --- a/src/joystick/SDL_sysjoystick.h +++ b/src/joystick/SDL_sysjoystick.h @@ -72,6 +72,12 @@ typedef struct SDL_JoystickSensorInfo float data[3]; // If this needs to expand, update SDL_GamepadSensorEvent } SDL_JoystickSensorInfo; +typedef struct SDL_JoystickCapSenseInfo +{ + SDL_GamepadCapSenseType type; + bool down; +} SDL_JoystickCapSenseInfo; + #define _guarded SDL_GUARDED_BY(SDL_joystick_lock) struct SDL_Joystick @@ -105,6 +111,9 @@ struct SDL_Joystick int nsensors_enabled _guarded; SDL_JoystickSensorInfo *sensors _guarded; + int ncapsenses _guarded; // Number of capsense sources on the joystick + SDL_JoystickCapSenseInfo *capsenses _guarded; // Current capsense states + Uint16 low_frequency_rumble _guarded; Uint16 high_frequency_rumble _guarded; Uint64 rumble_expiration _guarded; diff --git a/src/joystick/hidapi/SDL_hidapi_steam_hori.c b/src/joystick/hidapi/SDL_hidapi_steam_hori.c index c30b7cef0c..c24602f7fe 100644 --- a/src/joystick/hidapi/SDL_hidapi_steam_hori.c +++ b/src/joystick/hidapi/SDL_hidapi_steam_hori.c @@ -39,8 +39,6 @@ enum SDL_GAMEPAD_BUTTON_HORI_FL, SDL_GAMEPAD_BUTTON_HORI_M1, SDL_GAMEPAD_BUTTON_HORI_M2, - SDL_GAMEPAD_BUTTON_HORI_JOYSTICK_TOUCH_L, - SDL_GAMEPAD_BUTTON_HORI_JOYSTICK_TOUCH_R, SDL_GAMEPAD_NUM_HORI_BUTTONS }; @@ -133,6 +131,10 @@ static bool HIDAPI_DriverSteamHori_OpenJoystick(SDL_HIDAPI_Device *device, SDL_J const Uint64 sensorupdatestep_ms = ctx->wireless ? 8333 : 4000; // Equivalent to 120hz / 250hz respectively ctx->simulated_sensor_step_ns = SDL_US_TO_NS(sensorupdatestep_ms); + + SDL_PrivateJoystickAddCapSense(joystick, SDL_GAMEPAD_CAPSENSE_LEFT_STICK); + SDL_PrivateJoystickAddCapSense(joystick, SDL_GAMEPAD_CAPSENSE_RIGHT_STICK); + return true; } @@ -273,8 +275,8 @@ static void HIDAPI_DriverSteamHori_HandleStatePacket(SDL_Joystick *joystick, SDL SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data[7] & 0x02) != 0)); SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data[7] & 0x04) != 0)); SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_HORI_M2, ((data[7] & 0x08) != 0)); - SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_HORI_JOYSTICK_TOUCH_L, ((data[7] & 0x10) != 0)); - SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_HORI_JOYSTICK_TOUCH_R, ((data[7] & 0x20) != 0)); + SDL_SendJoystickCapSense(timestamp, joystick, SDL_GAMEPAD_CAPSENSE_LEFT_STICK, ((data[7] & 0x10) != 0)); + SDL_SendJoystickCapSense(timestamp, joystick, SDL_GAMEPAD_CAPSENSE_RIGHT_STICK, ((data[7] & 0x20) != 0)); SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_HORI_FR, ((data[7] & 0x40) != 0)); SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_HORI_FL, ((data[7] & 0x80) != 0)); } diff --git a/src/joystick/hidapi/SDL_hidapi_steam_triton.c b/src/joystick/hidapi/SDL_hidapi_steam_triton.c index f1c5881229..97152d359c 100644 --- a/src/joystick/hidapi/SDL_hidapi_steam_triton.c +++ b/src/joystick/hidapi/SDL_hidapi_steam_triton.c @@ -47,10 +47,6 @@ enum SDL_GAMEPAD_BUTTON_TRITON_LEFT_PADDLE2, SDL_GAMEPAD_BUTTON_TRITON_RIGHT_TOUCHPAD, SDL_GAMEPAD_BUTTON_TRITON_LEFT_TOUCHPAD, - SDL_GAMEPAD_BUTTON_TRITON_RIGHT_JOYSTICK_TOUCH, - SDL_GAMEPAD_BUTTON_TRITON_LEFT_JOYSTICK_TOUCH, - SDL_GAMEPAD_BUTTON_TRITON_RIGHT_GRIP_TOUCH, - SDL_GAMEPAD_BUTTON_TRITON_LEFT_GRIP_TOUCH, SDL_GAMEPAD_NUM_TRITON_BUTTONS, }; @@ -187,15 +183,15 @@ static void HIDAPI_DriverSteamTriton_HandleState(SDL_HIDAPI_Device *device, SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_TRITON_LEFT_TOUCHPAD, ((pTritonReport->buttons & TRITON_LEFT_TOUCHPAD_CLICK) != 0)); - SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_TRITON_RIGHT_JOYSTICK_TOUCH, - ((pTritonReport->buttons & TRITON_RIGHT_JOYSTICK_TOUCH) != 0)); - SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_TRITON_LEFT_JOYSTICK_TOUCH, - ((pTritonReport->buttons & TRITON_LEFT_JOYSTICK_TOUCH) != 0)); + SDL_SendJoystickCapSense(timestamp, joystick, SDL_GAMEPAD_CAPSENSE_RIGHT_STICK, + ((pTritonReport->buttons & TRITON_RIGHT_JOYSTICK_TOUCH) != 0)); + SDL_SendJoystickCapSense(timestamp, joystick, SDL_GAMEPAD_CAPSENSE_LEFT_STICK, + ((pTritonReport->buttons & TRITON_LEFT_JOYSTICK_TOUCH) != 0)); - SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_TRITON_RIGHT_GRIP_TOUCH, - ((pTritonReport->buttons & TRITON_RIGHT_GRIP_TOUCH) != 0)); - SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_TRITON_LEFT_GRIP_TOUCH, - ((pTritonReport->buttons & TRITON_LEFT_GRIP_TOUCH) != 0)); + SDL_SendJoystickCapSense(timestamp, joystick, SDL_GAMEPAD_CAPSENSE_RIGHT_GRIP, + ((pTritonReport->buttons & TRITON_RIGHT_GRIP_TOUCH) != 0)); + SDL_SendJoystickCapSense(timestamp, joystick, SDL_GAMEPAD_CAPSENSE_LEFT_GRIP, + ((pTritonReport->buttons & TRITON_LEFT_GRIP_TOUCH) != 0)); if (pTritonReport->buttons & TRITON_LBUTTON_DPAD_UP) { hat |= SDL_HAT_UP; @@ -480,6 +476,11 @@ static bool HIDAPI_DriverSteamTriton_OpenJoystick(SDL_HIDAPI_Device *device, SDL SDL_PrivateJoystickAddTouchpad(joystick, 1); SDL_PrivateJoystickAddTouchpad(joystick, 1); + SDL_PrivateJoystickAddCapSense(joystick, SDL_GAMEPAD_CAPSENSE_LEFT_STICK); + SDL_PrivateJoystickAddCapSense(joystick, SDL_GAMEPAD_CAPSENSE_RIGHT_STICK); + SDL_PrivateJoystickAddCapSense(joystick, SDL_GAMEPAD_CAPSENSE_LEFT_GRIP); + SDL_PrivateJoystickAddCapSense(joystick, SDL_GAMEPAD_CAPSENSE_RIGHT_GRIP); + return true; } diff --git a/src/joystick/hidapi/SDL_hidapi_steamdeck.c b/src/joystick/hidapi/SDL_hidapi_steamdeck.c index 92ca8402a9..286b7c97ea 100644 --- a/src/joystick/hidapi/SDL_hidapi_steamdeck.c +++ b/src/joystick/hidapi/SDL_hidapi_steamdeck.c @@ -41,8 +41,6 @@ enum SDL_GAMEPAD_BUTTON_STEAM_DECK_LEFT_PADDLE2, SDL_GAMEPAD_BUTTON_STEAM_DECK_RIGHT_TOUCHPAD, SDL_GAMEPAD_BUTTON_STEAM_DECK_LEFT_TOUCHPAD, - SDL_GAMEPAD_BUTTON_STEAM_DECK_RIGHT_JOYSTICK_TOUCH, - SDL_GAMEPAD_BUTTON_STEAM_DECK_LEFT_JOYSTICK_TOUCH, SDL_GAMEPAD_NUM_STEAM_DECK_BUTTONS, }; @@ -202,10 +200,10 @@ static void HIDAPI_DriverSteamDeck_HandleState(SDL_HIDAPI_Device *device, SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_STEAM_DECK_LEFT_TOUCHPAD, ((pInReport->payload.deckState.ulButtonsL & STEAMDECK_LBUTTON_LEFT_PAD) != 0)); - SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_STEAM_DECK_RIGHT_JOYSTICK_TOUCH, - ((pInReport->payload.deckState.ulButtonsH & STEAMDECK_HBUTTON_RSTICK_TOUCH) != 0)); - SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_STEAM_DECK_LEFT_JOYSTICK_TOUCH, - ((pInReport->payload.deckState.ulButtonsH & STEAMDECK_HBUTTON_LSTICK_TOUCH) != 0)); + SDL_SendJoystickCapSense(timestamp, joystick, SDL_GAMEPAD_CAPSENSE_RIGHT_STICK, + ((pInReport->payload.deckState.ulButtonsH & STEAMDECK_HBUTTON_RSTICK_TOUCH) != 0)); + SDL_SendJoystickCapSense(timestamp, joystick, SDL_GAMEPAD_CAPSENSE_LEFT_STICK, + ((pInReport->payload.deckState.ulButtonsH & STEAMDECK_HBUTTON_LSTICK_TOUCH) != 0)); if (pInReport->payload.deckState.ulButtonsL & STEAMDECK_LBUTTON_DPAD_UP) { hat |= SDL_HAT_UP; @@ -397,6 +395,9 @@ static bool HIDAPI_DriverSteamDeck_OpenJoystick(SDL_HIDAPI_Device *device, SDL_J SDL_PrivateJoystickAddTouchpad(joystick, 1); SDL_PrivateJoystickAddTouchpad(joystick, 1); + SDL_PrivateJoystickAddCapSense(joystick, SDL_GAMEPAD_CAPSENSE_LEFT_STICK); + SDL_PrivateJoystickAddCapSense(joystick, SDL_GAMEPAD_CAPSENSE_RIGHT_STICK); + return true; } diff --git a/src/test/SDL_test_common.c b/src/test/SDL_test_common.c index 460d05d027..ee324a7a35 100644 --- a/src/test/SDL_test_common.c +++ b/src/test/SDL_test_common.c @@ -1582,6 +1582,23 @@ static const char *GamepadButtonName(const SDL_GamepadButton button) } } +static const char *CapSenseName(const SDL_GamepadCapSenseType type) +{ + switch (type) { +#define CAPSENSE_CASE(cap) \ + case SDL_GAMEPAD_CAPSENSE_##cap: \ + return #cap + CAPSENSE_CASE(INVALID); + CAPSENSE_CASE(LEFT_STICK); + CAPSENSE_CASE(RIGHT_STICK); + CAPSENSE_CASE(LEFT_GRIP); + CAPSENSE_CASE(RIGHT_GRIP); +#undef CAPSENSE_CASE + default: + return "???"; + } +} + void SDLTest_PrintEvent(const SDL_Event *event) { switch (event->type) { @@ -1878,6 +1895,16 @@ void SDLTest_PrintEvent(const SDL_Event *event) event->gbutton.which, event->gbutton.button, GamepadButtonName((SDL_GamepadButton)event->gbutton.button)); break; + case SDL_EVENT_GAMEPAD_CAPSENSE_TOUCH: + SDL_Log("SDL EVENT: Gamepad %" SDL_PRIu32 "capsense %u ('%s') touch", + event->gcapsense.which, event->gcapsense.capsense, + CapSenseName((SDL_GamepadCapSenseType)event->gcapsense.capsense)); + break; + case SDL_EVENT_GAMEPAD_CAPSENSE_RELEASE: + SDL_Log("SDL EVENT: Gamepad %" SDL_PRIu32 " capsense %u ('%s') release", + event->gcapsense.which, event->gcapsense.capsense, + CapSenseName((SDL_GamepadCapSenseType)event->gcapsense.capsense)); + break; case SDL_EVENT_CLIPBOARD_UPDATE: SDL_Log("SDL EVENT: Clipboard updated"); break; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5166688ae9..42f1d58aa3 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -102,7 +102,7 @@ if(NOT CMAKE_VERSION VERSION_LESS 3.20) endif() if(DOS) - set(NAME83_LONG "unifont-15.1.05.hex;unifont-15.1.05-license.txt;physaudiodev.png;logaudiodev.png;audiofile.png;soundboard.png;soundboard_levels.png;trashcan.png;msdf_font.png;msdf_font.csv;gamepad_front.png;gamepad_back.png;gamepad_face_abxy.png;gamepad_face_axby.png;gamepad_face_bayx.png;gamepad_face_sony.png;gamepad_battery.png;gamepad_battery_unknown.png;gamepad_battery_wired.png;gamepad_touchpad.png;gamepad_dual_touchpad.png;gamepad_button.png;gamepad_button_small.png;gamepad_button_background.png;gamepad_axis.png;gamepad_axis_arrow.png;gamepad_wired.png;gamepad_wireless.png;sdl-test_round.png") + set(NAME83_LONG "unifont-15.1.05.hex;unifont-15.1.05-license.txt;physaudiodev.png;logaudiodev.png;audiofile.png;soundboard.png;soundboard_levels.png;trashcan.png;msdf_font.png;msdf_font.csv;gamepad_front.png;gamepad_back.png;gamepad_face_abxy.png;gamepad_face_axby.png;gamepad_face_bayx.png;gamepad_face_sony.png;gamepad_battery.png;gamepad_battery_unknown.png;gamepad_battery_wired.png;gamepad_touchpad.png;gamepad_dual_touchpad.png;gamepad_button.png;gamepad_button_small.png;gamepad_button_background.png;gamepad_axis.png;gamepad_axis_arrow.png;gamepad_wired.png;gamepad_wireless.png;gamepad_grip_sense.png;sdl-test_round.png") set(DOS83_SHORT "UNIFONT.HEX;UNIFONTL.TXT;PHYSADEV.PNG;LOGADEV.PNG;AUDIOFIL.PNG;SNDBRD.PNG;SNDLVL.PNG;TRASHCAN.PNG;MSDFFONT.PNG;MSDFFONT.CSV;GP_FRONT.PNG;GP_BACK.PNG;GP_FABXY.PNG;GP_FAXBY.PNG;GP_FBAYX.PNG;GP_FSONY.PNG;GP_BATT.PNG;GP_BATTX.PNG;GP_BATTW.PNG;GP_TOUCH.PNG;GP_BTN.PNG;GP_BTNSM.PNG;GP_BTNBG.PNG;GP_AXIS.PNG;GP_AXARW.PNG;GP_WIRED.PNG;GP_WLESS.PNG;SDLROUND.PNG") endif() @@ -315,6 +315,7 @@ files2headers(gamepad_image_headers gamepad_face_bayx.png gamepad_face_sony.png gamepad_front.png + gamepad_grip_sense.png gamepad_touchpad.png gamepad_wired.png gamepad_wireless.png diff --git a/test/gamepad_grip_sense.h b/test/gamepad_grip_sense.h new file mode 100644 index 0000000000..225e797896 --- /dev/null +++ b/test/gamepad_grip_sense.h @@ -0,0 +1,73 @@ +unsigned char gamepad_grip_sense_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3c, + 0x08, 0x06, 0x00, 0x00, 0x00, 0x66, 0x76, 0x9a, 0x56, 0x00, 0x00, 0x00, + 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, + 0x65, 0x00, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x49, 0x6d, 0x61, 0x67, + 0x65, 0x52, 0x65, 0x61, 0x64, 0x79, 0x71, 0xc9, 0x65, 0x3c, 0x00, 0x00, + 0x02, 0xe7, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x94, 0x58, 0xcb, 0xb1, + 0xea, 0x30, 0x0c, 0x75, 0x8c, 0x0b, 0xa0, 0x24, 0x06, 0x8a, 0xa0, 0x04, + 0xd6, 0x54, 0xc2, 0x16, 0xe8, 0x20, 0x1b, 0x86, 0x02, 0x20, 0x30, 0x43, + 0x41, 0xc0, 0x9a, 0x4f, 0x2e, 0xf2, 0x7b, 0xca, 0x55, 0x94, 0x23, 0xe1, + 0xeb, 0x19, 0x26, 0x21, 0xb1, 0x64, 0x49, 0xe7, 0x48, 0x72, 0x5c, 0xd5, + 0x75, 0x1d, 0xe4, 0xd8, 0x6e, 0xb7, 0xed, 0xf1, 0x78, 0x0c, 0x25, 0x63, + 0x36, 0x9b, 0x85, 0xc3, 0xe1, 0x50, 0xc9, 0x67, 0x89, 0x6f, 0xd6, 0xeb, + 0x75, 0x7b, 0x3a, 0x9d, 0xf2, 0xfd, 0x6a, 0xb5, 0x0a, 0xa3, 0xd1, 0x28, + 0xc4, 0x18, 0x4d, 0x65, 0x8f, 0xc7, 0x23, 0x2c, 0x97, 0xcb, 0x50, 0x55, + 0x55, 0x3b, 0x9d, 0x4e, 0xc3, 0xc7, 0x88, 0xac, 0xb8, 0x22, 0x0b, 0x37, + 0x9b, 0x4d, 0xdb, 0x34, 0x4d, 0x56, 0x94, 0x52, 0xa2, 0x49, 0x9d, 0x60, + 0xdb, 0xb6, 0xf9, 0xa7, 0x9f, 0xd1, 0x62, 0x74, 0x7d, 0x3e, 0x9f, 0x59, + 0x31, 0x5b, 0x9b, 0x4d, 0x60, 0x65, 0x64, 0x95, 0x54, 0xc2, 0xf7, 0x52, + 0x59, 0xb6, 0xe2, 0xf3, 0x9f, 0x9f, 0x93, 0x0c, 0xc9, 0x72, 0x98, 0xaa, + 0x8f, 0xb9, 0xed, 0x78, 0x3c, 0x0e, 0x93, 0xc9, 0xa4, 0x13, 0x64, 0x01, + 0xad, 0x40, 0x2a, 0xa7, 0xeb, 0xfb, 0xfd, 0xee, 0xae, 0xe7, 0xf3, 0x39, + 0xdc, 0xef, 0xf7, 0x40, 0x6f, 0x5b, 0x69, 0x1d, 0xb9, 0x42, 0x13, 0xf8, + 0xca, 0x83, 0x5d, 0xb4, 0xac, 0x66, 0xd7, 0x93, 0xb4, 0x42, 0xba, 0x82, + 0xe2, 0x86, 0xe6, 0xea, 0xd1, 0x83, 0x51, 0x4f, 0xd0, 0x21, 0x90, 0xb1, + 0x45, 0xf3, 0x07, 0x0a, 0xb5, 0x22, 0x1d, 0x4f, 0x72, 0x9b, 0xdf, 0x31, + 0xd2, 0x7a, 0x7e, 0x44, 0xab, 0x49, 0xb7, 0xf9, 0xb9, 0x0e, 0x87, 0x66, + 0xc3, 0x40, 0x21, 0xc7, 0x45, 0x2f, 0x80, 0xe2, 0x29, 0x95, 0x6a, 0x70, + 0xa2, 0x14, 0x46, 0x28, 0x4a, 0x12, 0xcb, 0x77, 0xda, 0x83, 0x5e, 0xea, + 0x79, 0xe8, 0x5a, 0x68, 0x5a, 0x0c, 0x88, 0xa5, 0x80, 0x94, 0x8e, 0x58, + 0xa2, 0x48, 0xff, 0xf7, 0x16, 0x89, 0x88, 0xac, 0x5a, 0x40, 0x52, 0xc4, + 0x22, 0xfa, 0x00, 0x14, 0x99, 0xab, 0x9a, 0x1a, 0x12, 0x20, 0xc4, 0x00, + 0xa9, 0x3c, 0x21, 0xe8, 0x39, 0xe9, 0x35, 0xef, 0x64, 0xe6, 0x58, 0x39, + 0x0d, 0x63, 0x58, 0x12, 0x3b, 0x97, 0x87, 0x9e, 0xa2, 0x5e, 0x7c, 0x44, + 0x1c, 0x91, 0x01, 0x03, 0x85, 0x56, 0xac, 0x3c, 0x20, 0xb4, 0xdb, 0x83, + 0x5c, 0x46, 0x88, 0xa2, 0x85, 0xac, 0x45, 0x93, 0xb6, 0x80, 0x2b, 0xb0, + 0x17, 0x0a, 0x04, 0x0e, 0x54, 0x88, 0x5c, 0x40, 0x55, 0xc8, 0x23, 0x37, + 0x4c, 0x3d, 0x9d, 0x35, 0x50, 0xf0, 0x7f, 0x58, 0x60, 0xf9, 0xd2, 0x24, + 0x46, 0xad, 0xd3, 0x8a, 0x27, 0xa4, 0x4d, 0x09, 0x07, 0x65, 0x03, 0x2b, + 0x72, 0x19, 0x35, 0x1e, 0xbd, 0x10, 0x02, 0xc4, 0xb4, 0x50, 0xe6, 0xf1, + 0xb7, 0xe1, 0x55, 0x9e, 0x64, 0xf5, 0x86, 0x12, 0x62, 0xa3, 0x38, 0x26, + 0x24, 0x60, 0x21, 0x68, 0xd1, 0x89, 0xe2, 0xda, 0xc9, 0x6a, 0xaa, 0x68, + 0x94, 0x4b, 0x38, 0x29, 0xe7, 0x45, 0x44, 0x01, 0xa4, 0x04, 0x29, 0xe0, + 0x32, 0x07, 0x73, 0x59, 0xc6, 0x84, 0x27, 0x7a, 0x80, 0x58, 0x00, 0x26, + 0xe4, 0x82, 0x6c, 0x9b, 0x88, 0x36, 0x1e, 0xca, 0x70, 0x6f, 0x83, 0xc0, + 0xb0, 0x2c, 0xd6, 0xcf, 0x93, 0x95, 0xf8, 0xf2, 0xde, 0x4a, 0x33, 0x94, + 0x04, 0xd1, 0x2a, 0x98, 0x12, 0x65, 0xf9, 0xeb, 0x35, 0x75, 0x51, 0x37, + 0x7b, 0xb4, 0x29, 0x71, 0x0d, 0x29, 0x41, 0x79, 0x9d, 0x4a, 0x33, 0xc3, + 0xab, 0x30, 0x66, 0xa6, 0x78, 0xd5, 0x59, 0x7f, 0x15, 0x68, 0xaf, 0x78, + 0x5e, 0xf2, 0xaa, 0x09, 0x2a, 0x61, 0x52, 0x18, 0xed, 0x23, 0xa3, 0x45, + 0x5c, 0xab, 0x60, 0xf0, 0x3b, 0xab, 0x2e, 0x42, 0x50, 0xac, 0x6d, 0x5d, + 0xf1, 0xee, 0xeb, 0xdb, 0xc7, 0x0d, 0xb4, 0xc4, 0xf8, 0x6c, 0x8b, 0x96, + 0x30, 0xda, 0x08, 0xe9, 0x4d, 0x92, 0xd9, 0xe8, 0xf9, 0x81, 0xec, 0xc7, + 0xb2, 0x40, 0x58, 0x19, 0x82, 0x72, 0xbb, 0xe7, 0xb2, 0x55, 0x58, 0x75, + 0xe3, 0x2f, 0xda, 0x39, 0xa0, 0xf2, 0x6e, 0x71, 0x11, 0xed, 0x78, 0xe1, + 0xde, 0xc6, 0x2b, 0x61, 0xba, 0xaa, 0x5b, 0x19, 0x65, 0xb6, 0x51, 0xcf, + 0x6d, 0xaf, 0x4b, 0xc6, 0x6f, 0x3c, 0x43, 0x9f, 0x65, 0x72, 0x87, 0x6b, + 0x12, 0xdb, 0xfa, 0x16, 0xf1, 0xea, 0x22, 0xf2, 0x28, 0x95, 0xb0, 0x5f, + 0xb7, 0x57, 0xaf, 0x72, 0x47, 0x44, 0x07, 0x64, 0x15, 0x83, 0x84, 0x8e, + 0x0f, 0x7a, 0x88, 0xf3, 0x11, 0x01, 0x9d, 0x6c, 0xc8, 0xb6, 0xc8, 0x20, + 0x7c, 0xa3, 0x0c, 0x2f, 0x40, 0x27, 0x2a, 0xb7, 0xdb, 0x2d, 0xc4, 0xc5, + 0x62, 0x51, 0x5d, 0xaf, 0xd7, 0x7c, 0x6c, 0xe2, 0x7d, 0x65, 0xea, 0x2a, + 0x24, 0xbd, 0x79, 0xbd, 0x5e, 0xf9, 0xbc, 0xa1, 0x69, 0x9a, 0x7f, 0xa7, + 0x22, 0xa4, 0xfd, 0x72, 0xb9, 0xf4, 0xb6, 0xc4, 0x3a, 0x73, 0xac, 0xbe, + 0x42, 0x73, 0x49, 0x96, 0x4e, 0x56, 0xba, 0x18, 0xd6, 0x75, 0x5d, 0xed, + 0x76, 0xbb, 0xb0, 0xdf, 0xef, 0xf3, 0x61, 0x84, 0x26, 0xb5, 0x4e, 0x49, + 0x56, 0x4c, 0x73, 0x49, 0x86, 0x64, 0x3f, 0xef, 0x7e, 0x0f, 0x82, 0xe4, + 0x98, 0xcf, 0xe7, 0x7f, 0xfa, 0xfc, 0x64, 0x45, 0x3c, 0x7e, 0x04, 0x18, + 0x00, 0x80, 0xe2, 0x32, 0x3a, 0xb2, 0xea, 0x1b, 0xdf, 0x00, 0x00, 0x00, + 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; +unsigned int gamepad_grip_sense_png_len = 837; diff --git a/test/gamepad_grip_sense.png b/test/gamepad_grip_sense.png new file mode 100644 index 0000000000..1703d507e5 Binary files /dev/null and b/test/gamepad_grip_sense.png differ diff --git a/test/gamepadutils.c b/test/gamepadutils.c index 6fff0b419b..d7fa1c5a45 100644 --- a/test/gamepadutils.c +++ b/test/gamepadutils.c @@ -30,6 +30,7 @@ #include "gamepad_button_background.h" #include "gamepad_wired.h" #include "gamepad_wireless.h" +#include "gamepad_grip_sense.h" #include @@ -305,6 +306,17 @@ static const struct { 355, 200 }, /* SDL_GAMEPAD_BUTTON_LEFT_PADDLE2 */ }; +static const struct +{ + int x; + int y; +} capsense_positions[] = { + { 97, 194 }, /* SDL_GAMEPAD_CAPSENSE_LEFT_STICK */ + { 330, 270 }, /* SDL_GAMEPAD_CAPSENSE_RIGHT_STICK */ + { 50, 260 }, /* SDL_GAMEPAD_CAPSENSE_LEFT_GRIP */ + { 462, 260 }, /* SDL_GAMEPAD_CAPSENSE_RIGHT_GRIP */ +}; + /* This is indexed by gamepad element */ static const struct { @@ -366,6 +378,7 @@ struct GamepadImage SDL_Texture *dual_touchpad_texture; SDL_Texture *button_texture; SDL_Texture *axis_texture; + SDL_Texture *grip_sense_texture; float gamepad_width; float gamepad_height; float face_width; @@ -382,6 +395,8 @@ struct GamepadImage float button_height; float axis_width; float axis_height; + float grip_sense_width; + float grip_sense_height; float x; float y; @@ -392,6 +407,7 @@ struct GamepadImage ControllerDisplayMode display_mode; bool elements[SDL_GAMEPAD_ELEMENT_MAX]; + bool capsense_elements[SDL_GAMEPAD_CAPSENSE_ELEMENT_MAX]; SDL_JoystickConnectionState connection_state; SDL_PowerState battery_state; @@ -453,6 +469,10 @@ GamepadImage *CreateGamepadImage(SDL_Renderer *renderer) SDL_GetTextureSize(ctx->axis_texture, &ctx->axis_width, &ctx->axis_height); SDL_SetTextureColorMod(ctx->axis_texture, 10, 255, 21); + ctx->grip_sense_texture = CreateTexture(renderer, gamepad_grip_sense_png, gamepad_grip_sense_png_len); + SDL_GetTextureSize(ctx->grip_sense_texture, &ctx->grip_sense_width, &ctx->grip_sense_height); + SDL_SetTextureColorMod(ctx->grip_sense_texture, 10, 255, 21); + ctx->showing_front = true; } return ctx; @@ -680,6 +700,7 @@ void ClearGamepadImage(GamepadImage *ctx) } SDL_zeroa(ctx->elements); + SDL_zeroa(ctx->capsense_elements); } void SetGamepadImageElement(GamepadImage *ctx, int element, bool active) @@ -691,6 +712,15 @@ void SetGamepadImageElement(GamepadImage *ctx, int element, bool active) ctx->elements[element] = active; } +static void SetGamepadCapSenseImageElement(GamepadImage *ctx, int capsense_element, bool active) +{ + if (!ctx) { + return; + } + + ctx->capsense_elements[capsense_element] = active; +} + static void FreeTouchpads(GamepadImage *ctx) { if (ctx->touchpads) { @@ -729,6 +759,11 @@ void UpdateGamepadImageFromGamepad(GamepadImage *ctx, SDL_Gamepad *gamepad) SetGamepadImageElement(ctx, button, SDL_GetGamepadButton(gamepad, button)); } + for (i = 0; i < SDL_GAMEPAD_CAPSENSE_COUNT; ++i) { + const SDL_GamepadCapSenseType capsense = (SDL_GamepadCapSenseType)i; + SetGamepadCapSenseImageElement(ctx, capsense, SDL_GetGamepadCapSense(gamepad, capsense)); + } + for (i = 0; i < SDL_GAMEPAD_AXIS_COUNT; ++i) { const SDL_GamepadAxis axis = (SDL_GamepadAxis)i; const Sint16 deadzone = 8000; /* !!! FIXME: real deadzone */ @@ -968,6 +1003,31 @@ void RenderGamepadImage(GamepadImage *ctx) } } } + + if (ctx->display_mode == CONTROLLER_MODE_TESTING && ctx->showing_front) { + SDL_SetTextureAlphaMod(ctx->button_texture, SDL_ALPHA_OPAQUE / 2); + for (i = 0; i < 2; ++i) { + if (ctx->capsense_elements[i]) { + dst.w = ctx->button_width / 2; + dst.h = ctx->button_height / 2; + dst.x = ctx->x + capsense_positions[i].x - dst.w / 2; + dst.y = ctx->y + capsense_positions[i].y - dst.h / 2; + SDL_RenderTexture(ctx->renderer, ctx->button_texture, NULL, &dst); + } + } + SDL_SetTextureAlphaMod(ctx->button_texture, SDL_ALPHA_OPAQUE); + SDL_SetTextureAlphaMod(ctx->grip_sense_texture, SDL_ALPHA_OPAQUE / 2); + for (i = 2; i < SDL_arraysize(capsense_positions); ++i) { + if (ctx->capsense_elements[i]) { + dst.w = ctx->grip_sense_width; + dst.h = ctx->grip_sense_height; + dst.x = ctx->x + capsense_positions[i].x - dst.w / 2; + dst.y = ctx->y + capsense_positions[i].y - dst.h / 2; + SDL_RenderTexture(ctx->renderer, ctx->grip_sense_texture, NULL, &dst); + } + } + SDL_SetTextureAlphaMod(ctx->grip_sense_texture, SDL_ALPHA_OPAQUE); + } } void DestroyGamepadImage(GamepadImage *ctx) @@ -1033,6 +1093,14 @@ static const char *gamepad_axis_names[] = { }; SDL_COMPILE_TIME_ASSERT(gamepad_axis_names, SDL_arraysize(gamepad_axis_names) == SDL_GAMEPAD_AXIS_COUNT); +static const char *capsense_names[] = { + "L.Stick Touch", + "R.Stick Touch", + "L.Grip Sense", + "R.Grip Sense", +}; +SDL_COMPILE_TIME_ASSERT(capsense_names, SDL_arraysize(capsense_names) == SDL_GAMEPAD_CAPSENSE_COUNT); + struct GamepadDisplay { SDL_Renderer *renderer; @@ -1714,6 +1782,28 @@ void RenderGamepadDisplay(GamepadDisplay *ctx, SDL_Gamepad *gamepad) } } + for (i = 0; i < SDL_GAMEPAD_CAPSENSE_COUNT; i++) { + const SDL_GamepadCapSenseType capsense = (SDL_GamepadCapSenseType)i; + if (SDL_GamepadHasCapSense(gamepad, capsense)) { + SDL_snprintf(text, sizeof(text), "%s:", capsense_names[i]); + SDLTest_DrawString(ctx->renderer, x + center - SDL_strlen(text) * FONT_CHARACTER_SIZE, y, text); + + if (SDL_GetGamepadCapSense(gamepad, capsense)) { + SDL_SetTextureColorMod(ctx->button_texture, 10, 255, 21); + } else { + SDL_SetTextureColorMod(ctx->button_texture, 255, 255, 255); + } + + dst.x = x + center + 2.0f; + dst.y = y + FONT_CHARACTER_SIZE / 2 - ctx->button_height / 2; + dst.w = ctx->button_width; + dst.h = ctx->button_height; + SDL_RenderTexture(ctx->renderer, ctx->button_texture, NULL, &dst); + + y += ctx->button_height + 2.0f; + }; + } + has_accel = SDL_GamepadHasSensor(gamepad, SDL_SENSOR_ACCEL); has_gyro = SDL_GamepadHasSensor(gamepad, SDL_SENSOR_GYRO); diff --git a/test/gamepadutils.h b/test/gamepadutils.h index 503238afe1..77b6026957 100644 --- a/test/gamepadutils.h +++ b/test/gamepadutils.h @@ -46,6 +46,15 @@ enum SDL_GAMEPAD_ELEMENT_MAX, }; +enum +{ + SDL_GAMEPAD_CAPSENSE_ELEMENT_LEFT_STICK, + SDL_GAMEPAD_CAPSENSE_ELEMENT_RIGHT_STICK, + SDL_GAMEPAD_CAPSENSE_ELEMENT_LEFT_GRIP, + SDL_GAMEPAD_CAPSENSE_ELEMENT_RIGHT_GRIP, + SDL_GAMEPAD_CAPSENSE_ELEMENT_MAX +}; + #define HIGHLIGHT_COLOR 224, 255, 255, SDL_ALPHA_OPAQUE #define HIGHLIGHT_TEXTURE_MOD 224, 255, 255 #define PRESSED_COLOR 175, 238, 238, SDL_ALPHA_OPAQUE diff --git a/test/testcontroller.c b/test/testcontroller.c index d6276d97e7..f0d3dd4867 100644 --- a/test/testcontroller.c +++ b/test/testcontroller.c @@ -2213,6 +2213,16 @@ SDL_AppResult SDLCALL SDL_AppEvent(void *appstate, SDL_Event *event) } break; + case SDL_EVENT_GAMEPAD_CAPSENSE_TOUCH: + case SDL_EVENT_GAMEPAD_CAPSENSE_RELEASE: +#ifdef VERBOSE_CAPSENSE + SDL_Log("Gamepad %" SDL_PRIu32 " capsense %u %s", + event->gcapsense.which, + event->gcapsense.capsense, + event->gcapsense.down ? "touch" : "release"); +#endif /* VERBOSE_CAPSENSE */ + break; + case SDL_EVENT_MOUSE_BUTTON_DOWN: if (virtual_joystick && controller && controller->joystick == virtual_joystick) { VirtualGamepadMouseDown(event->button.x, event->button.y); diff --git a/test/testutils.c b/test/testutils.c index 99f18831e9..79d5abfeca 100644 --- a/test/testutils.c +++ b/test/testutils.c @@ -47,6 +47,7 @@ static const struct { "gamepad_axis_arrow.png", "GP_AXARW.PNG" }, { "gamepad_wired.png", "GP_WIRED.PNG" }, { "gamepad_wireless.png", "GP_WLESS.PNG" }, + { "gamepad_grip_sense.png", "GP_GRIPS.PNG" }, { "sdl-test_round.png", "SDLROUND.PNG" }, { NULL, NULL } };