mirror of
https://github.com/libsdl-org/SDL.git
synced 2026-02-13 07:13:35 +00:00
Added support for the new Steam Controller
This commit is contained in:
@@ -82,16 +82,16 @@
|
||||
<LibraryPath Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">C:\Program Files %28x86%29\Microsoft DirectX SDK %28June 2010%29\Lib\x86;$(LibraryPath)</LibraryPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<IncludePath>$(ProjectDir)/../../src;$(IncludePath)</IncludePath>
|
||||
<IncludePath>$(ProjectDir)/../../src;$(ProjectDir)/../../src/core/windows;$(IncludePath)</IncludePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<IncludePath>$(ProjectDir)/../../src;$(IncludePath)</IncludePath>
|
||||
<IncludePath>$(ProjectDir)/../../src;$(ProjectDir)/../../src/core/windows;$(IncludePath)</IncludePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<IncludePath>$(ProjectDir)/../../src;$(IncludePath)</IncludePath>
|
||||
<IncludePath>$(ProjectDir)/../../src;$(ProjectDir)/../../src/core/windows;$(IncludePath)</IncludePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<IncludePath>$(ProjectDir)/../../src;$(IncludePath)</IncludePath>
|
||||
<IncludePath>$(ProjectDir)/../../src;$(ProjectDir)/../../src/core/windows;$(IncludePath)</IncludePath>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<PreBuildEvent>
|
||||
@@ -625,6 +625,7 @@
|
||||
<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_steam_triton.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_switch2.c" />
|
||||
|
||||
@@ -1660,6 +1660,10 @@
|
||||
<ClCompile Include="..\..\src\storage\generic\SDL_genericstorage.c" />
|
||||
<ClCompile Include="..\..\src\storage\steam\SDL_steamstorage.c" />
|
||||
<ClCompile Include="..\..\src\storage\SDL_storage.c" />
|
||||
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_steam_triton.c">
|
||||
<Filter>joystick\hidapi</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ClCompile Include="..\..\src\events\SDL_eventwatch.c" />
|
||||
<ClCompile Include="..\..\src\core\windows\pch_cpp.cpp">
|
||||
<Filter>core\windows</Filter>
|
||||
|
||||
@@ -415,6 +415,7 @@
|
||||
F386F6F92884663E001840AA /* SDL_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = F386F6E62884663E001840AA /* SDL_utils.c */; };
|
||||
F388C95528B5F6F700661ECF /* SDL_hidapi_ps3.c in Sources */ = {isa = PBXBuildFile; fileRef = F388C95428B5F6F600661ECF /* SDL_hidapi_ps3.c */; };
|
||||
F39344CE2E99771B0056986F /* SDL_dlopennote.h in Headers */ = {isa = PBXBuildFile; fileRef = F39344CD2E99771B0056986F /* SDL_dlopennote.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
F38C72492CEEB1DE000B0A90 /* SDL_hidapi_steam_triton.c in Sources */ = {isa = PBXBuildFile; fileRef = F38C72482CEEB1DE000B0A90 /* SDL_hidapi_steam_triton.c */; };
|
||||
F395BF6525633B2400942BFF /* SDL_crc32.c in Sources */ = {isa = PBXBuildFile; fileRef = F395BF6425633B2400942BFF /* SDL_crc32.c */; };
|
||||
F395C1932569C68F00942BFF /* SDL_iokitjoystick_c.h in Headers */ = {isa = PBXBuildFile; fileRef = F395C1912569C68E00942BFF /* SDL_iokitjoystick_c.h */; };
|
||||
F395C19C2569C68F00942BFF /* SDL_iokitjoystick.c in Sources */ = {isa = PBXBuildFile; fileRef = F395C1922569C68E00942BFF /* SDL_iokitjoystick.c */; };
|
||||
@@ -998,6 +999,7 @@
|
||||
F386F6E62884663E001840AA /* SDL_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_utils.c; sourceTree = "<group>"; };
|
||||
F388C95428B5F6F600661ECF /* SDL_hidapi_ps3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_ps3.c; sourceTree = "<group>"; };
|
||||
F39344CD2E99771B0056986F /* SDL_dlopennote.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDL_dlopennote.h; sourceTree = "<group>"; };
|
||||
F38C72482CEEB1DE000B0A90 /* SDL_hidapi_steam_triton.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_steam_triton.c; sourceTree = "<group>"; };
|
||||
F395BF6425633B2400942BFF /* SDL_crc32.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_crc32.c; sourceTree = "<group>"; };
|
||||
F395C1912569C68E00942BFF /* SDL_iokitjoystick_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_iokitjoystick_c.h; sourceTree = "<group>"; };
|
||||
F395C1922569C68E00942BFF /* SDL_iokitjoystick.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_iokitjoystick.c; sourceTree = "<group>"; };
|
||||
@@ -1962,6 +1964,7 @@
|
||||
F3984CCF25BCC92800374F43 /* SDL_hidapi_stadia.c */,
|
||||
A75FDAAC23E2795C00529352 /* SDL_hidapi_steam.c */,
|
||||
F3FD042D2C9B755700824C4C /* SDL_hidapi_steam_hori.c */,
|
||||
F38C72482CEEB1DE000B0A90 /* SDL_hidapi_steam_triton.c */,
|
||||
A797456F2B2E9D39009D224A /* SDL_hidapi_steamdeck.c */,
|
||||
A7D8A7C623E2513E00DCD162 /* SDL_hidapi_switch.c */,
|
||||
A7D8A7C623E2513E00DCD163 /* SDL_hidapi_switch2.c */,
|
||||
@@ -3009,6 +3012,7 @@
|
||||
F316ABD92B5C3185002EF551 /* SDL_memcpy.c in Sources */,
|
||||
A7D8B97A23E2514400DCD162 /* SDL_render.c in Sources */,
|
||||
A7D8ABD323E2514100DCD162 /* SDL_stretch.c in Sources */,
|
||||
F38C72492CEEB1DE000B0A90 /* SDL_hidapi_steam_triton.c in Sources */,
|
||||
A7D8AC3923E2514100DCD162 /* SDL_blit_copy.c in Sources */,
|
||||
A7D8B5CF23E2514300DCD162 /* SDL_syspower.m in Sources */,
|
||||
F3B439512C935C2400792030 /* SDL_dummyprocess.c in Sources */,
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
|
||||
#define VALVE_USB_VID 0x28DE
|
||||
#define D0G_BLE2_PID 0x1106
|
||||
#define TRITON_BLE_PID 0x1303
|
||||
|
||||
typedef uint32_t uint32;
|
||||
typedef uint64_t uint64;
|
||||
@@ -76,7 +77,8 @@ typedef uint64_t uint64;
|
||||
#define VALVE_SERVICE @"100F6C32-1735-4313-B402-38567131E5F3"
|
||||
|
||||
// (READ/NOTIFICATIONS)
|
||||
#define VALVE_INPUT_CHAR @"100F6C33-1735-4313-B402-38567131E5F3"
|
||||
#define VALVE_INPUT_CHAR_0x1106 @"100F6C33-1735-4313-B402-38567131E5F3"
|
||||
#define VALVE_INPUT_CHAR_0x1303 @"100F6C77-1735-4313-B402-38567131E5F3"
|
||||
|
||||
// (READ/WRITE)
|
||||
#define VALVE_REPORT_CHAR @"100F6C34-1735-4313-B402-38567131E5F3"
|
||||
@@ -101,21 +103,7 @@ typedef struct
|
||||
|
||||
typedef struct {
|
||||
uint8_t id;
|
||||
union {
|
||||
bluetoothSegment segment;
|
||||
struct {
|
||||
uint8_t segmentHeader;
|
||||
uint8_t featureReportMessageID;
|
||||
uint8_t length;
|
||||
uint8_t settingIdentifier;
|
||||
union {
|
||||
uint16_t usPayload;
|
||||
uint32_t uPayload;
|
||||
uint64_t ulPayload;
|
||||
uint8_t ucPayload[15];
|
||||
};
|
||||
};
|
||||
};
|
||||
bluetoothSegment segment;
|
||||
} hidFeatureReport;
|
||||
|
||||
#pragma pack(pop)
|
||||
@@ -125,34 +113,62 @@ size_t GetBluetoothSegmentSize(bluetoothSegment *segment)
|
||||
return segment->length + 3;
|
||||
}
|
||||
|
||||
#define RingBuffer_cbElem 19
|
||||
#define RingBuffer_nElem 4096
|
||||
#define RingBuffer_nElem 256
|
||||
|
||||
typedef struct {
|
||||
int _first, _last;
|
||||
uint8_t _data[ ( RingBuffer_nElem * RingBuffer_cbElem ) ];
|
||||
int _cbElem;
|
||||
uint8_t *_data;
|
||||
pthread_mutex_t accessLock;
|
||||
} RingBuffer;
|
||||
|
||||
static void RingBuffer_init( RingBuffer *this )
|
||||
static RingBuffer *RingBuffer_alloc( int cbElem )
|
||||
{
|
||||
RingBuffer *this = (RingBuffer *)malloc( sizeof(*this) );
|
||||
if (!this)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
this->_first = -1;
|
||||
this->_last = 0;
|
||||
this->_cbElem = cbElem;
|
||||
this->_data = (uint8_t *)malloc(RingBuffer_nElem * cbElem);
|
||||
if ( !this->_data )
|
||||
{
|
||||
free( this );
|
||||
return NULL;
|
||||
}
|
||||
pthread_mutex_init( &this->accessLock, 0 );
|
||||
return this;
|
||||
}
|
||||
|
||||
static void RingBuffer_free( RingBuffer *this )
|
||||
{
|
||||
if ( this )
|
||||
{
|
||||
free( this->_data );
|
||||
free( this );
|
||||
}
|
||||
}
|
||||
|
||||
static bool RingBuffer_write( RingBuffer *this, const uint8_t *src )
|
||||
{
|
||||
if ( !this )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
pthread_mutex_lock( &this->accessLock );
|
||||
memcpy( &this->_data[ this->_last ], src, RingBuffer_cbElem );
|
||||
memcpy( &this->_data[ this->_last ], src, this->_cbElem );
|
||||
if ( this->_first == -1 )
|
||||
{
|
||||
this->_first = this->_last;
|
||||
}
|
||||
this->_last = ( this->_last + RingBuffer_cbElem ) % (RingBuffer_nElem * RingBuffer_cbElem);
|
||||
this->_last = ( this->_last + this->_cbElem ) % (RingBuffer_nElem * this->_cbElem);
|
||||
if ( this->_last == this->_first )
|
||||
{
|
||||
this->_first = ( this->_first + RingBuffer_cbElem ) % (RingBuffer_nElem * RingBuffer_cbElem);
|
||||
this->_first = ( this->_first + this->_cbElem ) % (RingBuffer_nElem * this->_cbElem);
|
||||
pthread_mutex_unlock( &this->accessLock );
|
||||
return false;
|
||||
}
|
||||
@@ -162,14 +178,19 @@ static bool RingBuffer_write( RingBuffer *this, const uint8_t *src )
|
||||
|
||||
static bool RingBuffer_read( RingBuffer *this, uint8_t *dst )
|
||||
{
|
||||
if ( !this )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
pthread_mutex_lock( &this->accessLock );
|
||||
if ( this->_first == -1 )
|
||||
{
|
||||
pthread_mutex_unlock( &this->accessLock );
|
||||
return false;
|
||||
}
|
||||
memcpy( dst, &this->_data[ this->_first ], RingBuffer_cbElem );
|
||||
this->_first = ( this->_first + RingBuffer_cbElem ) % (RingBuffer_nElem * RingBuffer_cbElem);
|
||||
memcpy( dst, &this->_data[ this->_first ], this->_cbElem );
|
||||
this->_first = ( this->_first + this->_cbElem ) % (RingBuffer_nElem * this->_cbElem);
|
||||
if ( this->_first == this->_last )
|
||||
{
|
||||
this->_first = -1;
|
||||
@@ -191,12 +212,14 @@ typedef enum
|
||||
|
||||
@interface HIDBLEDevice : NSObject <CBPeripheralDelegate>
|
||||
{
|
||||
RingBuffer _inputReports;
|
||||
uint8_t _featureReport[20];
|
||||
RingBuffer *_inputReports;
|
||||
NSData *_featureReport;
|
||||
NSMutableDictionary *_outputReports;
|
||||
BLEDeviceWaitState _waitStateForReadFeatureReport;
|
||||
BLEDeviceWaitState _waitStateForWriteFeatureReport;
|
||||
}
|
||||
|
||||
@property (nonatomic, readwrite) uint16_t pid;
|
||||
@property (nonatomic, readwrite) bool connected;
|
||||
@property (nonatomic, readwrite) bool ready;
|
||||
|
||||
@@ -205,6 +228,7 @@ typedef enum
|
||||
@property (nonatomic, strong) CBCharacteristic *bleCharacteristicReport;
|
||||
|
||||
- (id)initWithPeripheral:(CBPeripheral *)peripheral;
|
||||
- (void)onDisconnect;
|
||||
|
||||
@end
|
||||
|
||||
@@ -278,8 +302,7 @@ typedef enum
|
||||
HIDBLEDevice *steamController = [self.deviceMap objectForKey:peripheral];
|
||||
if ( steamController )
|
||||
{
|
||||
steamController.connected = NO;
|
||||
steamController.ready = NO;
|
||||
[steamController onDisconnect];
|
||||
[self.centralManager cancelPeripheralConnection:peripheral];
|
||||
}
|
||||
}
|
||||
@@ -474,8 +497,7 @@ typedef enum
|
||||
HIDBLEDevice *steamController = [self.deviceMap objectForKey:peripheral];
|
||||
if ( steamController )
|
||||
{
|
||||
steamController.connected = NO;
|
||||
steamController.ready = NO;
|
||||
[steamController onDisconnect];
|
||||
[self.deviceMap removeObjectForKey:peripheral];
|
||||
}
|
||||
}
|
||||
@@ -500,12 +522,14 @@ static void process_pending_events(void)
|
||||
{
|
||||
if ( self = [super init] )
|
||||
{
|
||||
RingBuffer_init( &_inputReports );
|
||||
self.pid = 0;
|
||||
_inputReports = NULL;
|
||||
_outputReports = [[NSMutableDictionary alloc] init];
|
||||
_connected = NO;
|
||||
_ready = NO;
|
||||
self.bleSteamController = nil;
|
||||
self.bleCharacteristicInput = nil;
|
||||
self.bleCharacteristicReport = nil;
|
||||
_connected = NO;
|
||||
_ready = NO;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -514,7 +538,9 @@ static void process_pending_events(void)
|
||||
{
|
||||
if ( self = [super init] )
|
||||
{
|
||||
RingBuffer_init( &_inputReports );
|
||||
self.pid = 0;
|
||||
_inputReports = NULL;
|
||||
_outputReports = [[NSMutableDictionary alloc] init];
|
||||
_connected = NO;
|
||||
_ready = NO;
|
||||
self.bleSteamController = peripheral;
|
||||
@@ -528,6 +554,18 @@ static void process_pending_events(void)
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)onDisconnect
|
||||
{
|
||||
self.connected = NO;
|
||||
self.ready = NO;
|
||||
|
||||
if ( _inputReports )
|
||||
{
|
||||
RingBuffer_free( _inputReports );
|
||||
_inputReports = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setConnected:(bool)connected
|
||||
{
|
||||
_connected = connected;
|
||||
@@ -543,94 +581,134 @@ static void process_pending_events(void)
|
||||
|
||||
- (size_t)read_input_report:(uint8_t *)dst
|
||||
{
|
||||
if ( RingBuffer_read( &_inputReports, dst+1 ) )
|
||||
if ( RingBuffer_read( _inputReports, dst+1 ) )
|
||||
{
|
||||
*dst = 0x03;
|
||||
return 20;
|
||||
switch ( self.pid )
|
||||
{
|
||||
case D0G_BLE2_PID:
|
||||
*dst = 0x03;
|
||||
break;
|
||||
case TRITON_BLE_PID:
|
||||
*dst = 0x42;
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
return _inputReports->_cbElem + 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (int)send_report:(const uint8_t *)data length:(size_t)length
|
||||
{
|
||||
if ( self.pid == D0G_BLE2_PID )
|
||||
{
|
||||
[_bleSteamController writeValue:[NSData dataWithBytes:data length:length] forCharacteristic:_bleCharacteristicReport type:CBCharacteristicWriteWithResponse];
|
||||
return (int)length;
|
||||
}
|
||||
|
||||
- (int)send_feature_report:(hidFeatureReport *)report
|
||||
// We need to look up the correct characteristic for this output report
|
||||
if ( length > 0 )
|
||||
{
|
||||
CBCharacteristic *aChar = [_outputReports objectForKey:[NSNumber numberWithInt:data[0]]];
|
||||
if ( aChar != nil )
|
||||
{
|
||||
[_bleSteamController writeValue:[NSData dataWithBytes:&data[1] length:(length - 1)] forCharacteristic:aChar type:CBCharacteristicWriteWithResponse];
|
||||
return (int)length;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
- (int)send_feature_report:(hidFeatureReport *)report length:(size_t)length
|
||||
{
|
||||
#if FEATURE_REPORT_LOGGING
|
||||
uint8_t *reportBytes = (uint8_t *)report;
|
||||
|
||||
NSLog( @"HIDBLE:send_feature_report (%02zu/19) [%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x]", GetBluetoothSegmentSize( report->segment ),
|
||||
NSLog( @"HIDBLE:send_feature_report (%02zu/19) [%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x]", length,
|
||||
reportBytes[1], reportBytes[2], reportBytes[3], reportBytes[4], reportBytes[5], reportBytes[6],
|
||||
reportBytes[7], reportBytes[8], reportBytes[9], reportBytes[10], reportBytes[11], reportBytes[12],
|
||||
reportBytes[13], reportBytes[14], reportBytes[15], reportBytes[16], reportBytes[17], reportBytes[18],
|
||||
reportBytes[19] );
|
||||
#endif
|
||||
|
||||
int sendSize = (int)GetBluetoothSegmentSize( &report->segment );
|
||||
if ( sendSize > 20 )
|
||||
sendSize = 20;
|
||||
|
||||
#if 1
|
||||
// fire-and-forget - we are going to not wait for the response here because all Steam Controller BLE send_feature_report's are ignored,
|
||||
// except errors.
|
||||
[_bleSteamController writeValue:[NSData dataWithBytes:&report->segment length:sendSize] forCharacteristic:_bleCharacteristicReport type:CBCharacteristicWriteWithResponse];
|
||||
[_bleSteamController writeValue:[NSData dataWithBytes:&report->segment length:MIN(length, 64)] forCharacteristic:_bleCharacteristicReport type:CBCharacteristicWriteWithResponse];
|
||||
|
||||
// pretend we received a result anybody cares about
|
||||
return 19;
|
||||
return (int)length;
|
||||
|
||||
#else
|
||||
// this is technically the correct send_feature_report logic if you want to make sure it gets through and is
|
||||
// acknowledged or errors out
|
||||
_waitStateForWriteFeatureReport = BLEDeviceWaitState_Waiting;
|
||||
[_bleSteamController writeValue:[NSData dataWithBytes:&report->segment length:sendSize
|
||||
[_bleSteamController writeValue:[NSData dataWithBytes:&report->segment length:MIN(length, 64)
|
||||
] forCharacteristic:_bleCharacteristicReport type:CBCharacteristicWriteWithResponse];
|
||||
|
||||
while ( _waitStateForWriteFeatureReport == BLEDeviceWaitState_Waiting )
|
||||
while ( _connected && _waitStateForWriteFeatureReport == BLEDeviceWaitState_Waiting )
|
||||
{
|
||||
process_pending_events();
|
||||
}
|
||||
|
||||
if ( _waitStateForWriteFeatureReport == BLEDeviceWaitState_Error )
|
||||
if ( !_connected || _waitStateForWriteFeatureReport == BLEDeviceWaitState_Error )
|
||||
{
|
||||
_waitStateForWriteFeatureReport = BLEDeviceWaitState_None;
|
||||
return -1;
|
||||
}
|
||||
|
||||
_waitStateForWriteFeatureReport = BLEDeviceWaitState_None;
|
||||
return 19;
|
||||
return (int)length;
|
||||
#endif
|
||||
}
|
||||
|
||||
- (int)get_feature_report:(uint8_t)feature into:(uint8_t *)buffer
|
||||
- (int)get_feature_report:(uint8_t)feature into:(uint8_t *)buffer length:(size_t)length
|
||||
{
|
||||
_waitStateForReadFeatureReport = BLEDeviceWaitState_Waiting;
|
||||
[_bleSteamController readValueForCharacteristic:_bleCharacteristicReport];
|
||||
|
||||
while ( _waitStateForReadFeatureReport == BLEDeviceWaitState_Waiting )
|
||||
process_pending_events();
|
||||
while ( _connected && _waitStateForReadFeatureReport == BLEDeviceWaitState_Waiting )
|
||||
{
|
||||
process_pending_events();
|
||||
}
|
||||
|
||||
if ( _waitStateForReadFeatureReport == BLEDeviceWaitState_Error )
|
||||
if ( !_connected || _waitStateForReadFeatureReport == BLEDeviceWaitState_Error )
|
||||
{
|
||||
_waitStateForReadFeatureReport = BLEDeviceWaitState_None;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy( buffer, _featureReport, sizeof(_featureReport) );
|
||||
int amount = 0;
|
||||
if ( _featureReport.length > 0 )
|
||||
{
|
||||
uint8_t *data = (uint8_t *)_featureReport.bytes;
|
||||
if ( *data == *buffer )
|
||||
{
|
||||
amount = (int)MIN( length, _featureReport.length );
|
||||
memcpy( buffer, _featureReport.bytes, amount );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Leave the report in the buffer
|
||||
amount = (int)MIN( length - 1, _featureReport.length );
|
||||
memcpy( &buffer[ 1 ], _featureReport.bytes, amount );
|
||||
++amount;
|
||||
}
|
||||
}
|
||||
|
||||
_waitStateForReadFeatureReport = BLEDeviceWaitState_None;
|
||||
|
||||
#if FEATURE_REPORT_LOGGING
|
||||
NSLog( @"HIDBLE:get_feature_report (19) [%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x]",
|
||||
buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6],
|
||||
buffer[7], buffer[8], buffer[9], buffer[10], buffer[11], buffer[12],
|
||||
buffer[13], buffer[14], buffer[15], buffer[16], buffer[17], buffer[18],
|
||||
buffer[19] );
|
||||
NSLog( @"HIDBLE:get_feature_report (%lu/%zu) [%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x]",
|
||||
_featureReport.length, length,
|
||||
buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6],
|
||||
buffer[7], buffer[8], buffer[9], buffer[10], buffer[11], buffer[12],
|
||||
buffer[13], buffer[14], buffer[15], buffer[16], buffer[17], buffer[18],
|
||||
buffer[19] );
|
||||
#endif
|
||||
|
||||
return 19;
|
||||
return amount;
|
||||
}
|
||||
|
||||
#pragma mark CBPeripheralDelegate Implementation
|
||||
@@ -667,8 +745,14 @@ static void process_pending_events(void)
|
||||
{
|
||||
NSLog( @"Found Characteristic %@", aChar );
|
||||
|
||||
if ( [aChar.UUID isEqual:[CBUUID UUIDWithString:VALVE_INPUT_CHAR]] )
|
||||
if ( [aChar.UUID isEqual:[CBUUID UUIDWithString:VALVE_INPUT_CHAR_0x1106]] )
|
||||
{
|
||||
self.pid = D0G_BLE2_PID;
|
||||
self.bleCharacteristicInput = aChar;
|
||||
}
|
||||
else if ( [aChar.UUID isEqual:[CBUUID UUIDWithString:VALVE_INPUT_CHAR_0x1303]] )
|
||||
{
|
||||
self.pid = TRITON_BLE_PID;
|
||||
self.bleCharacteristicInput = aChar;
|
||||
}
|
||||
else if ( [aChar.UUID isEqual:[CBUUID UUIDWithString:VALVE_REPORT_CHAR]] )
|
||||
@@ -676,6 +760,21 @@ static void process_pending_events(void)
|
||||
self.bleCharacteristicReport = aChar;
|
||||
[self.bleSteamController discoverDescriptorsForCharacteristic: aChar];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSString *UUIDString = [aChar.UUID UUIDString];
|
||||
int report_id = 0;
|
||||
if ( sscanf( UUIDString.UTF8String, "100F6C%x", &report_id ) == 1 && report_id > 0x35 )
|
||||
{
|
||||
report_id -= 0x35;
|
||||
//NSLog( @"Found characteristic for output report 0x%.2x", report_id );
|
||||
|
||||
if (report_id >= 0x80) {
|
||||
// An output report
|
||||
[_outputReports setObject:aChar forKey:[NSNumber numberWithInt:report_id]];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -690,17 +789,33 @@ static void process_pending_events(void)
|
||||
if ( self.ready == NO )
|
||||
{
|
||||
self.ready = YES;
|
||||
if ( _inputReports == NULL )
|
||||
{
|
||||
int cbElem = 0;
|
||||
switch ( self.pid )
|
||||
{
|
||||
case D0G_BLE2_PID:
|
||||
cbElem = 19;
|
||||
break;
|
||||
case TRITON_BLE_PID:
|
||||
cbElem = 53;
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
_inputReports = RingBuffer_alloc( cbElem );
|
||||
}
|
||||
HIDBLEManager.sharedInstance.nPendingPairs -= 1;
|
||||
}
|
||||
|
||||
if ( [characteristic.UUID isEqual:_bleCharacteristicInput.UUID] )
|
||||
{
|
||||
NSData *data = [characteristic value];
|
||||
if ( data.length != 19 )
|
||||
if ( _inputReports && data.length != _inputReports->_cbElem )
|
||||
{
|
||||
NSLog( @"HIDBLE: incoming data is %lu bytes should be exactly 19", (unsigned long)data.length );
|
||||
NSLog( @"HIDBLE: incoming data is %lu bytes should be exactly %d", (unsigned long)data.length, _inputReports->_cbElem );
|
||||
}
|
||||
if ( !RingBuffer_write( &_inputReports, (const uint8_t *)data.bytes ) )
|
||||
if ( !RingBuffer_write( _inputReports, (const uint8_t *)data.bytes ) )
|
||||
{
|
||||
uint64_t ticksNow = mach_approximate_time();
|
||||
if ( ticksNow - s_ticksLastOverflowReport > (5ull * NSEC_PER_SEC / 10) )
|
||||
@@ -712,8 +827,6 @@ static void process_pending_events(void)
|
||||
}
|
||||
else if ( [characteristic.UUID isEqual:_bleCharacteristicReport.UUID] )
|
||||
{
|
||||
memset( _featureReport, 0, sizeof(_featureReport) );
|
||||
|
||||
if ( error != nil )
|
||||
{
|
||||
NSLog( @"HIDBLE: get_feature_report error: %@", error );
|
||||
@@ -721,12 +834,7 @@ static void process_pending_events(void)
|
||||
}
|
||||
else
|
||||
{
|
||||
NSData *data = [characteristic value];
|
||||
if ( data.length != 20 )
|
||||
{
|
||||
NSLog( @"HIDBLE: incoming data is %lu bytes should be exactly 20", (unsigned long)data.length );
|
||||
}
|
||||
memcpy( _featureReport, data.bytes, MIN( data.length, sizeof(_featureReport) ) );
|
||||
_featureReport = [characteristic value];
|
||||
_waitStateForReadFeatureReport = BLEDeviceWaitState_Complete;
|
||||
}
|
||||
}
|
||||
@@ -850,7 +958,7 @@ static struct hid_device_info *create_device_info_for_hid_device(HIDBLEDevice *d
|
||||
memset( device_info, 0, sizeof(struct hid_device_info) );
|
||||
device_info->path = strdup( device.bleSteamController.identifier.UUIDString.UTF8String );
|
||||
device_info->vendor_id = VALVE_USB_VID;
|
||||
device_info->product_id = D0G_BLE2_PID;
|
||||
device_info->product_id = device.pid;
|
||||
device_info->product_string = wcsdup( L"Steam Controller" );
|
||||
device_info->manufacturer_string = wcsdup( L"Valve Corporation" );
|
||||
device_info->bus_type = HID_API_BUS_BLUETOOTH;
|
||||
@@ -861,14 +969,6 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id,
|
||||
{ @autoreleasepool {
|
||||
struct hid_device_info *root = NULL;
|
||||
|
||||
/* See if there are any devices we should skip in enumeration */
|
||||
if (SDL_HIDAPI_ShouldIgnoreDevice(HID_API_BUS_BLUETOOTH, VALVE_USB_VID, D0G_BLE2_PID, 0, 0)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( ( vendor_id == 0 || vendor_id == VALVE_USB_VID ) &&
|
||||
( product_id == 0 || product_id == D0G_BLE2_PID ) )
|
||||
{
|
||||
HIDBLEManager *bleManager = HIDBLEManager.sharedInstance;
|
||||
[bleManager updateConnectedSteamControllers:false];
|
||||
NSEnumerator<HIDBLEDevice *> *devices = [bleManager.deviceMap objectEnumerator];
|
||||
@@ -891,11 +991,22 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id,
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ( vendor_id != 0 && vendor_id != VALVE_USB_VID ) ||
|
||||
( product_id != 0 && product_id != device.pid ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/* See if there are any devices we should skip in enumeration */
|
||||
if (SDL_HIDAPI_ShouldIgnoreDevice(HID_API_BUS_BLUETOOTH, VALVE_USB_VID, device.pid, 0, 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
struct hid_device_info *device_info = create_device_info_for_hid_device(device);
|
||||
device_info->next = root;
|
||||
root = device_info;
|
||||
}
|
||||
}
|
||||
return root;
|
||||
}}
|
||||
|
||||
@@ -975,7 +1086,7 @@ int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char
|
||||
if ( !device_handle.connected )
|
||||
return -1;
|
||||
|
||||
return [device_handle send_feature_report:(hidFeatureReport *)(void *)data];
|
||||
return [device_handle send_feature_report:(hidFeatureReport *)(void *)data length:length];
|
||||
}
|
||||
|
||||
int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length)
|
||||
@@ -985,7 +1096,7 @@ int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data,
|
||||
if ( !device_handle.connected )
|
||||
return -1;
|
||||
|
||||
size_t written = [device_handle get_feature_report:data[0] into:data];
|
||||
size_t written = [device_handle get_feature_report:data[0] into:data length:length];
|
||||
|
||||
return written == length-1 ? (int)length : (int)written;
|
||||
}
|
||||
@@ -1018,7 +1129,7 @@ int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t
|
||||
NSLog( @"hid_read_timeout with non-zero wait" );
|
||||
}
|
||||
int result = (int)[device_handle read_input_report:data];
|
||||
#if FEATURE_REPORT_LOGGING
|
||||
#if 0 //FEATURE_REPORT_LOGGING
|
||||
NSLog( @"HIDBLE:hid_read_timeout (%d) [%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x]", result,
|
||||
data[1], data[2], data[3], data[4], data[5], data[6],
|
||||
data[7], data[8], data[9], data[10], data[11], data[12],
|
||||
|
||||
@@ -1252,6 +1252,9 @@ static GamepadMapping_t *SDL_CreateMappingForHIDAPIGamepad(SDL_GUID guid)
|
||||
if (SDL_IsJoystickSteamController(vendor, product)) {
|
||||
// Steam controllers have 2 back paddle buttons
|
||||
SDL_strlcat(mapping_string, "paddle1:b11,paddle2:b12,", sizeof(mapping_string));
|
||||
} else if (SDL_IsJoystickSteamTriton(vendor, product)) {
|
||||
// Steam controllers have 2 back paddle buttons
|
||||
SDL_strlcat(mapping_string, "misc1:b11,paddle1:b12,paddle2:b13,paddle3:b14,paddle4:b15", sizeof(mapping_string));
|
||||
} else if (SDL_IsJoystickNintendoSwitchPro(vendor, product) ||
|
||||
SDL_IsJoystickNintendoSwitchProInputOnly(vendor, product)) {
|
||||
// Nintendo Switch Pro controllers have a screenshot button
|
||||
|
||||
@@ -3295,6 +3295,12 @@ bool SDL_IsJoystickSteamDeck(Uint16 vendor_id, Uint16 product_id)
|
||||
return eType == k_eControllerType_SteamControllerNeptune;
|
||||
}
|
||||
|
||||
bool SDL_IsJoystickSteamTriton(Uint16 vendor_id, Uint16 product_id)
|
||||
{
|
||||
EControllerType eType = GuessControllerType(vendor_id, product_id);
|
||||
return eType == k_eControllerType_SteamControllerTriton;
|
||||
}
|
||||
|
||||
bool SDL_IsJoystickXInput(SDL_GUID guid)
|
||||
{
|
||||
return (guid.data[14] == 'x') ? true : false;
|
||||
|
||||
@@ -144,6 +144,9 @@ extern bool SDL_IsJoystickFlydigiController(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);
|
||||
|
||||
// Function to return whether a joystick is a Steam Triton
|
||||
extern bool SDL_IsJoystickSteamTriton(Uint16 vendor_id, Uint16 product_id);
|
||||
|
||||
// Function to return whether a joystick guid comes from the XInput driver
|
||||
extern bool SDL_IsJoystickXInput(SDL_GUID guid);
|
||||
|
||||
|
||||
@@ -588,9 +588,9 @@ static const ControllerDescription_t arrControllers[] = {
|
||||
{ MAKE_CONTROLLER_ID( 0x20d6, 0xa715 ), k_eControllerType_SwitchInputOnlyController, NULL }, // Power A Fusion Wireless Arcade Stick (USB Mode) Over BT is shows up as 057e 2009
|
||||
{ MAKE_CONTROLLER_ID( 0x20d6, 0xa716 ), k_eControllerType_SwitchInputOnlyController, NULL }, // PowerA Nintendo Switch Fusion Pro Controller - USB requires toggling switch on back of device
|
||||
{ MAKE_CONTROLLER_ID( 0x20d6, 0xa718 ), k_eControllerType_SwitchInputOnlyController, NULL }, // PowerA Nintendo Switch Nano Wired Controller
|
||||
{ MAKE_CONTROLLER_ID( 0x33dd, 0x0001 ), k_eControllerType_SwitchInputOnlyController, NULL }, // ZUIKI MasCon for Nintendo Switch Black
|
||||
{ MAKE_CONTROLLER_ID( 0x33dd, 0x0002 ), k_eControllerType_SwitchInputOnlyController, NULL }, // ZUIKI MasCon for Nintendo Switch ??
|
||||
{ MAKE_CONTROLLER_ID( 0x33dd, 0x0003 ), k_eControllerType_SwitchInputOnlyController, NULL }, // ZUIKI MasCon for Nintendo Switch Red
|
||||
{ MAKE_CONTROLLER_ID( 0x33dd, 0x0001 ), k_eControllerType_SwitchInputOnlyController, NULL }, // ZUIKI MasCon for Nintendo Switch Black
|
||||
{ MAKE_CONTROLLER_ID( 0x33dd, 0x0002 ), k_eControllerType_SwitchInputOnlyController, NULL }, // ZUIKI MasCon for Nintendo Switch ??
|
||||
{ MAKE_CONTROLLER_ID( 0x33dd, 0x0003 ), k_eControllerType_SwitchInputOnlyController, NULL }, // ZUIKI MasCon for Nintendo Switch Red
|
||||
|
||||
// Valve products
|
||||
{ MAKE_CONTROLLER_ID( 0x0000, 0x11fb ), k_eControllerType_MobileTouch, NULL }, // Streaming mobile touch virtual controls
|
||||
@@ -603,4 +603,8 @@ static const ControllerDescription_t arrControllers[] = {
|
||||
{ MAKE_CONTROLLER_ID( 0x28de, 0x1201 ), k_eControllerType_SteamControllerV2, NULL }, // Valve wired Steam Controller (HEADCRAB)
|
||||
{ MAKE_CONTROLLER_ID( 0x28de, 0x1202 ), k_eControllerType_SteamControllerV2, NULL }, // Valve Bluetooth Steam Controller (HEADCRAB)
|
||||
{ MAKE_CONTROLLER_ID( 0x28de, 0x1205 ), k_eControllerType_SteamControllerNeptune, NULL }, // Valve Steam Deck Builtin Controller
|
||||
{ MAKE_CONTROLLER_ID( 0x28de, 0x1302 ), k_eControllerType_SteamControllerTriton, NULL }, // Valve Steam Triton Controller
|
||||
{ MAKE_CONTROLLER_ID( 0x28de, 0x1303 ), k_eControllerType_SteamControllerTriton, NULL }, // Valve Steam Triton Controller (BLE)
|
||||
{ MAKE_CONTROLLER_ID( 0x28de, 0x1304 ), k_eControllerType_SteamControllerTriton, NULL }, // Valve Steam Proteus Dongle (Proprietary)
|
||||
{ MAKE_CONTROLLER_ID( 0x28de, 0x1305 ), k_eControllerType_SteamControllerTriton, NULL }, // Valve Steam Nereid Dongle (Proprietary)
|
||||
};
|
||||
|
||||
@@ -39,6 +39,8 @@ typedef enum
|
||||
k_eControllerType_SteamControllerV2 = 3,
|
||||
k_eControllerType_SteamControllerNeptune = 4,
|
||||
|
||||
k_eControllerType_SteamControllerTriton = 10,
|
||||
|
||||
// Other Controllers
|
||||
k_eControllerType_UnknownNonSteamController = 30,
|
||||
k_eControllerType_XBox360Controller = 31,
|
||||
|
||||
532
src/joystick/hidapi/SDL_hidapi_steam_triton.c
Normal file
532
src/joystick/hidapi/SDL_hidapi_steam_triton.c
Normal file
@@ -0,0 +1,532 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 2023 Max Maisel <max.maisel@posteo.de>
|
||||
|
||||
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"
|
||||
|
||||
#ifdef SDL_JOYSTICK_HIDAPI_STEAM_TRITON
|
||||
|
||||
/*****************************************************************************************************/
|
||||
|
||||
#include "steam/controller_constants.h"
|
||||
#include "steam/controller_structs.h"
|
||||
|
||||
// Always 1kHz according to USB descriptor, but actually about 4 ms.
|
||||
#define TRITON_SENSOR_UPDATE_INTERVAL_US 4032
|
||||
|
||||
enum
|
||||
{
|
||||
SDL_GAMEPAD_BUTTON_STEAM_DECK_QAM = 11,
|
||||
SDL_GAMEPAD_BUTTON_STEAM_DECK_RIGHT_PADDLE1,
|
||||
SDL_GAMEPAD_BUTTON_STEAM_DECK_LEFT_PADDLE1,
|
||||
SDL_GAMEPAD_BUTTON_STEAM_DECK_RIGHT_PADDLE2,
|
||||
SDL_GAMEPAD_BUTTON_STEAM_DECK_LEFT_PADDLE2,
|
||||
SDL_GAMEPAD_NUM_TRITON_BUTTONS,
|
||||
};
|
||||
|
||||
typedef enum
|
||||
{
|
||||
|
||||
TRITON_LBUTTON_A = 0x00000001,
|
||||
TRITON_LBUTTON_B = 0x00000002,
|
||||
TRITON_LBUTTON_X = 0x00000004,
|
||||
TRITON_LBUTTON_Y = 0x00000008,
|
||||
|
||||
TRITON_HBUTTON_QAM = 0x00000010,
|
||||
TRITON_LBUTTON_R3 = 0x00000020,
|
||||
TRITON_LBUTTON_VIEW = 0x00000040,
|
||||
TRITON_HBUTTON_R4 = 0x00000080,
|
||||
|
||||
TRITON_LBUTTON_R5 = 0x00000100,
|
||||
TRITON_LBUTTON_R = 0x00000200,
|
||||
TRITON_LBUTTON_DPAD_DOWN = 0x00000400,
|
||||
TRITON_LBUTTON_DPAD_RIGHT = 0x00000800,
|
||||
|
||||
TRITON_LBUTTON_DPAD_LEFT = 0x00001000,
|
||||
TRITON_LBUTTON_DPAD_UP = 0x00002000,
|
||||
TRITON_LBUTTON_MENU = 0x00004000,
|
||||
TRITON_LBUTTON_L3 = 0x00008000,
|
||||
|
||||
TRITON_LBUTTON_STEAM = 0x00010000,
|
||||
TRITON_HBUTTON_L4 = 0x00020000,
|
||||
TRITON_LBUTTON_L5 = 0x00040000,
|
||||
TRITON_LBUTTON_L = 0x00080000,
|
||||
|
||||
/*
|
||||
STEAM_RIGHTSTICK_FINGERDOWN_MASK, // Right Stick Touch 0x00100000
|
||||
STEAM_RIGHTPAD_FINGERDOWN_MASK, // Right Pad Touch 0x00200000
|
||||
STEAM_BUTTON_RIGHTPAD_CLICKED_MASK, // Right Pressure Click 0x00400000
|
||||
STEAM_RIGHT_TRIGGER_MASK, // Right Trigger Click 0x00800000
|
||||
|
||||
STEAM_LEFTSTICK_FINGERDOWN_MASK, // Left Stick Touch 0x01000000
|
||||
STEAM_LEFTPAD_FINGERDOWN_MASK, // Left Pad Touch 0x02000000
|
||||
STEAM_BUTTON_LEFTPAD_CLICKED_MASK, // Left Pressure Click 0x04000000
|
||||
STEAM_LEFT_TRIGGER_MASK, // Left Trigger Click 0x08000000
|
||||
STEAM_RIGHT_AUX_MASK, // Right Pinky Touch 0x10000000
|
||||
STEAM_LEFT_AUX_MASK, // Left Pinky Touch 0x20000000
|
||||
*/
|
||||
} TritonButtons;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
bool connected;
|
||||
bool report_sensors;
|
||||
Uint32 last_sensor_tick;
|
||||
Uint64 sensor_timestamp_ns;
|
||||
Uint64 last_button_state;
|
||||
Uint64 last_lizard_update;
|
||||
} SDL_DriverSteamTriton_Context;
|
||||
|
||||
static bool IsProteusDongle(Uint16 product_id)
|
||||
{
|
||||
return (product_id == USB_PRODUCT_VALVE_STEAM_PROTEUS_DONGLE ||
|
||||
product_id == USB_PRODUCT_VALVE_STEAM_NEREID_DONGLE);
|
||||
}
|
||||
|
||||
static bool DisableSteamTritonLizardMode(SDL_hid_device *dev)
|
||||
{
|
||||
int rc;
|
||||
Uint8 buffer[HID_FEATURE_REPORT_BYTES] = { 1 };
|
||||
FeatureReportMsg *msg = (FeatureReportMsg *)(buffer + 1);
|
||||
|
||||
msg->header.type = ID_SET_SETTINGS_VALUES;
|
||||
msg->header.length = 1 * sizeof(ControllerSetting);
|
||||
msg->payload.setSettingsValues.settings[0].settingNum = SETTING_LIZARD_MODE;
|
||||
msg->payload.setSettingsValues.settings[0].settingValue = LIZARD_MODE_OFF;
|
||||
|
||||
rc = SDL_hid_send_feature_report(dev, buffer, sizeof(buffer));
|
||||
if (rc != sizeof(buffer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void HIDAPI_DriverSteamTriton_HandleState(SDL_HIDAPI_Device *device,
|
||||
SDL_Joystick *joystick,
|
||||
TritonMTUFull_t *pTritonReport)
|
||||
{
|
||||
float values[3];
|
||||
SDL_DriverSteamTriton_Context *ctx = (SDL_DriverSteamTriton_Context *)device->context;
|
||||
Uint64 timestamp = SDL_GetTicksNS();
|
||||
|
||||
if (pTritonReport->uButtons != ctx->last_button_state) {
|
||||
Uint8 hat = 0;
|
||||
|
||||
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SOUTH,
|
||||
((pTritonReport->uButtons & TRITON_LBUTTON_A) != 0));
|
||||
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_EAST,
|
||||
((pTritonReport->uButtons & TRITON_LBUTTON_B) != 0));
|
||||
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_WEST,
|
||||
((pTritonReport->uButtons & TRITON_LBUTTON_X) != 0));
|
||||
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_NORTH,
|
||||
((pTritonReport->uButtons & TRITON_LBUTTON_Y) != 0));
|
||||
|
||||
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER,
|
||||
((pTritonReport->uButtons & TRITON_LBUTTON_L) != 0));
|
||||
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER,
|
||||
((pTritonReport->uButtons & TRITON_LBUTTON_R) != 0));
|
||||
|
||||
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK,
|
||||
((pTritonReport->uButtons & TRITON_LBUTTON_MENU) != 0));
|
||||
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START,
|
||||
((pTritonReport->uButtons & TRITON_LBUTTON_VIEW) != 0));
|
||||
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE,
|
||||
((pTritonReport->uButtons & TRITON_LBUTTON_STEAM) != 0));
|
||||
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_STEAM_DECK_QAM,
|
||||
((pTritonReport->uButtons & TRITON_HBUTTON_QAM) != 0));
|
||||
|
||||
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK,
|
||||
((pTritonReport->uButtons & TRITON_LBUTTON_L3) != 0));
|
||||
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK,
|
||||
((pTritonReport->uButtons & TRITON_LBUTTON_R3) != 0));
|
||||
|
||||
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_STEAM_DECK_RIGHT_PADDLE1,
|
||||
((pTritonReport->uButtons & TRITON_HBUTTON_R4) != 0));
|
||||
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_STEAM_DECK_LEFT_PADDLE1,
|
||||
((pTritonReport->uButtons & TRITON_HBUTTON_L4) != 0));
|
||||
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_STEAM_DECK_RIGHT_PADDLE2,
|
||||
((pTritonReport->uButtons & TRITON_LBUTTON_R5) != 0));
|
||||
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_STEAM_DECK_LEFT_PADDLE2,
|
||||
((pTritonReport->uButtons & TRITON_LBUTTON_L5) != 0));
|
||||
|
||||
if (pTritonReport->uButtons & TRITON_LBUTTON_DPAD_UP) {
|
||||
hat |= SDL_HAT_UP;
|
||||
}
|
||||
if (pTritonReport->uButtons & TRITON_LBUTTON_DPAD_DOWN) {
|
||||
hat |= SDL_HAT_DOWN;
|
||||
}
|
||||
if (pTritonReport->uButtons & TRITON_LBUTTON_DPAD_LEFT) {
|
||||
hat |= SDL_HAT_LEFT;
|
||||
}
|
||||
if (pTritonReport->uButtons & TRITON_LBUTTON_DPAD_RIGHT) {
|
||||
hat |= SDL_HAT_RIGHT;
|
||||
}
|
||||
SDL_SendJoystickHat(timestamp, joystick, 0, hat);
|
||||
|
||||
ctx->last_button_state = pTritonReport->uButtons;
|
||||
}
|
||||
|
||||
// RKRK There're button bits for this if you so choose.
|
||||
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER,
|
||||
(int)pTritonReport->sTriggerLeft * 2 - 32768);
|
||||
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER,
|
||||
(int)pTritonReport->sTriggerRight * 2 - 32768);
|
||||
|
||||
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX,
|
||||
pTritonReport->sLeftStickX);
|
||||
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY,
|
||||
-pTritonReport->sLeftStickY);
|
||||
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX,
|
||||
pTritonReport->sRightStickX);
|
||||
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY,
|
||||
-pTritonReport->sRightStickY);
|
||||
|
||||
if (ctx->report_sensors && pTritonReport->imu.uTimestamp != ctx->last_sensor_tick) {
|
||||
Uint32 delta_us = (pTritonReport->imu.uTimestamp - ctx->last_sensor_tick);
|
||||
|
||||
ctx->sensor_timestamp_ns += SDL_US_TO_NS(delta_us);
|
||||
|
||||
values[0] = (pTritonReport->imu.sGyroX / 32768.0f) * (2000.0f * (SDL_PI_F / 180.0f));
|
||||
values[1] = (pTritonReport->imu.sGyroZ / 32768.0f) * (2000.0f * (SDL_PI_F / 180.0f));
|
||||
values[2] = (-pTritonReport->imu.sGyroY / 32768.0f) * (2000.0f * (SDL_PI_F / 180.0f));
|
||||
SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_GYRO, ctx->sensor_timestamp_ns, values, 3);
|
||||
|
||||
values[0] = (pTritonReport->imu.sAccelX / 32768.0f) * 2.0f * SDL_STANDARD_GRAVITY;
|
||||
values[1] = (pTritonReport->imu.sAccelZ / 32768.0f) * 2.0f * SDL_STANDARD_GRAVITY;
|
||||
values[2] = (-pTritonReport->imu.sAccelY / 32768.0f) * 2.0f * SDL_STANDARD_GRAVITY;
|
||||
SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_ACCEL, ctx->sensor_timestamp_ns, values, 3);
|
||||
|
||||
ctx->last_sensor_tick = pTritonReport->imu.uTimestamp;
|
||||
}
|
||||
}
|
||||
|
||||
static void HIDAPI_DriverSteamTriton_HandleBatteryStatus(SDL_HIDAPI_Device *device,
|
||||
SDL_Joystick *joystick,
|
||||
TritonBatteryStatus_t *pTritonBatteryStatus)
|
||||
{
|
||||
SDL_PowerState state;
|
||||
|
||||
if (device->is_bluetooth) {
|
||||
state = SDL_POWERSTATE_ON_BATTERY;
|
||||
} else if (IsProteusDongle(device->product_id)) {
|
||||
state = SDL_POWERSTATE_ON_BATTERY;
|
||||
} else if (pTritonBatteryStatus->ucBatteryLevel == 100) {
|
||||
state = SDL_POWERSTATE_CHARGED;
|
||||
} else {
|
||||
state = SDL_POWERSTATE_CHARGING;
|
||||
}
|
||||
SDL_SendJoystickPowerInfo(joystick, state, pTritonBatteryStatus->ucBatteryLevel);
|
||||
}
|
||||
|
||||
static bool HIDAPI_DriverSteamTriton_SetControllerConnected(SDL_HIDAPI_Device *device, bool connected)
|
||||
{
|
||||
SDL_DriverSteamTriton_Context *ctx = (SDL_DriverSteamTriton_Context *)device->context;
|
||||
|
||||
if (ctx->connected != connected) {
|
||||
if (connected) {
|
||||
SDL_JoystickID joystickID;
|
||||
if (!HIDAPI_JoystickConnected(device, &joystickID)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (device->num_joysticks > 0) {
|
||||
HIDAPI_JoystickDisconnected(device, device->joysticks[0]);
|
||||
}
|
||||
}
|
||||
ctx->connected = connected;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void HIDAPI_DriverSteamTriton_HandleWirelessStatus(SDL_HIDAPI_Device *device,
|
||||
TritonWirelessStatus_t *pTritonWirelessStatus)
|
||||
{
|
||||
switch (pTritonWirelessStatus->state) {
|
||||
case k_ETritonWirelessStateConnect:
|
||||
HIDAPI_DriverSteamTriton_SetControllerConnected(device, true);
|
||||
break;
|
||||
case k_ETritonWirelessStateDisconnect:
|
||||
HIDAPI_DriverSteamTriton_SetControllerConnected(device, false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************************************/
|
||||
|
||||
static void HIDAPI_DriverSteamTriton_RegisterHints(SDL_HintCallback callback, void *userdata)
|
||||
{
|
||||
SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_STEAM, callback, userdata);
|
||||
}
|
||||
|
||||
static void HIDAPI_DriverSteamTriton_UnregisterHints(SDL_HintCallback callback, void *userdata)
|
||||
{
|
||||
SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_STEAM, callback, userdata);
|
||||
}
|
||||
|
||||
static bool HIDAPI_DriverSteamTriton_IsEnabled(void)
|
||||
{
|
||||
return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_STEAM,
|
||||
SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI, SDL_HIDAPI_DEFAULT));
|
||||
}
|
||||
|
||||
static bool HIDAPI_DriverSteamTriton_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)
|
||||
{
|
||||
|
||||
if (IsProteusDongle(product_id)) {
|
||||
if (interface_number >= 2 && interface_number <= 5) {
|
||||
// The set of controller interfaces for Proteus & Nereid...currently
|
||||
return true;
|
||||
}
|
||||
} else if (SDL_IsJoystickSteamTriton(vendor_id, product_id)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool HIDAPI_DriverSteamTriton_InitDevice(SDL_HIDAPI_Device *device)
|
||||
{
|
||||
SDL_DriverSteamTriton_Context *ctx;
|
||||
|
||||
ctx = (SDL_DriverSteamTriton_Context *)SDL_calloc(1, sizeof(*ctx));
|
||||
if (ctx == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
device->context = ctx;
|
||||
|
||||
HIDAPI_SetDeviceName(device, "Steam Controller");
|
||||
|
||||
if (IsProteusDongle(device->product_id)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Wired controller, connected!
|
||||
return HIDAPI_DriverSteamTriton_SetControllerConnected(device, true);
|
||||
}
|
||||
|
||||
static int HIDAPI_DriverSteamTriton_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void HIDAPI_DriverSteamTriton_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
|
||||
{
|
||||
}
|
||||
|
||||
static bool HIDAPI_DriverSteamTriton_UpdateDevice(SDL_HIDAPI_Device *device)
|
||||
{
|
||||
SDL_DriverSteamTriton_Context *ctx = (SDL_DriverSteamTriton_Context *)device->context;
|
||||
SDL_Joystick *joystick = NULL;
|
||||
|
||||
if (device->num_joysticks > 0) {
|
||||
joystick = SDL_GetJoystickFromID(device->joysticks[0]);
|
||||
}
|
||||
|
||||
if (ctx->connected && joystick) {
|
||||
Uint64 now = SDL_GetTicks();
|
||||
if (!ctx->last_lizard_update || (now - ctx->last_lizard_update) >= 3000) {
|
||||
DisableSteamTritonLizardMode(device->dev);
|
||||
ctx->last_lizard_update = now;
|
||||
}
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
uint8_t data[64];
|
||||
int r = SDL_hid_read(device->dev, data, sizeof(data));
|
||||
|
||||
if (r == 0) {
|
||||
return true;
|
||||
}
|
||||
if (r < 0) {
|
||||
// Failed to read from controller
|
||||
HIDAPI_DriverSteamTriton_SetControllerConnected(device, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (data[0]) {
|
||||
case ID_TRITON_CONTROLLER_STATE:
|
||||
if (!joystick) {
|
||||
HIDAPI_DriverSteamTriton_SetControllerConnected(device, true);
|
||||
if (device->num_joysticks > 0) {
|
||||
joystick = SDL_GetJoystickFromID(device->joysticks[0]);
|
||||
}
|
||||
}
|
||||
if (joystick && r >= (1 + sizeof(TritonMTUFull_t))) {
|
||||
TritonMTUFull_t *pTritonReport = (TritonMTUFull_t *)&data[1];
|
||||
HIDAPI_DriverSteamTriton_HandleState(device, joystick, pTritonReport);
|
||||
}
|
||||
break;
|
||||
case ID_TRITON_BATTERY_STATUS:
|
||||
if (joystick && r >= (1 + sizeof(TritonBatteryStatus_t))) {
|
||||
TritonBatteryStatus_t *pTritonBatteryStatus = (TritonBatteryStatus_t *)&data[1];
|
||||
HIDAPI_DriverSteamTriton_HandleBatteryStatus(device, joystick, pTritonBatteryStatus);
|
||||
}
|
||||
break;
|
||||
case ID_TRITON_WIRELESS_STATUS_X:
|
||||
case ID_TRITON_WIRELESS_STATUS:
|
||||
if (r >= (1 + sizeof(TritonWirelessStatus_t))) {
|
||||
TritonWirelessStatus_t *pTritonWirelessStatus = (TritonWirelessStatus_t *)&data[1];
|
||||
HIDAPI_DriverSteamTriton_HandleWirelessStatus(device, pTritonWirelessStatus);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool HIDAPI_DriverSteamTriton_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
|
||||
{
|
||||
float update_rate_in_hz = 1000000.0f / TRITON_SENSOR_UPDATE_INTERVAL_US;
|
||||
|
||||
SDL_AssertJoysticksLocked();
|
||||
|
||||
// Initialize the joystick capabilities
|
||||
joystick->nbuttons = SDL_GAMEPAD_NUM_TRITON_BUTTONS;
|
||||
joystick->naxes = SDL_GAMEPAD_AXIS_COUNT;
|
||||
joystick->nhats = 1;
|
||||
|
||||
SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, update_rate_in_hz);
|
||||
SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, update_rate_in_hz);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HIDAPI_DriverSteamTriton_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
|
||||
{
|
||||
int rc;
|
||||
|
||||
//RKRK Not sure about size. Probalby 64+1 is OK for ORs
|
||||
Uint8 buffer[HID_RUMBLE_OUTPUT_REPORT_BYTES];
|
||||
OutputReportMsg *msg = (OutputReportMsg *)(buffer);
|
||||
|
||||
msg->report_id = ID_OUT_REPORT_HAPTIC_RUMBLE;
|
||||
msg->payload.hapticRumble.type = 0;
|
||||
msg->payload.hapticRumble.intensity = 0;
|
||||
msg->payload.hapticRumble.left.speed = low_frequency_rumble;
|
||||
msg->payload.hapticRumble.left.gain = 0;
|
||||
msg->payload.hapticRumble.right.speed = high_frequency_rumble;
|
||||
msg->payload.hapticRumble.right.gain = 0;
|
||||
|
||||
|
||||
rc = SDL_hid_write(device->dev, buffer, sizeof(buffer));
|
||||
if (rc != sizeof(buffer)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HIDAPI_DriverSteamTriton_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static Uint32 HIDAPI_DriverSteamTriton_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
|
||||
{
|
||||
return SDL_JOYSTICK_CAP_RUMBLE;
|
||||
}
|
||||
|
||||
static bool HIDAPI_DriverSteamTriton_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static bool HIDAPI_DriverSteamTriton_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *data, int size)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static bool HIDAPI_DriverSteamTriton_SetSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, bool enabled)
|
||||
{
|
||||
SDL_DriverSteamTriton_Context *ctx = (SDL_DriverSteamTriton_Context *)device->context;
|
||||
int rc;
|
||||
Uint8 buffer[HID_FEATURE_REPORT_BYTES] = { 1 };
|
||||
FeatureReportMsg *msg = (FeatureReportMsg *)(buffer + 1);
|
||||
|
||||
msg->header.type = ID_SET_SETTINGS_VALUES;
|
||||
msg->header.length = 1 * sizeof(ControllerSetting);
|
||||
msg->payload.setSettingsValues.settings[0].settingNum = SETTING_IMU_MODE;
|
||||
if (enabled) {
|
||||
msg->payload.setSettingsValues.settings[0].settingValue = (SETTING_GYRO_MODE_SEND_RAW_ACCEL | SETTING_GYRO_MODE_SEND_RAW_GYRO);
|
||||
} else {
|
||||
msg->payload.setSettingsValues.settings[0].settingValue = SETTING_GYRO_MODE_OFF;
|
||||
}
|
||||
|
||||
rc = SDL_hid_send_feature_report(device->dev, buffer, sizeof(buffer));
|
||||
if (rc != sizeof(buffer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ctx->report_sensors = enabled;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void HIDAPI_DriverSteamTriton_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
|
||||
{
|
||||
// Lizard mode id automatically re-enabled by watchdog. Nothing to do here.
|
||||
}
|
||||
|
||||
static void HIDAPI_DriverSteamTriton_FreeDevice(SDL_HIDAPI_Device *device)
|
||||
{
|
||||
}
|
||||
|
||||
SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSteamTriton = {
|
||||
SDL_HINT_JOYSTICK_HIDAPI_STEAM,
|
||||
true,
|
||||
HIDAPI_DriverSteamTriton_RegisterHints,
|
||||
HIDAPI_DriverSteamTriton_UnregisterHints,
|
||||
HIDAPI_DriverSteamTriton_IsEnabled,
|
||||
HIDAPI_DriverSteamTriton_IsSupportedDevice,
|
||||
HIDAPI_DriverSteamTriton_InitDevice,
|
||||
HIDAPI_DriverSteamTriton_GetDevicePlayerIndex,
|
||||
HIDAPI_DriverSteamTriton_SetDevicePlayerIndex,
|
||||
HIDAPI_DriverSteamTriton_UpdateDevice,
|
||||
HIDAPI_DriverSteamTriton_OpenJoystick,
|
||||
HIDAPI_DriverSteamTriton_RumbleJoystick,
|
||||
HIDAPI_DriverSteamTriton_RumbleJoystickTriggers,
|
||||
HIDAPI_DriverSteamTriton_GetJoystickCapabilities,
|
||||
HIDAPI_DriverSteamTriton_SetJoystickLED,
|
||||
HIDAPI_DriverSteamTriton_SendJoystickEffect,
|
||||
HIDAPI_DriverSteamTriton_SetSensorsEnabled,
|
||||
HIDAPI_DriverSteamTriton_CloseJoystick,
|
||||
HIDAPI_DriverSteamTriton_FreeDevice,
|
||||
};
|
||||
|
||||
#endif // SDL_JOYSTICK_HIDAPI_STEAM_TRITON
|
||||
|
||||
#endif // SDL_JOYSTICK_HIDAPI
|
||||
@@ -70,6 +70,9 @@ static SDL_HIDAPI_DeviceDriver *SDL_HIDAPI_drivers[] = {
|
||||
#ifdef SDL_JOYSTICK_HIDAPI_STEAMDECK
|
||||
&SDL_HIDAPI_DriverSteamDeck,
|
||||
#endif
|
||||
#ifdef SDL_JOYSTICK_HIDAPI_STEAMDECK
|
||||
&SDL_HIDAPI_DriverSteamTriton,
|
||||
#endif
|
||||
#ifdef SDL_JOYSTICK_HIDAPI_SWITCH
|
||||
&SDL_HIDAPI_DriverNintendoClassic,
|
||||
&SDL_HIDAPI_DriverJoyCons,
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#define SDL_JOYSTICK_HIDAPI_XBOXONE
|
||||
#define SDL_JOYSTICK_HIDAPI_SHIELD
|
||||
#define SDL_JOYSTICK_HIDAPI_STEAM_HORI
|
||||
#define SDL_JOYSTICK_HIDAPI_STEAM_TRITON
|
||||
#define SDL_JOYSTICK_HIDAPI_LG4FF
|
||||
#define SDL_JOYSTICK_HIDAPI_8BITDO
|
||||
#define SDL_JOYSTICK_HIDAPI_FLYDIGI
|
||||
@@ -168,6 +169,7 @@ 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;
|
||||
extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSteamTriton;
|
||||
extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverLg4ff;
|
||||
extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_Driver8BitDo;
|
||||
extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverFlydigi;
|
||||
|
||||
@@ -411,6 +411,12 @@ typedef enum {
|
||||
TRACKPAD_NUM_MODES
|
||||
} TrackpadDPadMode;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
LIZARD_MODE_OFF,
|
||||
LIZARD_MODE_ON,
|
||||
} LizardModeState_t;
|
||||
|
||||
// Read-write controller settings (only add to this enum and never change the order)
|
||||
typedef enum
|
||||
{
|
||||
@@ -423,7 +429,7 @@ typedef enum
|
||||
SETTING_USB_DEBUG_MODE,
|
||||
SETTING_LEFT_TRACKPAD_MODE,
|
||||
SETTING_RIGHT_TRACKPAD_MODE,
|
||||
SETTING_MOUSE_POINTER_ENABLED,
|
||||
SETTING_LIZARD_MODE,
|
||||
|
||||
// 10
|
||||
SETTING_DPAD_DEADZONE,
|
||||
|
||||
@@ -154,6 +154,100 @@ typedef struct
|
||||
|
||||
} FeatureReportMsg;
|
||||
|
||||
// Triton and derivatives utilize output reports for haptic commands. This is a
|
||||
// snapshot from Nov 2024 -- things may change.
|
||||
|
||||
// Triton Output Report Lengths #defs include +1 for the OR ID
|
||||
|
||||
// Output Report Haptic Messages for Triton
|
||||
typedef struct
|
||||
{
|
||||
uint8_t type;
|
||||
uint16_t intensity;
|
||||
struct
|
||||
{
|
||||
uint16_t speed;
|
||||
int8_t gain;
|
||||
} left, right;
|
||||
} MsgHapticRumble;
|
||||
#define HID_RUMBLE_OUTPUT_REPORT_BYTES 10
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t side;
|
||||
uint16_t on_us;
|
||||
uint16_t off_us;
|
||||
uint16_t repeat_count;
|
||||
uint16_t gain_db;
|
||||
} MsgHapticPulse;
|
||||
#define HID_HAPTIC_PULSE_OUTPUT_REPORT_BYTES 10
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t side;
|
||||
uint8_t command;
|
||||
int8_t gain_db;
|
||||
} MsgHapticCommand;
|
||||
#define HID_HAPTIC_COMMAND_REPORT_BYTES 4
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t side;
|
||||
int8_t gain_db;
|
||||
uint16_t frequency;
|
||||
uint16_t duration_ms;
|
||||
uint16_t lfo_freq;
|
||||
uint8_t lfo_depth;
|
||||
} MsgHapticLfoTone;
|
||||
#define HID_HAPTIC_LFO_TONE_REPORT_BYTES 10
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t side;
|
||||
int8_t gain_db;
|
||||
uint16_t duration_ms;
|
||||
struct
|
||||
{
|
||||
uint16_t frequency;
|
||||
} start, end;
|
||||
} MsgHapticLogSweep;
|
||||
#define HID_HAPTIC_LOG_SWEEP_REPORT_BYTES 9
|
||||
|
||||
typedef struct m
|
||||
{
|
||||
uint8_t side;
|
||||
uint8_t script_id;
|
||||
int8_t gain_db;
|
||||
} MsgHapticScript;
|
||||
#define HID_HAPTIC_SCRIPT_REPORT_BYTES 4
|
||||
|
||||
typedef enum
|
||||
{
|
||||
ID_OUT_REPORT_HAPTIC_RUMBLE = 0x80,
|
||||
ID_OUT_REPORT_HAPTIC_PULSE = 0x81,
|
||||
ID_OUT_REPORT_HAPTIC_COMMAND = 0x82,
|
||||
ID_OUT_REPORT_HAPTIC_LFO_TONE = 0x83,
|
||||
ID_OUT_REPORT_HAPTIC_LOG_SWEEP = 0x85,
|
||||
ID_OUT_REPORT_HAPTIC_SCRIPT = 0x86,
|
||||
} ValveTritonOutReportMessageIDs;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t report_id;
|
||||
union
|
||||
{
|
||||
MsgHapticRumble hapticRumble;
|
||||
MsgHapticPulse hapticPulse;
|
||||
MsgHapticCommand hapticCommand;
|
||||
MsgHapticLfoTone hapticLfoTone;
|
||||
MsgHapticLogSweep hapticLogSweep;
|
||||
MsgHapticScript hapticScript;
|
||||
} payload;
|
||||
|
||||
} OutputReportMsg;
|
||||
|
||||
|
||||
// Roll this version forward anytime that you are breaking compatibility of existing
|
||||
// message types within ValveInReport_t or the header itself. Hopefully this should
|
||||
// be super rare and instead you should just add new message payloads to the union,
|
||||
@@ -413,51 +507,118 @@ typedef struct
|
||||
unsigned short sPressurePadRight;
|
||||
} SteamDeckStatePacket_t;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ValveInReportHeader_t header;
|
||||
|
||||
union
|
||||
{
|
||||
ValveControllerStatePacket_t controllerState;
|
||||
ValveControllerBLEStatePacket_t controllerBLEState;
|
||||
ValveControllerDebugPacket_t debugState;
|
||||
ValveControllerTrackpadImage_t padImage;
|
||||
ValveControllerRawTrackpadImage_t rawPadImage;
|
||||
SteamControllerWirelessEvent_t wirelessEvent;
|
||||
SteamControllerStatusEvent_t statusEvent;
|
||||
SteamDeckStatePacket_t deckState;
|
||||
} payload;
|
||||
|
||||
ValveInReportHeader_t header;
|
||||
|
||||
union
|
||||
{
|
||||
ValveControllerStatePacket_t controllerState;
|
||||
ValveControllerBLEStatePacket_t controllerBLEState;
|
||||
ValveControllerDebugPacket_t debugState;
|
||||
ValveControllerTrackpadImage_t padImage;
|
||||
ValveControllerRawTrackpadImage_t rawPadImage;
|
||||
SteamControllerWirelessEvent_t wirelessEvent;
|
||||
SteamControllerStatusEvent_t statusEvent;
|
||||
SteamDeckStatePacket_t deckState;
|
||||
} payload;
|
||||
|
||||
} ValveInReport_t;
|
||||
|
||||
|
||||
// Enumeration for BLE packet protocol
|
||||
enum EBLEPacketReportNums
|
||||
{
|
||||
// Skipping past 2-3 because they are escape characters in Uart protocol
|
||||
k_EBLEReportState = 4,
|
||||
k_EBLEReportStatus = 5,
|
||||
k_EBLEReportState = 4,
|
||||
k_EBLEReportStatus = 5,
|
||||
};
|
||||
|
||||
|
||||
// Enumeration of data chunks in BLE state packets
|
||||
enum EBLEOptionDataChunksBitmask
|
||||
{
|
||||
// First byte upper nibble
|
||||
k_EBLEButtonChunk1 = 0x10,
|
||||
k_EBLEButtonChunk2 = 0x20,
|
||||
k_EBLEButtonChunk3 = 0x40,
|
||||
k_EBLELeftJoystickChunk = 0x80,
|
||||
// First byte upper nibble
|
||||
k_EBLEButtonChunk1 = 0x10,
|
||||
k_EBLEButtonChunk2 = 0x20,
|
||||
k_EBLEButtonChunk3 = 0x40,
|
||||
k_EBLELeftJoystickChunk = 0x80,
|
||||
|
||||
// Second full byte
|
||||
k_EBLELeftTrackpadChunk = 0x100,
|
||||
k_EBLERightTrackpadChunk = 0x200,
|
||||
k_EBLEIMUAccelChunk = 0x400,
|
||||
k_EBLEIMUGyroChunk = 0x800,
|
||||
k_EBLEIMUQuatChunk = 0x1000,
|
||||
// Second full byte
|
||||
k_EBLELeftTrackpadChunk = 0x100,
|
||||
k_EBLERightTrackpadChunk = 0x200,
|
||||
k_EBLEIMUAccelChunk = 0x400,
|
||||
k_EBLEIMUGyroChunk = 0x800,
|
||||
k_EBLEIMUQuatChunk = 0x1000,
|
||||
};
|
||||
|
||||
// Triton and derivatives do not use the ValveInReport_t structure
|
||||
|
||||
enum ETritonReportIDTypes
|
||||
{
|
||||
ID_TRITON_CONTROLLER_STATE = 0x42,
|
||||
ID_TRITON_BATTERY_STATUS = 0x43,
|
||||
ID_TRITON_WIRELESS_STATUS_X = 0x46,
|
||||
ID_TRITON_WIRELESS_STATUS = 0x79,
|
||||
};
|
||||
|
||||
enum ETritonWirelessState
|
||||
{
|
||||
k_ETritonWirelessStateDisconnect = 1,
|
||||
k_ETritonWirelessStateConnect = 2,
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t uTimestamp;
|
||||
short sAccelX;
|
||||
short sAccelY;
|
||||
short sAccelZ;
|
||||
|
||||
short sGyroX;
|
||||
short sGyroY;
|
||||
short sGyroZ;
|
||||
|
||||
short sGyroQuatW;
|
||||
short sGyroQuatX;
|
||||
short sGyroQuatY;
|
||||
short sGyroQuatZ;
|
||||
} TritonMTUIMU_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t cSeq_num;
|
||||
uint32_t uButtons;
|
||||
short sTriggerLeft;
|
||||
short sTriggerRight;
|
||||
|
||||
short sLeftStickX;
|
||||
short sLeftStickY;
|
||||
short sRightStickX;
|
||||
short sRightStickY;
|
||||
|
||||
short sLeftPadX;
|
||||
short sLeftPadY;
|
||||
unsigned short ucPressureLeft;
|
||||
|
||||
short sRightPadX;
|
||||
short sRightPadY;
|
||||
unsigned short ucPressureRight;
|
||||
TritonMTUIMU_t imu;
|
||||
} TritonMTUFull_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned char ucBatteryLevel;
|
||||
unsigned short sBatteryVoltage;
|
||||
unsigned short sSystemVoltage;
|
||||
unsigned short sInputVoltage;
|
||||
unsigned short sCurrent;
|
||||
unsigned short sInputCurrent;
|
||||
char cTemperature;
|
||||
} TritonBatteryStatus_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned char state;
|
||||
} TritonWirelessStatus_t;
|
||||
|
||||
#pragma pack()
|
||||
|
||||
#endif // _CONTROLLER_STRUCTS
|
||||
|
||||
@@ -149,6 +149,8 @@
|
||||
#define USB_PRODUCT_THRUSTMASTER_ESWAPX_PRO_PS4 0xd00e
|
||||
#define USB_PRODUCT_THRUSTMASTER_T_FLIGHT_HOTAS_ONE 0xb68c
|
||||
#define USB_PRODUCT_VALVE_STEAM_CONTROLLER_DONGLE 0x1142
|
||||
#define USB_PRODUCT_VALVE_STEAM_PROTEUS_DONGLE 0x1304
|
||||
#define USB_PRODUCT_VALVE_STEAM_NEREID_DONGLE 0x1305
|
||||
#define USB_PRODUCT_VICTRIX_FS_PRO 0x0203
|
||||
#define USB_PRODUCT_VICTRIX_FS_PRO_V2 0x0207
|
||||
#define USB_PRODUCT_XBOX360_XUSB_CONTROLLER 0x02a1 // XUSB driver software PID
|
||||
|
||||
Reference in New Issue
Block a user