diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj
index 7af7132b29..f5a1612f4d 100644
--- a/VisualC/SDL/SDL.vcxproj
+++ b/VisualC/SDL/SDL.vcxproj
@@ -82,16 +82,16 @@
C:\Program Files %28x86%29\Microsoft DirectX SDK %28June 2010%29\Lib\x86;$(LibraryPath)
- $(ProjectDir)/../../src;$(IncludePath)
+ $(ProjectDir)/../../src;$(ProjectDir)/../../src/core/windows;$(IncludePath)
- $(ProjectDir)/../../src;$(IncludePath)
+ $(ProjectDir)/../../src;$(ProjectDir)/../../src/core/windows;$(IncludePath)
- $(ProjectDir)/../../src;$(IncludePath)
+ $(ProjectDir)/../../src;$(ProjectDir)/../../src/core/windows;$(IncludePath)
- $(ProjectDir)/../../src;$(IncludePath)
+ $(ProjectDir)/../../src;$(ProjectDir)/../../src/core/windows;$(IncludePath)
@@ -625,6 +625,7 @@
+
diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters
index 77bd59d5fb..7e5aa144c7 100644
--- a/VisualC/SDL/SDL.vcxproj.filters
+++ b/VisualC/SDL/SDL.vcxproj.filters
@@ -1660,6 +1660,10 @@
+
+ joystick\hidapi
+
+
core\windows
diff --git a/Xcode/SDL/SDL.xcodeproj/project.pbxproj b/Xcode/SDL/SDL.xcodeproj/project.pbxproj
index bd3c41722c..6d57ae7e17 100644
--- a/Xcode/SDL/SDL.xcodeproj/project.pbxproj
+++ b/Xcode/SDL/SDL.xcodeproj/project.pbxproj
@@ -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 = ""; };
F388C95428B5F6F600661ECF /* SDL_hidapi_ps3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_ps3.c; sourceTree = ""; };
F39344CD2E99771B0056986F /* SDL_dlopennote.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDL_dlopennote.h; sourceTree = ""; };
+ F38C72482CEEB1DE000B0A90 /* SDL_hidapi_steam_triton.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_steam_triton.c; sourceTree = ""; };
F395BF6425633B2400942BFF /* SDL_crc32.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_crc32.c; sourceTree = ""; };
F395C1912569C68E00942BFF /* SDL_iokitjoystick_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_iokitjoystick_c.h; sourceTree = ""; };
F395C1922569C68E00942BFF /* SDL_iokitjoystick.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_iokitjoystick.c; sourceTree = ""; };
@@ -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 */,
diff --git a/src/hidapi/ios/hid.m b/src/hidapi/ios/hid.m
index cb5e231759..24a3c791ee 100644
--- a/src/hidapi/ios/hid.m
+++ b/src/hidapi/ios/hid.m
@@ -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
{
- 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 *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],
diff --git a/src/joystick/SDL_gamepad.c b/src/joystick/SDL_gamepad.c
index cab6221d12..b63a47a879 100644
--- a/src/joystick/SDL_gamepad.c
+++ b/src/joystick/SDL_gamepad.c
@@ -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
diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c
index d099928282..831cc541ca 100644
--- a/src/joystick/SDL_joystick.c
+++ b/src/joystick/SDL_joystick.c
@@ -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;
diff --git a/src/joystick/SDL_joystick_c.h b/src/joystick/SDL_joystick_c.h
index c6e1a7b792..f186616454 100644
--- a/src/joystick/SDL_joystick_c.h
+++ b/src/joystick/SDL_joystick_c.h
@@ -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);
diff --git a/src/joystick/controller_list.h b/src/joystick/controller_list.h
index 4abd813551..080983fa47 100644
--- a/src/joystick/controller_list.h
+++ b/src/joystick/controller_list.h
@@ -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)
};
diff --git a/src/joystick/controller_type.h b/src/joystick/controller_type.h
index 155c8ad11e..bb94840aff 100644
--- a/src/joystick/controller_type.h
+++ b/src/joystick/controller_type.h
@@ -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,
diff --git a/src/joystick/hidapi/SDL_hidapi_steam_triton.c b/src/joystick/hidapi/SDL_hidapi_steam_triton.c
new file mode 100644
index 0000000000..315d803d8a
--- /dev/null
+++ b/src/joystick/hidapi/SDL_hidapi_steam_triton.c
@@ -0,0 +1,532 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 2023 Max Maisel
+
+ 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
diff --git a/src/joystick/hidapi/SDL_hidapijoystick.c b/src/joystick/hidapi/SDL_hidapijoystick.c
index 07e7e3552e..7ce4815130 100644
--- a/src/joystick/hidapi/SDL_hidapijoystick.c
+++ b/src/joystick/hidapi/SDL_hidapijoystick.c
@@ -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,
diff --git a/src/joystick/hidapi/SDL_hidapijoystick_c.h b/src/joystick/hidapi/SDL_hidapijoystick_c.h
index 64a0f53ce1..12d87fc3b9 100644
--- a/src/joystick/hidapi/SDL_hidapijoystick_c.h
+++ b/src/joystick/hidapi/SDL_hidapijoystick_c.h
@@ -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;
diff --git a/src/joystick/hidapi/steam/controller_constants.h b/src/joystick/hidapi/steam/controller_constants.h
index 3a62d27245..b60021e0c5 100644
--- a/src/joystick/hidapi/steam/controller_constants.h
+++ b/src/joystick/hidapi/steam/controller_constants.h
@@ -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,
diff --git a/src/joystick/hidapi/steam/controller_structs.h b/src/joystick/hidapi/steam/controller_structs.h
index 90160db3a6..0496f8308e 100644
--- a/src/joystick/hidapi/steam/controller_structs.h
+++ b/src/joystick/hidapi/steam/controller_structs.h
@@ -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
diff --git a/src/joystick/usb_ids.h b/src/joystick/usb_ids.h
index 62a6c34a00..f34d843fd4 100644
--- a/src/joystick/usb_ids.h
+++ b/src/joystick/usb_ids.h
@@ -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