Added support for the HORI licensed Steam Controller

This commit is contained in:
Sam Lantinga 2024-09-18 14:08:32 -07:00
parent 481203c074
commit 398dff7c25
13 changed files with 462 additions and 4 deletions

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Gaming.Desktop.x64">
@ -691,6 +691,7 @@
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_shield.c" />
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_stadia.c" />
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_steam.c" />
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_steam_hori.c" />
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_steamdeck.c" />
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_switch.c" />
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_wii.c" />

View File

@ -80,6 +80,7 @@
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_shield.c" />
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_stadia.c" />
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_steam.c" />
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_steam_hori.c" />
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_steamdeck.c" />
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_switch.c" />
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_wii.c" />

View File

@ -569,6 +569,7 @@
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_shield.c" />
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_stadia.c" />
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_steam.c" />
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_steam_hori.c" />
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_steamdeck.c" />
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_switch.c" />
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_wii.c" />

View File

@ -1229,6 +1229,9 @@
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_steam.c">
<Filter>joystick\hidapi</Filter>
</ClCompile>
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_steam_hori.c">
<Filter>joystick\hidapi</Filter>
</ClCompile>
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_steamdeck.c">
<Filter>joystick\hidapi</Filter>
</ClCompile>

View File

@ -544,6 +544,8 @@
F3FA5A232B59ACE000FEAD97 /* yuv_rgb_lsx.c in Sources */ = {isa = PBXBuildFile; fileRef = F3FA5A1A2B59ACE000FEAD97 /* yuv_rgb_lsx.c */; };
F3FA5A242B59ACE000FEAD97 /* yuv_rgb_lsx.h in Headers */ = {isa = PBXBuildFile; fileRef = F3FA5A1B2B59ACE000FEAD97 /* yuv_rgb_lsx.h */; };
F3FA5A252B59ACE000FEAD97 /* yuv_rgb_common.h in Headers */ = {isa = PBXBuildFile; fileRef = F3FA5A1C2B59ACE000FEAD97 /* yuv_rgb_common.h */; };
F3FD042E2C9B755700824C4C /* SDL_hidapi_nintendo.h in Headers */ = {isa = PBXBuildFile; fileRef = F3FD042C2C9B755700824C4C /* SDL_hidapi_nintendo.h */; };
F3FD042F2C9B755700824C4C /* SDL_hidapi_steam_hori.c in Sources */ = {isa = PBXBuildFile; fileRef = F3FD042D2C9B755700824C4C /* SDL_hidapi_steam_hori.c */; };
FA73671D19A540EF004122E4 /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA73671C19A540EF004122E4 /* CoreVideo.framework */; platformFilters = (ios, maccatalyst, macos, tvos, watchos, ); };
/* End PBXBuildFile section */
@ -1115,6 +1117,8 @@
F3FA5A1A2B59ACE000FEAD97 /* yuv_rgb_lsx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = yuv_rgb_lsx.c; sourceTree = "<group>"; };
F3FA5A1B2B59ACE000FEAD97 /* yuv_rgb_lsx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = yuv_rgb_lsx.h; sourceTree = "<group>"; };
F3FA5A1C2B59ACE000FEAD97 /* yuv_rgb_common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = yuv_rgb_common.h; sourceTree = "<group>"; };
F3FD042C2C9B755700824C4C /* SDL_hidapi_nintendo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDL_hidapi_nintendo.h; sourceTree = "<group>"; };
F3FD042D2C9B755700824C4C /* SDL_hidapi_steam_hori.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_steam_hori.c; sourceTree = "<group>"; };
F59C710300D5CB5801000001 /* ReadMe.txt */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = ReadMe.txt; sourceTree = "<group>"; };
F59C710600D5CB5801000001 /* SDL.info */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = SDL.info; sourceTree = "<group>"; };
F5A2EF3900C6A39A01000001 /* BUGS.txt */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; name = BUGS.txt; path = ../../BUGS.txt; sourceTree = SOURCE_ROOT; };
@ -1898,25 +1902,27 @@
A7D8A7BE23E2513E00DCD162 /* hidapi */ = {
isa = PBXGroup;
children = (
A797456F2B2E9D39009D224A /* SDL_hidapi_steamdeck.c */,
F32305FE28939F6400E66D30 /* SDL_hidapi_combined.c */,
A7D8A7C923E2513E00DCD162 /* SDL_hidapi_gamecube.c */,
F3F07D59269640160074468B /* SDL_hidapi_luna.c */,
F3FD042C2C9B755700824C4C /* SDL_hidapi_nintendo.h */,
F388C95428B5F6F600661ECF /* SDL_hidapi_ps3.c */,
A7D8A7C323E2513E00DCD162 /* SDL_hidapi_ps4.c */,
F3A4909D2554D38500E92A8B /* SDL_hidapi_ps5.c */,
A75FDBC423EA380300529352 /* SDL_hidapi_rumble.c */,
A75FDBC323EA380300529352 /* SDL_hidapi_rumble.h */,
A75FDBC423EA380300529352 /* SDL_hidapi_rumble.c */,
9846B07B287A9020000C35C8 /* SDL_hidapi_shield.c */,
F3984CCF25BCC92800374F43 /* SDL_hidapi_stadia.c */,
A75FDAAC23E2795C00529352 /* SDL_hidapi_steam.c */,
F3FD042D2C9B755700824C4C /* SDL_hidapi_steam_hori.c */,
A797456F2B2E9D39009D224A /* SDL_hidapi_steamdeck.c */,
A7D8A7C623E2513E00DCD162 /* SDL_hidapi_switch.c */,
F3D60A8228C16A1800788A3A /* SDL_hidapi_wii.c */,
A7D8A7C223E2513E00DCD162 /* SDL_hidapi_xbox360.c */,
A7D8A7C823E2513E00DCD162 /* SDL_hidapi_xbox360w.c */,
A7D8A7C523E2513E00DCD162 /* SDL_hidapi_xboxone.c */,
A7D8A7C723E2513E00DCD162 /* SDL_hidapijoystick_c.h */,
A7D8A7C423E2513E00DCD162 /* SDL_hidapijoystick.c */,
A7D8A7C723E2513E00DCD162 /* SDL_hidapijoystick_c.h */,
);
path = hidapi;
sourceTree = "<group>";
@ -2671,6 +2677,7 @@
A7D8B26023E2514200DCD162 /* vulkan.h in Headers */,
A7D8B2B423E2514200DCD162 /* vulkan_android.h in Headers */,
A7D8B2A823E2514200DCD162 /* vulkan_core.h in Headers */,
F3FD042E2C9B755700824C4C /* SDL_hidapi_nintendo.h in Headers */,
F3A9AE9B2C8A13C100AAC390 /* SDL_pipeline_gpu.h in Headers */,
E41D20152BA9577D003073FA /* SDL_storage.h in Headers */,
F37E18522BA50E760098C111 /* SDL_dialog.h in Headers */,
@ -3003,6 +3010,7 @@
A7D8B42E23E2514300DCD162 /* SDL_syscond.c in Sources */,
A7D8AADA23E2514100DCD162 /* SDL_syshaptic.c in Sources */,
A7D8BAE523E2514500DCD162 /* e_exp.c in Sources */,
F3FD042F2C9B755700824C4C /* SDL_hidapi_steam_hori.c in Sources */,
A7D8BB8123E2514500DCD162 /* SDL_quit.c in Sources */,
F3FA5A232B59ACE000FEAD97 /* yuv_rgb_lsx.c in Sources */,
A7D8AEA623E2514100DCD162 /* SDL_cocoawindow.m in Sources */,

View File

@ -1654,6 +1654,17 @@ extern "C" {
*/
#define SDL_HINT_JOYSTICK_HIDAPI_STEAMDECK "SDL_JOYSTICK_HIDAPI_STEAMDECK"
/**
* A variable controlling whether the HIDAPI driver for HORI licensed Steam controllers should be used.
*
* This variable can be set to the following values:
* "0" - HIDAPI driver is not used
* "1" - HIDAPI driver is used
*
* The default is the value of SDL_HINT_JOYSTICK_HIDAPI
*/
#define SDL_HINT_JOYSTICK_HIDAPI_STEAM_HORI "SDL_JOYSTICK_HIDAPI_STEAM_HORI"
/**
* A variable controlling whether the HIDAPI driver for Nintendo Switch
* controllers should be used.

View File

@ -806,6 +806,9 @@ static GamepadMapping_t *SDL_CreateMappingForHIDAPIGamepad(SDL_GUID guid)
// The original SHIELD controller has a touchpad and plus/minus buttons as well
SDL_strlcat(mapping_string, "touchpad:b12,misc2:b13,misc3:b14", sizeof(mapping_string));
}
} 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,misc2:b11,misc3:b16,misc4:b17", sizeof(mapping_string));
} else {
switch (SDL_GetGamepadTypeFromGUID(guid, NULL)) {
case SDL_GAMEPAD_TYPE_PS4:

View File

@ -3099,6 +3099,11 @@ bool SDL_IsJoystickSteamController(Uint16 vendor_id, Uint16 product_id)
return eType == k_eControllerType_SteamController || eType == k_eControllerType_SteamControllerV2;
}
bool SDL_IsJoystickHoriSteamController(Uint16 vendor_id, Uint16 product_id)
{
return vendor_id == USB_VENDOR_HORI && (product_id == USB_PRODUCT_HORI_STEAM_CONTROLLER || product_id == USB_PRODUCT_HORI_STEAM_CONTROLLER_BT);
}
bool SDL_IsJoystickSteamDeck(Uint16 vendor_id, Uint16 product_id)
{
EControllerType eType = GuessControllerType(vendor_id, product_id);

View File

@ -129,6 +129,9 @@ extern bool SDL_IsJoystickNVIDIASHIELDController(Uint16 vendor_id, Uint16 produc
// Function to return whether a joystick is a Steam Controller
extern bool SDL_IsJoystickSteamController(Uint16 vendor_id, Uint16 product_id);
// Function to return whether a joystick is a HORI Steam controller
extern bool SDL_IsJoystickHoriSteamController(Uint16 vendor_id, Uint16 product_id);
// Function to return whether a joystick is a Steam Deck
extern bool SDL_IsJoystickSteamDeck(Uint16 vendor_id, Uint16 product_id);

View File

@ -0,0 +1,415 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifdef SDL_JOYSTICK_HIDAPI
#include "../SDL_sysjoystick.h"
#include "SDL_hidapijoystick_c.h"
#include "SDL_hidapi_rumble.h"
#include "../SDL_joystick_c.h"
#ifdef SDL_JOYSTICK_HIDAPI_STEAM_HORI
/* Define this if you want to log all packets from the controller */
/*#define DEBUG_HORI_PROTOCOL*/
#define LOAD16(A, B) (Sint16)((Uint16)(A) | (((Uint16)(B)) << 8))
enum
{
SDL_GAMEPAD_BUTTON_HORI_QAM = 11,
SDL_GAMEPAD_BUTTON_HORI_FR,
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
};
typedef struct
{
Uint8 last_state[USB_PACKET_LENGTH];
Uint64 sensor_ticks;
Uint32 last_tick;
bool wireless;
bool serial_needs_init;
} SDL_DriverSteamHori_Context;
static bool HIDAPI_DriverSteamHori_UpdateDevice(SDL_HIDAPI_Device *device);
static void HIDAPI_DriverSteamHori_RegisterHints(SDL_HintCallback callback, void *userdata)
{
SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_STEAM_HORI, callback, userdata);
}
static void HIDAPI_DriverSteamHori_UnregisterHints(SDL_HintCallback callback, void *userdata)
{
SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_STEAM_HORI, callback, userdata);
}
static bool HIDAPI_DriverSteamHori_IsEnabled(void)
{
return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_STEAM_HORI, SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI, SDL_HIDAPI_DEFAULT));
}
static bool HIDAPI_DriverSteamHori_IsSupportedDevice(SDL_HIDAPI_Device *device, const char *name, SDL_GamepadType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol)
{
return SDL_IsJoystickHoriSteamController(vendor_id, product_id);
}
static bool HIDAPI_DriverSteamHori_InitDevice(SDL_HIDAPI_Device *device)
{
SDL_DriverSteamHori_Context *ctx;
ctx = (SDL_DriverSteamHori_Context *)SDL_calloc(1, sizeof(*ctx));
if (!ctx) {
return false;
}
device->context = ctx;
ctx->serial_needs_init = true;
HIDAPI_SetDeviceName(device, "Wireless HORIPAD For Steam");
return HIDAPI_JoystickConnected(device, NULL);
}
static int HIDAPI_DriverSteamHori_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
{
return -1;
}
static void HIDAPI_DriverSteamHori_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
{
}
static bool HIDAPI_DriverSteamHori_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
{
SDL_DriverSteamHori_Context *ctx = (SDL_DriverSteamHori_Context *)device->context;
SDL_AssertJoysticksLocked();
SDL_zeroa(ctx->last_state);
/* Initialize the joystick capabilities */
joystick->nbuttons = SDL_GAMEPAD_NUM_HORI_BUTTONS;
joystick->naxes = SDL_GAMEPAD_AXIS_COUNT;
joystick->nhats = 1;
ctx->wireless = device->product_id == USB_PRODUCT_HORI_STEAM_CONTROLLER_BT;
if (ctx->wireless && device->serial) {
joystick->serial = SDL_strdup(device->serial);
ctx->serial_needs_init = false;
} else if (!ctx->wireless) {
// Need to actual read from the device to init the serial
HIDAPI_DriverSteamHori_UpdateDevice(device);
}
SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, 250.0f);
SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 250.0f);
return true;
}
static bool HIDAPI_DriverSteamHori_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
{
// Device doesn't support rumble
return SDL_Unsupported();
}
static bool HIDAPI_DriverSteamHori_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
{
return SDL_Unsupported();
}
static Uint32 HIDAPI_DriverSteamHori_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
{
return 0;
}
static bool HIDAPI_DriverSteamHori_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
{
return SDL_Unsupported();
}
static bool HIDAPI_DriverSteamHori_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *data, int size)
{
return SDL_Unsupported();
}
static bool HIDAPI_DriverSteamHori_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, bool enabled)
{
return true;
}
#undef clamp
#define clamp(val, min, max) (((val) > (max)) ? (max) : (((val) < (min)) ? (min) : (val)))
#ifndef DEG2RAD
#define DEG2RAD(x) ((float)(x) * (float)(SDL_PI_F / 180.f))
#endif
//---------------------------------------------------------------------------
// Scale and clamp values to a range
//---------------------------------------------------------------------------
static float RemapValClamped(float val, float A, float B, float C, float D)
{
if (A == B) {
return (val - B) >= 0.0f ? D : C;
} else {
float cVal = (val - A) / (B - A);
cVal = clamp(cVal, 0.0f, 1.0f);
return C + (D - C) * cVal;
}
}
#define REPORT_HEADER_USB 0x07
#define REPORT_HEADER_BT 0x00
static void HIDAPI_DriverSteamHori_HandleStatePacket(SDL_Joystick *joystick, SDL_DriverSteamHori_Context *ctx, Uint8 *data, int size)
{
Sint16 axis;
Uint64 timestamp = SDL_GetTicksNS();
// Make sure it's gamepad state and not OTA FW update info
if (data[0] != REPORT_HEADER_USB && data[0] != REPORT_HEADER_BT) {
/* We don't know how to handle this report */
return;
}
#define READ_STICK_AXIS(offset) \
(data[offset] == 0x80 ? 0 : (Sint16)HIDAPI_RemapVal((float)((int)data[offset] - 0x80), -0x80, 0xff - 0x80, SDL_MIN_SINT16, SDL_MAX_SINT16))
{
axis = READ_STICK_AXIS(1);
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis);
axis = READ_STICK_AXIS(2);
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, axis);
axis = READ_STICK_AXIS(3);
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis);
axis = READ_STICK_AXIS(4);
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis);
}
#undef READ_STICK_AXIS
if (ctx->last_state[5] != data[5]) {
Uint8 hat;
switch (data[5] & 0xF) {
case 0:
hat = SDL_HAT_UP;
break;
case 1:
hat = SDL_HAT_RIGHTUP;
break;
case 2:
hat = SDL_HAT_RIGHT;
break;
case 3:
hat = SDL_HAT_RIGHTDOWN;
break;
case 4:
hat = SDL_HAT_DOWN;
break;
case 5:
hat = SDL_HAT_LEFTDOWN;
break;
case 6:
hat = SDL_HAT_LEFT;
break;
case 7:
hat = SDL_HAT_LEFTUP;
break;
default:
hat = SDL_HAT_CENTERED;
break;
}
SDL_SendJoystickHat(timestamp, joystick, 0, hat);
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SOUTH, ((data[5] & 0x10) != 0));
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_EAST, ((data[5] & 0x20) != 0));
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_HORI_QAM, ((data[5] & 0x40) != 0));
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_WEST, ((data[5] & 0x80) != 0));
}
if (ctx->last_state[6] != data[6]) {
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_NORTH, ((data[6] & 0x01) != 0));
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_HORI_M1 /* M1 */, ((data[6] & 0x02) != 0));
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data[6] & 0x04) != 0));
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data[6] & 0x08) != 0));
// TODO: can we handle the digital trigger mode? The data seems to come through analog regardless of the trigger state
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data[6] & 0x40) != 0));
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data[6] & 0x80) != 0));
}
if (ctx->last_state[7] != data[7]) {
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data[7] & 0x01) != 0));
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_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));
}
if (!ctx->wireless && ctx->serial_needs_init) {
char serial[18];
(void)SDL_snprintf(serial, sizeof(serial), "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x",
data[38], data[39], data[40], data[41], data[42], data[43]);
joystick->serial = SDL_strdup(serial);
ctx->serial_needs_init = false;
}
#define READ_TRIGGER_AXIS(offset) \
(Sint16)(((int)data[offset] * 257) - 32768)
{
axis = READ_TRIGGER_AXIS(8);
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis);
axis = READ_TRIGGER_AXIS(9);
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis);
}
#undef READ_TRIGGER_AXIS
if (1) {
Uint64 sensor_timestamp;
float imu_data[3];
/* 16-bit timestamp */
Uint32 delta;
Uint16 tick = LOAD16(data[10],
data[11]);
if (ctx->last_tick < tick) {
delta = (tick - ctx->last_tick);
} else {
delta = (SDL_MAX_UINT16 - ctx->last_tick + tick + 1);
}
ctx->last_tick = tick;
ctx->sensor_ticks += delta;
/* Sensor timestamp is in 1us units, but there seems to be some issues with the values reported from the device */
sensor_timestamp = timestamp; // if the values were good we woudl call SDL_US_TO_NS(ctx->sensor_ticks);
const float accelScale = SDL_STANDARD_GRAVITY * 8 / 32768.0f;
const float gyroScale = DEG2RAD(2048);
imu_data[1] = RemapValClamped(-1.0f * LOAD16(data[12], data[13]), INT16_MIN, INT16_MAX, -gyroScale, gyroScale);
imu_data[2] = RemapValClamped(-1.0f * LOAD16(data[14], data[15]), INT16_MIN, INT16_MAX, -gyroScale, gyroScale);
imu_data[0] = RemapValClamped(-1.0f * LOAD16(data[16], data[17]), INT16_MIN, INT16_MAX, -gyroScale, gyroScale);
SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_GYRO, sensor_timestamp, imu_data, 3);
// SDL_Log("%u %f, %f, %f \n", data[0], imu_data[0], imu_data[1], imu_data[2] );
imu_data[2] = LOAD16(data[18], data[19]) * accelScale;
imu_data[1] = -1 * LOAD16(data[20], data[21]) * accelScale;
imu_data[0] = LOAD16(data[22], data[23]) * accelScale;
SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_ACCEL, sensor_timestamp, imu_data, 3);
}
if (ctx->last_state[24] != data[24]) {
bool bCharging = (data[24] & 0x10) != 0;
int percent = (data[24] & 0xF) * 10;
SDL_PowerState state;
if (bCharging) {
state = SDL_POWERSTATE_CHARGING;
} else if (ctx->wireless) {
state = SDL_POWERSTATE_ON_BATTERY;
} else {
state = SDL_POWERSTATE_CHARGED;
}
SDL_SendJoystickPowerInfo(joystick, state, percent);
}
SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
}
static bool HIDAPI_DriverSteamHori_UpdateDevice(SDL_HIDAPI_Device *device)
{
SDL_DriverSteamHori_Context *ctx = (SDL_DriverSteamHori_Context *)device->context;
SDL_Joystick *joystick = NULL;
Uint8 data[USB_PACKET_LENGTH];
int size = 0;
if (device->num_joysticks > 0) {
joystick = SDL_GetJoystickFromID(device->joysticks[0]);
} else {
return false;
}
while ((size = SDL_hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) {
#ifdef DEBUG_HORI_PROTOCOL
HIDAPI_DumpPacket("Google Hori packet: size = %d", data, size);
#endif
if (!joystick) {
continue;
}
HIDAPI_DriverSteamHori_HandleStatePacket(joystick, ctx, data, size);
}
if (size < 0) {
/* Read error, device is disconnected */
HIDAPI_JoystickDisconnected(device, device->joysticks[0]);
}
return (size >= 0);
}
static void HIDAPI_DriverSteamHori_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
{
}
static void HIDAPI_DriverSteamHori_FreeDevice(SDL_HIDAPI_Device *device)
{
}
SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSteamHori = {
SDL_HINT_JOYSTICK_HIDAPI_STEAM_HORI,
true,
HIDAPI_DriverSteamHori_RegisterHints,
HIDAPI_DriverSteamHori_UnregisterHints,
HIDAPI_DriverSteamHori_IsEnabled,
HIDAPI_DriverSteamHori_IsSupportedDevice,
HIDAPI_DriverSteamHori_InitDevice,
HIDAPI_DriverSteamHori_GetDevicePlayerIndex,
HIDAPI_DriverSteamHori_SetDevicePlayerIndex,
HIDAPI_DriverSteamHori_UpdateDevice,
HIDAPI_DriverSteamHori_OpenJoystick,
HIDAPI_DriverSteamHori_RumbleJoystick,
HIDAPI_DriverSteamHori_RumbleJoystickTriggers,
HIDAPI_DriverSteamHori_GetJoystickCapabilities,
HIDAPI_DriverSteamHori_SetJoystickLED,
HIDAPI_DriverSteamHori_SendJoystickEffect,
HIDAPI_DriverSteamHori_SetJoystickSensorsEnabled,
HIDAPI_DriverSteamHori_CloseJoystick,
HIDAPI_DriverSteamHori_FreeDevice,
};
#endif /* SDL_JOYSTICK_HIDAPI_STEAM_HORI */
#endif /* SDL_JOYSTICK_HIDAPI */

View File

@ -67,6 +67,9 @@ static SDL_HIDAPI_DeviceDriver *SDL_HIDAPI_drivers[] = {
#ifdef SDL_JOYSTICK_HIDAPI_STEAM
&SDL_HIDAPI_DriverSteam,
#endif
#ifdef SDL_JOYSTICK_HIDAPI_STEAM_HORI
&SDL_HIDAPI_DriverSteamHori,
#endif
#ifdef SDL_JOYSTICK_HIDAPI_STEAMDECK
&SDL_HIDAPI_DriverSteamDeck,
#endif

View File

@ -39,6 +39,7 @@
#define SDL_JOYSTICK_HIDAPI_XBOX360
#define SDL_JOYSTICK_HIDAPI_XBOXONE
#define SDL_JOYSTICK_HIDAPI_SHIELD
#define SDL_JOYSTICK_HIDAPI_STEAM_HORI
// Joystick capability definitions
#define SDL_JOYSTICK_CAP_MONO_LED 0x00000001
@ -150,6 +151,7 @@ extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverWii;
extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360;
extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360W;
extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXboxOne;
extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSteamHori;
// Return true if a HID device is present and supported as a joystick of the given type
extern bool HIDAPI_IsDeviceTypePresent(SDL_GamepadType type);

View File

@ -74,6 +74,8 @@
#define USB_PRODUCT_HORI_FIGHTING_STICK_ALPHA_PS4 0x011c
#define USB_PRODUCT_HORI_FIGHTING_STICK_ALPHA_PS5 0x0184
#define USB_PRODUCT_HORI_FIGHTING_STICK_ALPHA_PS5 0x0184
#define USB_PRODUCT_HORI_STEAM_CONTROLLER 0x01AB
#define USB_PRODUCT_HORI_STEAM_CONTROLLER_BT 0x0196
#define USB_PRODUCT_LOGITECH_F310 0xc216
#define USB_PRODUCT_LOGITECH_CHILLSTREAM 0xcad1
#define USB_PRODUCT_MADCATZ_SAITEK_SIDE_PANEL_CONTROL_DECK 0x2218