Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/sdl/joystick/hidapi/SDL_hidapi_steam.c
21917 views
1
/*
2
Simple DirectMedia Layer
3
Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
4
5
This software is provided 'as-is', without any express or implied
6
warranty. In no event will the authors be held liable for any damages
7
arising from the use of this software.
8
9
Permission is granted to anyone to use this software for any purpose,
10
including commercial applications, and to alter it and redistribute it
11
freely, subject to the following restrictions:
12
13
1. The origin of this software must not be misrepresented; you must not
14
claim that you wrote the original software. If you use this software
15
in a product, an acknowledgment in the product documentation would be
16
appreciated but is not required.
17
2. Altered source versions must be plainly marked as such, and must not be
18
misrepresented as being the original software.
19
3. This notice may not be removed or altered from any source distribution.
20
*/
21
#include "SDL_internal.h"
22
23
#ifdef SDL_JOYSTICK_HIDAPI
24
25
#include "../../SDL_hints_c.h"
26
#include "../SDL_sysjoystick.h"
27
#include "SDL_hidapijoystick_c.h"
28
29
#ifdef SDL_JOYSTICK_HIDAPI_STEAM
30
31
// Define this if you want to log all packets from the controller
32
// #define DEBUG_STEAM_PROTOCOL
33
34
#define SDL_HINT_JOYSTICK_HIDAPI_STEAM_PAIRING_ENABLED "SDL_JOYSTICK_HIDAPI_STEAM_PAIRING_ENABLED"
35
36
#if defined(SDL_PLATFORM_ANDROID) || defined(SDL_PLATFORM_IOS) || defined(SDL_PLATFORM_TVOS)
37
// This requires prompting for Bluetooth permissions, so make sure the application really wants it
38
#define SDL_HINT_JOYSTICK_HIDAPI_STEAM_DEFAULT false
39
#else
40
#define SDL_HINT_JOYSTICK_HIDAPI_STEAM_DEFAULT SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI, SDL_HIDAPI_DEFAULT)
41
#endif
42
43
#define PAIRING_STATE_DURATION_SECONDS 60
44
45
46
/*****************************************************************************************************/
47
48
#include "steam/controller_constants.h"
49
#include "steam/controller_structs.h"
50
51
enum
52
{
53
SDL_GAMEPAD_BUTTON_STEAM_RIGHT_PADDLE = 11,
54
SDL_GAMEPAD_BUTTON_STEAM_LEFT_PADDLE,
55
SDL_GAMEPAD_NUM_STEAM_BUTTONS,
56
};
57
58
typedef struct SteamControllerStateInternal_t
59
{
60
// Controller Type for this Controller State
61
Uint32 eControllerType;
62
63
// If packet num matches that on your prior call, then the controller state hasn't been changed since
64
// your last call and there is no need to process it
65
Uint32 unPacketNum;
66
67
// bit flags for each of the buttons
68
Uint64 ulButtons;
69
70
// Left pad coordinates
71
short sLeftPadX;
72
short sLeftPadY;
73
74
// Right pad coordinates
75
short sRightPadX;
76
short sRightPadY;
77
78
// Center pad coordinates
79
short sCenterPadX;
80
short sCenterPadY;
81
82
// Left analog stick coordinates
83
short sLeftStickX;
84
short sLeftStickY;
85
86
// Right analog stick coordinates
87
short sRightStickX;
88
short sRightStickY;
89
90
unsigned short sTriggerL;
91
unsigned short sTriggerR;
92
93
short sAccelX;
94
short sAccelY;
95
short sAccelZ;
96
97
short sGyroX;
98
short sGyroY;
99
short sGyroZ;
100
101
float sGyroQuatW;
102
float sGyroQuatX;
103
float sGyroQuatY;
104
float sGyroQuatZ;
105
106
short sGyroSteeringAngle;
107
108
unsigned short sBatteryLevel;
109
110
// Pressure sensor data.
111
unsigned short sPressurePadLeft;
112
unsigned short sPressurePadRight;
113
114
unsigned short sPressureBumperLeft;
115
unsigned short sPressureBumperRight;
116
117
// Internal state data
118
short sPrevLeftPad[2];
119
short sPrevLeftStick[2];
120
} SteamControllerStateInternal_t;
121
122
// Defines for ulButtons in SteamControllerStateInternal_t
123
#define STEAM_RIGHT_TRIGGER_MASK 0x00000001
124
#define STEAM_LEFT_TRIGGER_MASK 0x00000002
125
#define STEAM_RIGHT_BUMPER_MASK 0x00000004
126
#define STEAM_LEFT_BUMPER_MASK 0x00000008
127
#define STEAM_BUTTON_NORTH_MASK 0x00000010 // Y
128
#define STEAM_BUTTON_EAST_MASK 0x00000020 // B
129
#define STEAM_BUTTON_WEST_MASK 0x00000040 // X
130
#define STEAM_BUTTON_SOUTH_MASK 0x00000080 // A
131
#define STEAM_DPAD_UP_MASK 0x00000100 // DPAD UP
132
#define STEAM_DPAD_RIGHT_MASK 0x00000200 // DPAD RIGHT
133
#define STEAM_DPAD_LEFT_MASK 0x00000400 // DPAD LEFT
134
#define STEAM_DPAD_DOWN_MASK 0x00000800 // DPAD DOWN
135
#define STEAM_BUTTON_MENU_MASK 0x00001000 // SELECT
136
#define STEAM_BUTTON_STEAM_MASK 0x00002000 // GUIDE
137
#define STEAM_BUTTON_ESCAPE_MASK 0x00004000 // START
138
#define STEAM_BUTTON_BACK_LEFT_MASK 0x00008000
139
#define STEAM_BUTTON_BACK_RIGHT_MASK 0x00010000
140
#define STEAM_BUTTON_LEFTPAD_CLICKED_MASK 0x00020000
141
#define STEAM_BUTTON_RIGHTPAD_CLICKED_MASK 0x00040000
142
#define STEAM_LEFTPAD_FINGERDOWN_MASK 0x00080000
143
#define STEAM_RIGHTPAD_FINGERDOWN_MASK 0x00100000
144
#define STEAM_JOYSTICK_BUTTON_MASK 0x00400000
145
#define STEAM_LEFTPAD_AND_JOYSTICK_MASK 0x00800000
146
147
// Look for report version 0x0001, type WIRELESS (3), length >= 1 byte
148
#define D0G_IS_VALID_WIRELESS_EVENT(data, len) ((len) >= 5 && (data)[0] == 1 && (data)[1] == 0 && (data)[2] == 3 && (data)[3] >= 1)
149
#define D0G_GET_WIRELESS_EVENT_TYPE(data) ((data)[4])
150
#define D0G_WIRELESS_DISCONNECTED 1
151
#define D0G_WIRELESS_ESTABLISHED 2
152
#define D0G_WIRELESS_NEWLYPAIRED 3
153
154
#define D0G_IS_WIRELESS_DISCONNECT(data, len) (D0G_IS_VALID_WIRELESS_EVENT(data, len) && D0G_GET_WIRELESS_EVENT_TYPE(data) == D0G_WIRELESS_DISCONNECTED)
155
#define D0G_IS_WIRELESS_CONNECT(data, len) (D0G_IS_VALID_WIRELESS_EVENT(data, len) && D0G_GET_WIRELESS_EVENT_TYPE(data) != D0G_WIRELESS_DISCONNECTED)
156
157
158
#define MAX_REPORT_SEGMENT_PAYLOAD_SIZE 18
159
/*
160
* SteamControllerPacketAssembler has to be used when reading output repots from controllers.
161
*/
162
typedef struct
163
{
164
uint8_t uBuffer[MAX_REPORT_SEGMENT_PAYLOAD_SIZE * 8 + 1];
165
int nExpectedSegmentNumber;
166
bool bIsBle;
167
} SteamControllerPacketAssembler;
168
169
#undef clamp
170
#define clamp(val, min, max) (((val) > (max)) ? (max) : (((val) < (min)) ? (min) : (val)))
171
172
#undef offsetof
173
#define offsetof(s, m) (size_t) & (((s *)0)->m)
174
175
#ifdef DEBUG_STEAM_CONTROLLER
176
#define DPRINTF(format, ...) printf(format, ##__VA_ARGS__)
177
#define HEXDUMP(ptr, len) hexdump(ptr, len)
178
#else
179
#define DPRINTF(format, ...)
180
#define HEXDUMP(ptr, len)
181
#endif
182
#define printf SDL_Log
183
184
#define MAX_REPORT_SEGMENT_SIZE (MAX_REPORT_SEGMENT_PAYLOAD_SIZE + 2)
185
#define CALC_REPORT_SEGMENT_NUM(index) ((index / MAX_REPORT_SEGMENT_PAYLOAD_SIZE) & 0x07)
186
#define REPORT_SEGMENT_DATA_FLAG 0x80
187
#define REPORT_SEGMENT_LAST_FLAG 0x40
188
#define BLE_REPORT_NUMBER 0x03
189
190
#define STEAMCONTROLLER_TRIGGER_MAX_ANALOG 26000
191
192
// Enable mouse mode when using the Steam Controller locally
193
#undef ENABLE_MOUSE_MODE
194
195
// Wireless firmware quirk: the firmware intentionally signals "failure" when performing
196
// SET_FEATURE / GET_FEATURE when it actually means "pending radio roundtrip". The only
197
// way to make SET_FEATURE / GET_FEATURE work is to loop several times with a sleep. If
198
// it takes more than 50ms to get the response for SET_FEATURE / GET_FEATURE, we assume
199
// that the controller has failed.
200
#define RADIO_WORKAROUND_SLEEP_ATTEMPTS 50
201
#define RADIO_WORKAROUND_SLEEP_DURATION_US 500
202
203
// This was defined by experimentation. 2000 seemed to work but to give that extra bit of margin, set to 3ms.
204
#define CONTROLLER_CONFIGURATION_DELAY_US 3000
205
206
static uint8_t GetSegmentHeader(int nSegmentNumber, bool bLastPacket)
207
{
208
uint8_t header = REPORT_SEGMENT_DATA_FLAG;
209
header |= nSegmentNumber;
210
if (bLastPacket) {
211
header |= REPORT_SEGMENT_LAST_FLAG;
212
}
213
214
return header;
215
}
216
217
static void hexdump(const uint8_t *ptr, int len)
218
{
219
HIDAPI_DumpPacket("Data", ptr, len);
220
}
221
222
static void ResetSteamControllerPacketAssembler(SteamControllerPacketAssembler *pAssembler)
223
{
224
SDL_memset(pAssembler->uBuffer, 0, sizeof(pAssembler->uBuffer));
225
pAssembler->nExpectedSegmentNumber = 0;
226
}
227
228
static void InitializeSteamControllerPacketAssembler(SteamControllerPacketAssembler *pAssembler, bool bIsBle)
229
{
230
pAssembler->bIsBle = bIsBle;
231
ResetSteamControllerPacketAssembler(pAssembler);
232
}
233
234
// Returns:
235
// <0 on error
236
// 0 on not ready
237
// Complete packet size on completion
238
static int WriteSegmentToSteamControllerPacketAssembler(SteamControllerPacketAssembler *pAssembler, const uint8_t *pSegment, int nSegmentLength)
239
{
240
if (pAssembler->bIsBle) {
241
uint8_t uSegmentHeader = pSegment[1];
242
int nSegmentNumber = uSegmentHeader & 0x07;
243
244
HEXDUMP(pSegment, nSegmentLength);
245
246
if (pSegment[0] != BLE_REPORT_NUMBER) {
247
// We may get keyboard/mouse input events until controller stops sending them
248
return 0;
249
}
250
251
if (nSegmentLength != MAX_REPORT_SEGMENT_SIZE) {
252
printf("Bad segment size! %d\n", nSegmentLength);
253
hexdump(pSegment, nSegmentLength);
254
ResetSteamControllerPacketAssembler(pAssembler);
255
return -1;
256
}
257
258
DPRINTF("GOT PACKET HEADER = 0x%x\n", uSegmentHeader);
259
260
if (!(uSegmentHeader & REPORT_SEGMENT_DATA_FLAG)) {
261
// We get empty segments, just ignore them
262
return 0;
263
}
264
265
if (nSegmentNumber != pAssembler->nExpectedSegmentNumber) {
266
ResetSteamControllerPacketAssembler(pAssembler);
267
268
if (nSegmentNumber) {
269
// This happens occasionally
270
DPRINTF("Bad segment number, got %d, expected %d\n",
271
nSegmentNumber, pAssembler->nExpectedSegmentNumber);
272
return -1;
273
}
274
}
275
276
SDL_memcpy(pAssembler->uBuffer + nSegmentNumber * MAX_REPORT_SEGMENT_PAYLOAD_SIZE,
277
pSegment + 2, // ignore header and report number
278
MAX_REPORT_SEGMENT_PAYLOAD_SIZE);
279
280
if (uSegmentHeader & REPORT_SEGMENT_LAST_FLAG) {
281
pAssembler->nExpectedSegmentNumber = 0;
282
return (nSegmentNumber + 1) * MAX_REPORT_SEGMENT_PAYLOAD_SIZE;
283
}
284
285
pAssembler->nExpectedSegmentNumber++;
286
} else {
287
// Just pass through
288
SDL_memcpy(pAssembler->uBuffer,
289
pSegment,
290
nSegmentLength);
291
return nSegmentLength;
292
}
293
294
return 0;
295
}
296
297
#define BLE_MAX_READ_RETRIES 8
298
299
static int SetFeatureReport(SDL_HIDAPI_Device *dev, const unsigned char uBuffer[65], int nActualDataLen)
300
{
301
int nRet = -1;
302
303
DPRINTF("SetFeatureReport %p %p %d\n", dev, uBuffer, nActualDataLen);
304
305
if (dev->is_bluetooth) {
306
int nSegmentNumber = 0;
307
uint8_t uPacketBuffer[MAX_REPORT_SEGMENT_SIZE];
308
const unsigned char *pBufferPtr = uBuffer + 1;
309
310
if (nActualDataLen < 1) {
311
return -1;
312
}
313
314
// Skip report number in data
315
nActualDataLen--;
316
317
while (nActualDataLen > 0) {
318
int nBytesInPacket = nActualDataLen > MAX_REPORT_SEGMENT_PAYLOAD_SIZE ? MAX_REPORT_SEGMENT_PAYLOAD_SIZE : nActualDataLen;
319
320
nActualDataLen -= nBytesInPacket;
321
322
// Construct packet
323
SDL_memset(uPacketBuffer, 0, sizeof(uPacketBuffer));
324
uPacketBuffer[0] = BLE_REPORT_NUMBER;
325
uPacketBuffer[1] = GetSegmentHeader(nSegmentNumber, nActualDataLen == 0);
326
SDL_memcpy(&uPacketBuffer[2], pBufferPtr, nBytesInPacket);
327
328
pBufferPtr += nBytesInPacket;
329
nSegmentNumber++;
330
331
nRet = SDL_hid_send_feature_report(dev->dev, uPacketBuffer, sizeof(uPacketBuffer));
332
}
333
} else {
334
for (int nRetries = 0; nRetries < RADIO_WORKAROUND_SLEEP_ATTEMPTS; nRetries++) {
335
nRet = SDL_hid_send_feature_report(dev->dev, uBuffer, 65);
336
if (nRet >= 0) {
337
break;
338
}
339
340
SDL_DelayNS(RADIO_WORKAROUND_SLEEP_DURATION_US * 1000);
341
}
342
}
343
344
DPRINTF("SetFeatureReport() ret = %d\n", nRet);
345
346
return nRet;
347
}
348
349
static int GetFeatureReport(SDL_HIDAPI_Device *dev, unsigned char uBuffer[65])
350
{
351
int nRet = -1;
352
353
DPRINTF("GetFeatureReport( %p %p )\n", dev, uBuffer);
354
355
if (dev->is_bluetooth) {
356
int nRetries = 0;
357
uint8_t uSegmentBuffer[MAX_REPORT_SEGMENT_SIZE + 1];
358
uint8_t ucBytesToRead = MAX_REPORT_SEGMENT_SIZE;
359
uint8_t ucDataStartOffset = 0;
360
361
SteamControllerPacketAssembler assembler;
362
InitializeSteamControllerPacketAssembler(&assembler, dev->is_bluetooth);
363
364
// On Windows and macOS, BLE devices get 2 copies of the feature report ID, one that is removed by ReadFeatureReport,
365
// and one that's included in the buffer we receive. We pad the bytes to read and skip over the report ID
366
// if necessary.
367
#if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_MACOS)
368
++ucBytesToRead;
369
++ucDataStartOffset;
370
#endif
371
372
while (nRetries < BLE_MAX_READ_RETRIES) {
373
SDL_memset(uSegmentBuffer, 0, sizeof(uSegmentBuffer));
374
uSegmentBuffer[0] = BLE_REPORT_NUMBER;
375
nRet = SDL_hid_get_feature_report(dev->dev, uSegmentBuffer, ucBytesToRead);
376
377
DPRINTF("GetFeatureReport ble ret=%d\n", nRet);
378
HEXDUMP(uSegmentBuffer, nRet);
379
380
// Zero retry counter if we got data
381
if (nRet > 2 && (uSegmentBuffer[ucDataStartOffset + 1] & REPORT_SEGMENT_DATA_FLAG)) {
382
nRetries = 0;
383
} else {
384
nRetries++;
385
}
386
387
if (nRet > 0) {
388
int nPacketLength = WriteSegmentToSteamControllerPacketAssembler(&assembler,
389
uSegmentBuffer + ucDataStartOffset,
390
nRet - ucDataStartOffset);
391
392
if (nPacketLength > 0 && nPacketLength < 65) {
393
// Leave space for "report number"
394
uBuffer[0] = 0;
395
SDL_memcpy(uBuffer + 1, assembler.uBuffer, nPacketLength);
396
return nPacketLength;
397
}
398
}
399
}
400
printf("Could not get a full ble packet after %d retries\n", nRetries);
401
return -1;
402
} else {
403
SDL_memset(uBuffer, 0, 65);
404
405
for (int nRetries = 0; nRetries < RADIO_WORKAROUND_SLEEP_ATTEMPTS; nRetries++) {
406
nRet = SDL_hid_get_feature_report(dev->dev, uBuffer, 65);
407
if (nRet >= 0) {
408
break;
409
}
410
411
SDL_DelayNS(RADIO_WORKAROUND_SLEEP_DURATION_US * 1000);
412
}
413
414
DPRINTF("GetFeatureReport USB ret=%d\n", nRet);
415
HEXDUMP(uBuffer, nRet);
416
}
417
418
return nRet;
419
}
420
421
static int ReadResponse(SDL_HIDAPI_Device *dev, uint8_t uBuffer[65], int nExpectedResponse)
422
{
423
for (int nRetries = 0; nRetries < 10; nRetries++) {
424
int nRet = GetFeatureReport(dev, uBuffer);
425
426
DPRINTF("ReadResponse( %p %p 0x%x )\n", dev, uBuffer, nExpectedResponse);
427
428
if (nRet < 0) {
429
continue;
430
}
431
432
DPRINTF("ReadResponse got %d bytes of data: ", nRet);
433
HEXDUMP(uBuffer, nRet);
434
435
if (uBuffer[1] != nExpectedResponse) {
436
continue;
437
}
438
439
return nRet;
440
}
441
return -1;
442
}
443
444
//---------------------------------------------------------------------------
445
// Reset steam controller (unmap buttons and pads) and re-fetch capability bits
446
//---------------------------------------------------------------------------
447
static bool ResetSteamController(SDL_HIDAPI_Device *dev, bool bSuppressErrorSpew, uint32_t *punUpdateRateUS)
448
{
449
// Firmware quirk: Set Feature and Get Feature requests always require a 65-byte buffer.
450
unsigned char buf[65];
451
unsigned int i;
452
int res = -1;
453
int nSettings = 0;
454
int nAttributesLength;
455
FeatureReportMsg *msg;
456
uint32_t unUpdateRateUS = 9000; // Good default rate
457
458
DPRINTF("ResetSteamController hid=%p\n", dev);
459
460
buf[0] = 0;
461
buf[1] = ID_GET_ATTRIBUTES_VALUES;
462
res = SetFeatureReport(dev, buf, 2);
463
if (res < 0) {
464
if (!bSuppressErrorSpew) {
465
printf("GET_ATTRIBUTES_VALUES failed for controller %p\n", dev);
466
}
467
return false;
468
}
469
470
// Retrieve GET_ATTRIBUTES_VALUES result
471
// Wireless controller endpoints without a connected controller will return nAttrs == 0
472
res = ReadResponse(dev, buf, ID_GET_ATTRIBUTES_VALUES);
473
if (res < 0 || buf[1] != ID_GET_ATTRIBUTES_VALUES) {
474
HEXDUMP(buf, res);
475
if (!bSuppressErrorSpew) {
476
printf("Bad GET_ATTRIBUTES_VALUES response for controller %p\n", dev);
477
}
478
return false;
479
}
480
481
nAttributesLength = buf[2];
482
if (nAttributesLength > res) {
483
if (!bSuppressErrorSpew) {
484
printf("Bad GET_ATTRIBUTES_VALUES response for controller %p\n", dev);
485
}
486
return false;
487
}
488
489
msg = (FeatureReportMsg *)&buf[1];
490
for (i = 0; i < (int)msg->header.length / sizeof(ControllerAttribute); ++i) {
491
uint8_t unAttribute = msg->payload.getAttributes.attributes[i].attributeTag;
492
uint32_t unValue = msg->payload.getAttributes.attributes[i].attributeValue;
493
494
switch (unAttribute) {
495
case ATTRIB_UNIQUE_ID:
496
break;
497
case ATTRIB_PRODUCT_ID:
498
break;
499
case ATTRIB_CAPABILITIES:
500
break;
501
case ATTRIB_CONNECTION_INTERVAL_IN_US:
502
unUpdateRateUS = unValue;
503
break;
504
default:
505
break;
506
}
507
}
508
if (punUpdateRateUS) {
509
*punUpdateRateUS = unUpdateRateUS;
510
}
511
512
// Clear digital button mappings
513
buf[0] = 0;
514
buf[1] = ID_CLEAR_DIGITAL_MAPPINGS;
515
res = SetFeatureReport(dev, buf, 2);
516
if (res < 0) {
517
if (!bSuppressErrorSpew) {
518
printf("CLEAR_DIGITAL_MAPPINGS failed for controller %p\n", dev);
519
}
520
return false;
521
}
522
523
// Reset the default settings
524
SDL_memset(buf, 0, 65);
525
buf[1] = ID_LOAD_DEFAULT_SETTINGS;
526
buf[2] = 0;
527
res = SetFeatureReport(dev, buf, 3);
528
if (res < 0) {
529
if (!bSuppressErrorSpew) {
530
printf("LOAD_DEFAULT_SETTINGS failed for controller %p\n", dev);
531
}
532
return false;
533
}
534
535
// Apply custom settings - clear trackpad modes (cancel mouse emulation), etc
536
#define ADD_SETTING(SETTING, VALUE) \
537
buf[3 + nSettings * 3] = SETTING; \
538
buf[3 + nSettings * 3 + 1] = ((uint16_t)VALUE) & 0xFF; \
539
buf[3 + nSettings * 3 + 2] = ((uint16_t)VALUE) >> 8; \
540
++nSettings;
541
542
SDL_memset(buf, 0, 65);
543
buf[1] = ID_SET_SETTINGS_VALUES;
544
ADD_SETTING(SETTING_WIRELESS_PACKET_VERSION, 2);
545
ADD_SETTING(SETTING_LEFT_TRACKPAD_MODE, TRACKPAD_NONE);
546
#ifdef ENABLE_MOUSE_MODE
547
ADD_SETTING(SETTING_RIGHT_TRACKPAD_MODE, TRACKPAD_ABSOLUTE_MOUSE);
548
ADD_SETTING(SETTING_SMOOTH_ABSOLUTE_MOUSE, 1);
549
ADD_SETTING(SETTING_MOMENTUM_MAXIMUM_VELOCITY, 20000); // [0-20000] default 8000
550
ADD_SETTING(SETTING_MOMENTUM_DECAY_AMOUNT, 50); // [0-50] default 5
551
#else
552
ADD_SETTING(SETTING_RIGHT_TRACKPAD_MODE, TRACKPAD_NONE);
553
ADD_SETTING(SETTING_SMOOTH_ABSOLUTE_MOUSE, 0);
554
#endif
555
buf[2] = (unsigned char)(nSettings * 3);
556
557
res = SetFeatureReport(dev, buf, 3 + nSettings * 3);
558
if (res < 0) {
559
if (!bSuppressErrorSpew) {
560
printf("SET_SETTINGS failed for controller %p\n", dev);
561
}
562
return false;
563
}
564
565
#ifdef ENABLE_MOUSE_MODE
566
// Wait for ID_CLEAR_DIGITAL_MAPPINGS to be processed on the controller
567
bool bMappingsCleared = false;
568
int iRetry;
569
for (iRetry = 0; iRetry < 2; ++iRetry) {
570
SDL_memset(buf, 0, 65);
571
buf[1] = ID_GET_DIGITAL_MAPPINGS;
572
buf[2] = 1; // one byte - requesting from index 0
573
buf[3] = 0;
574
res = SetFeatureReport(dev, buf, 4);
575
if (res < 0) {
576
printf("GET_DIGITAL_MAPPINGS failed for controller %p\n", dev);
577
return false;
578
}
579
580
res = ReadResponse(dev, buf, ID_GET_DIGITAL_MAPPINGS);
581
if (res < 0 || buf[1] != ID_GET_DIGITAL_MAPPINGS) {
582
printf("Bad GET_DIGITAL_MAPPINGS response for controller %p\n", dev);
583
return false;
584
}
585
586
// If the length of the digital mappings result is not 1 (index byte, no mappings) then clearing hasn't executed
587
if (buf[2] == 1 && buf[3] == 0xFF) {
588
bMappingsCleared = true;
589
break;
590
}
591
usleep(CONTROLLER_CONFIGURATION_DELAY_US);
592
}
593
594
if (!bMappingsCleared && !bSuppressErrorSpew) {
595
printf("Warning: CLEAR_DIGITAL_MAPPINGS never completed for controller %p\n", dev);
596
}
597
598
// Set our new mappings
599
SDL_memset(buf, 0, 65);
600
buf[1] = ID_SET_DIGITAL_MAPPINGS;
601
buf[2] = 6; // 2 settings x 3 bytes
602
buf[3] = IO_DIGITAL_BUTTON_RIGHT_TRIGGER;
603
buf[4] = DEVICE_MOUSE;
604
buf[5] = MOUSE_BTN_LEFT;
605
buf[6] = IO_DIGITAL_BUTTON_LEFT_TRIGGER;
606
buf[7] = DEVICE_MOUSE;
607
buf[8] = MOUSE_BTN_RIGHT;
608
609
res = SetFeatureReport(dev, buf, 9);
610
if (res < 0) {
611
if (!bSuppressErrorSpew) {
612
printf("SET_DIGITAL_MAPPINGS failed for controller %p\n", dev);
613
}
614
return false;
615
}
616
#endif // ENABLE_MOUSE_MODE
617
618
return true;
619
}
620
621
//---------------------------------------------------------------------------
622
// Read from a Steam Controller
623
//---------------------------------------------------------------------------
624
static int ReadSteamController(SDL_hid_device *dev, uint8_t *pData, int nDataSize)
625
{
626
SDL_memset(pData, 0, nDataSize);
627
pData[0] = BLE_REPORT_NUMBER; // hid_read will also overwrite this with the same value, 0x03
628
return SDL_hid_read(dev, pData, nDataSize);
629
}
630
631
//---------------------------------------------------------------------------
632
// Set Steam Controller pairing state
633
//---------------------------------------------------------------------------
634
static void SetPairingState(SDL_HIDAPI_Device *dev, bool bEnablePairing)
635
{
636
unsigned char buf[65];
637
SDL_memset(buf, 0, 65);
638
buf[1] = ID_ENABLE_PAIRING;
639
buf[2] = 2; // 2 payload bytes: bool + timeout
640
buf[3] = bEnablePairing ? 1 : 0;
641
buf[4] = bEnablePairing ? PAIRING_STATE_DURATION_SECONDS : 0;
642
SetFeatureReport(dev, buf, 5);
643
}
644
645
//---------------------------------------------------------------------------
646
// Commit Steam Controller pairing
647
//---------------------------------------------------------------------------
648
static void CommitPairing(SDL_HIDAPI_Device *dev)
649
{
650
unsigned char buf[65];
651
SDL_memset(buf, 0, 65);
652
buf[1] = ID_DONGLE_COMMIT_DEVICE;
653
SetFeatureReport(dev, buf, 2);
654
}
655
656
//---------------------------------------------------------------------------
657
// Close a Steam Controller
658
//---------------------------------------------------------------------------
659
static void CloseSteamController(SDL_HIDAPI_Device *dev)
660
{
661
// Switch the Steam Controller back to lizard mode so it works with the OS
662
unsigned char buf[65];
663
int nSettings = 0;
664
665
// Reset digital button mappings
666
SDL_memset(buf, 0, 65);
667
buf[1] = ID_SET_DEFAULT_DIGITAL_MAPPINGS;
668
SetFeatureReport(dev, buf, 2);
669
670
// Reset the default settings
671
SDL_memset(buf, 0, 65);
672
buf[1] = ID_LOAD_DEFAULT_SETTINGS;
673
buf[2] = 0;
674
SetFeatureReport(dev, buf, 3);
675
676
// Reset mouse mode for lizard mode
677
SDL_memset(buf, 0, 65);
678
buf[1] = ID_SET_SETTINGS_VALUES;
679
ADD_SETTING(SETTING_RIGHT_TRACKPAD_MODE, TRACKPAD_ABSOLUTE_MOUSE);
680
buf[2] = (unsigned char)(nSettings * 3);
681
SetFeatureReport(dev, buf, 3 + nSettings * 3);
682
}
683
684
//---------------------------------------------------------------------------
685
// Scale and clamp values to a range
686
//---------------------------------------------------------------------------
687
static float RemapValClamped(float val, float A, float B, float C, float D)
688
{
689
if (A == B) {
690
return (val - B) >= 0.0f ? D : C;
691
} else {
692
float cVal = (val - A) / (B - A);
693
cVal = clamp(cVal, 0.0f, 1.0f);
694
695
return C + (D - C) * cVal;
696
}
697
}
698
699
//---------------------------------------------------------------------------
700
// Rotate the pad coordinates
701
//---------------------------------------------------------------------------
702
static void RotatePad(int *pX, int *pY, float flAngleInRad)
703
{
704
int origX = *pX, origY = *pY;
705
706
*pX = (int)(SDL_cosf(flAngleInRad) * origX - SDL_sinf(flAngleInRad) * origY);
707
*pY = (int)(SDL_sinf(flAngleInRad) * origX + SDL_cosf(flAngleInRad) * origY);
708
}
709
710
//---------------------------------------------------------------------------
711
// Format the first part of the state packet
712
//---------------------------------------------------------------------------
713
static void FormatStatePacketUntilGyro(SteamControllerStateInternal_t *pState, ValveControllerStatePacket_t *pStatePacket)
714
{
715
int nLeftPadX;
716
int nLeftPadY;
717
int nRightPadX;
718
int nRightPadY;
719
int nPadOffset;
720
721
// 15 degrees in rad
722
const float flRotationAngle = 0.261799f;
723
724
SDL_memset(pState, 0, offsetof(SteamControllerStateInternal_t, sBatteryLevel));
725
726
// pState->eControllerType = m_eControllerType;
727
pState->eControllerType = 2; // k_eControllerType_SteamController;
728
pState->unPacketNum = pStatePacket->unPacketNum;
729
730
// We have a chunk of trigger data in the packet format here, so zero it out afterwards
731
SDL_memcpy(&pState->ulButtons, &pStatePacket->ButtonTriggerData.ulButtons, 8);
732
pState->ulButtons &= ~0xFFFF000000LL;
733
734
// The firmware uses this bit to tell us what kind of data is packed into the left two axes
735
if (pStatePacket->ButtonTriggerData.ulButtons & STEAM_LEFTPAD_FINGERDOWN_MASK) {
736
// Finger-down bit not set; "left pad" is actually trackpad
737
pState->sLeftPadX = pState->sPrevLeftPad[0] = pStatePacket->sLeftPadX;
738
pState->sLeftPadY = pState->sPrevLeftPad[1] = pStatePacket->sLeftPadY;
739
740
if (pStatePacket->ButtonTriggerData.ulButtons & STEAM_LEFTPAD_AND_JOYSTICK_MASK) {
741
// The controller is interleaving both stick and pad data, both are active
742
pState->sLeftStickX = pState->sPrevLeftStick[0];
743
pState->sLeftStickY = pState->sPrevLeftStick[1];
744
} else {
745
// The stick is not active
746
pState->sPrevLeftStick[0] = 0;
747
pState->sPrevLeftStick[1] = 0;
748
}
749
} else {
750
// Finger-down bit not set; "left pad" is actually joystick
751
752
// XXX there's a firmware bug where sometimes padX is 0 and padY is a large number (actually the battery voltage)
753
// If that happens skip this packet and report last frames stick
754
/*
755
if ( m_eControllerType == k_eControllerType_SteamControllerV2 && pStatePacket->sLeftPadY > 900 ) {
756
pState->sLeftStickX = pState->sPrevLeftStick[0];
757
pState->sLeftStickY = pState->sPrevLeftStick[1];
758
} else
759
*/
760
{
761
pState->sPrevLeftStick[0] = pState->sLeftStickX = pStatePacket->sLeftPadX;
762
pState->sPrevLeftStick[1] = pState->sLeftStickY = pStatePacket->sLeftPadY;
763
}
764
/*
765
if (m_eControllerType == k_eControllerType_SteamControllerV2) {
766
UpdateV2JoystickCap(&state);
767
}
768
*/
769
770
if (pStatePacket->ButtonTriggerData.ulButtons & STEAM_LEFTPAD_AND_JOYSTICK_MASK) {
771
// The controller is interleaving both stick and pad data, both are active
772
pState->sLeftPadX = pState->sPrevLeftPad[0];
773
pState->sLeftPadY = pState->sPrevLeftPad[1];
774
} else {
775
// The trackpad is not active
776
pState->sPrevLeftPad[0] = 0;
777
pState->sPrevLeftPad[1] = 0;
778
779
// Old controllers send trackpad click for joystick button when trackpad is not active
780
if (pState->ulButtons & STEAM_BUTTON_LEFTPAD_CLICKED_MASK) {
781
pState->ulButtons &= ~STEAM_BUTTON_LEFTPAD_CLICKED_MASK;
782
pState->ulButtons |= STEAM_JOYSTICK_BUTTON_MASK;
783
}
784
}
785
}
786
787
// Fingerdown bit indicates if the packed left axis data was joystick or pad,
788
// but if we are interleaving both, the left finger is definitely on the pad.
789
if (pStatePacket->ButtonTriggerData.ulButtons & STEAM_LEFTPAD_AND_JOYSTICK_MASK) {
790
pState->ulButtons |= STEAM_LEFTPAD_FINGERDOWN_MASK;
791
}
792
793
pState->sRightPadX = pStatePacket->sRightPadX;
794
pState->sRightPadY = pStatePacket->sRightPadY;
795
796
nLeftPadX = pState->sLeftPadX;
797
nLeftPadY = pState->sLeftPadY;
798
nRightPadX = pState->sRightPadX;
799
nRightPadY = pState->sRightPadY;
800
801
RotatePad(&nLeftPadX, &nLeftPadY, -flRotationAngle);
802
RotatePad(&nRightPadX, &nRightPadY, flRotationAngle);
803
804
if (pState->ulButtons & STEAM_LEFTPAD_FINGERDOWN_MASK) {
805
nPadOffset = 1000;
806
} else {
807
nPadOffset = 0;
808
}
809
810
pState->sLeftPadX = (short)clamp(nLeftPadX + nPadOffset, SDL_MIN_SINT16, SDL_MAX_SINT16);
811
pState->sLeftPadY = (short)clamp(nLeftPadY + nPadOffset, SDL_MIN_SINT16, SDL_MAX_SINT16);
812
813
nPadOffset = 0;
814
if (pState->ulButtons & STEAM_RIGHTPAD_FINGERDOWN_MASK) {
815
nPadOffset = 1000;
816
} else {
817
nPadOffset = 0;
818
}
819
820
pState->sRightPadX = (short)clamp(nRightPadX + nPadOffset, SDL_MIN_SINT16, SDL_MAX_SINT16);
821
pState->sRightPadY = (short)clamp(nRightPadY + nPadOffset, SDL_MIN_SINT16, SDL_MAX_SINT16);
822
823
pState->sTriggerL = (unsigned short)RemapValClamped((float)((pStatePacket->ButtonTriggerData.Triggers.nLeft << 7) | pStatePacket->ButtonTriggerData.Triggers.nLeft), 0, STEAMCONTROLLER_TRIGGER_MAX_ANALOG, 0, SDL_MAX_SINT16);
824
pState->sTriggerR = (unsigned short)RemapValClamped((float)((pStatePacket->ButtonTriggerData.Triggers.nRight << 7) | pStatePacket->ButtonTriggerData.Triggers.nRight), 0, STEAMCONTROLLER_TRIGGER_MAX_ANALOG, 0, SDL_MAX_SINT16);
825
}
826
827
//---------------------------------------------------------------------------
828
// Update Steam Controller state from a BLE data packet, returns true if it parsed data
829
//---------------------------------------------------------------------------
830
static bool UpdateBLESteamControllerState(const uint8_t *pData, int nDataSize, SteamControllerStateInternal_t *pState)
831
{
832
int nLeftPadX;
833
int nLeftPadY;
834
int nRightPadX;
835
int nRightPadY;
836
int nPadOffset;
837
uint32_t ucOptionDataMask;
838
839
// 15 degrees in rad
840
const float flRotationAngle = 0.261799f;
841
842
pState->unPacketNum++;
843
ucOptionDataMask = (*pData++ & 0xF0);
844
ucOptionDataMask |= (uint32_t)(*pData++) << 8;
845
if (ucOptionDataMask & k_EBLEButtonChunk1) {
846
SDL_memcpy(&pState->ulButtons, pData, 3);
847
pData += 3;
848
}
849
if (ucOptionDataMask & k_EBLEButtonChunk2) {
850
// The middle 2 bytes of the button bits over the wire are triggers when over the wire and non-SC buttons in the internal controller state packet
851
pState->sTriggerL = (unsigned short)RemapValClamped((float)((pData[0] << 7) | pData[0]), 0, STEAMCONTROLLER_TRIGGER_MAX_ANALOG, 0, SDL_MAX_SINT16);
852
pState->sTriggerR = (unsigned short)RemapValClamped((float)((pData[1] << 7) | pData[1]), 0, STEAMCONTROLLER_TRIGGER_MAX_ANALOG, 0, SDL_MAX_SINT16);
853
pData += 2;
854
}
855
if (ucOptionDataMask & k_EBLEButtonChunk3) {
856
uint8_t *pButtonByte = (uint8_t *)&pState->ulButtons;
857
pButtonByte[5] = *pData++;
858
pButtonByte[6] = *pData++;
859
pButtonByte[7] = *pData++;
860
}
861
if (ucOptionDataMask & k_EBLELeftJoystickChunk) {
862
// This doesn't handle any of the special headcrab stuff for raw joystick which is OK for now since that FW doesn't support
863
// this protocol yet either
864
int nLength = sizeof(pState->sLeftStickX) + sizeof(pState->sLeftStickY);
865
SDL_memcpy(&pState->sLeftStickX, pData, nLength);
866
pData += nLength;
867
}
868
if (ucOptionDataMask & k_EBLELeftTrackpadChunk) {
869
int nLength = sizeof(pState->sLeftPadX) + sizeof(pState->sLeftPadY);
870
SDL_memcpy(&pState->sLeftPadX, pData, nLength);
871
if (pState->ulButtons & STEAM_LEFTPAD_FINGERDOWN_MASK) {
872
nPadOffset = 1000;
873
} else {
874
nPadOffset = 0;
875
}
876
877
nLeftPadX = pState->sLeftPadX;
878
nLeftPadY = pState->sLeftPadY;
879
RotatePad(&nLeftPadX, &nLeftPadY, -flRotationAngle);
880
pState->sLeftPadX = (short)clamp(nLeftPadX + nPadOffset, SDL_MIN_SINT16, SDL_MAX_SINT16);
881
pState->sLeftPadY = (short)clamp(nLeftPadY + nPadOffset, SDL_MIN_SINT16, SDL_MAX_SINT16);
882
pData += nLength;
883
}
884
if (ucOptionDataMask & k_EBLERightTrackpadChunk) {
885
int nLength = sizeof(pState->sRightPadX) + sizeof(pState->sRightPadY);
886
887
SDL_memcpy(&pState->sRightPadX, pData, nLength);
888
889
if (pState->ulButtons & STEAM_RIGHTPAD_FINGERDOWN_MASK) {
890
nPadOffset = 1000;
891
} else {
892
nPadOffset = 0;
893
}
894
895
nRightPadX = pState->sRightPadX;
896
nRightPadY = pState->sRightPadY;
897
RotatePad(&nRightPadX, &nRightPadY, flRotationAngle);
898
pState->sRightPadX = (short)clamp(nRightPadX + nPadOffset, SDL_MIN_SINT16, SDL_MAX_SINT16);
899
pState->sRightPadY = (short)clamp(nRightPadY + nPadOffset, SDL_MIN_SINT16, SDL_MAX_SINT16);
900
pData += nLength;
901
}
902
if (ucOptionDataMask & k_EBLEIMUAccelChunk) {
903
int nLength = sizeof(pState->sAccelX) + sizeof(pState->sAccelY) + sizeof(pState->sAccelZ);
904
SDL_memcpy(&pState->sAccelX, pData, nLength);
905
pData += nLength;
906
}
907
if (ucOptionDataMask & k_EBLEIMUGyroChunk) {
908
int nLength = sizeof(pState->sAccelX) + sizeof(pState->sAccelY) + sizeof(pState->sAccelZ);
909
SDL_memcpy(&pState->sGyroX, pData, nLength);
910
pData += nLength;
911
}
912
if (ucOptionDataMask & k_EBLEIMUQuatChunk) {
913
int nLength = sizeof(pState->sGyroQuatW) + sizeof(pState->sGyroQuatX) + sizeof(pState->sGyroQuatY) + sizeof(pState->sGyroQuatZ);
914
SDL_memcpy(&pState->sGyroQuatW, pData, nLength);
915
pData += nLength;
916
}
917
return true;
918
}
919
920
//---------------------------------------------------------------------------
921
// Update Steam Controller state from a data packet, returns true if it parsed data
922
//---------------------------------------------------------------------------
923
static bool UpdateSteamControllerState(const uint8_t *pData, int nDataSize, SteamControllerStateInternal_t *pState)
924
{
925
ValveInReport_t *pInReport = (ValveInReport_t *)pData;
926
927
if (pInReport->header.unReportVersion != k_ValveInReportMsgVersion) {
928
if ((pData[0] & 0x0F) == k_EBLEReportState) {
929
return UpdateBLESteamControllerState(pData, nDataSize, pState);
930
}
931
return false;
932
}
933
934
if ((pInReport->header.ucType != ID_CONTROLLER_STATE) &&
935
(pInReport->header.ucType != ID_CONTROLLER_BLE_STATE)) {
936
return false;
937
}
938
939
if (pInReport->header.ucType == ID_CONTROLLER_STATE) {
940
ValveControllerStatePacket_t *pStatePacket = &pInReport->payload.controllerState;
941
942
// No new data to process; indicate that we received a state packet, but otherwise do nothing.
943
if (pState->unPacketNum == pStatePacket->unPacketNum) {
944
return true;
945
}
946
947
FormatStatePacketUntilGyro(pState, pStatePacket);
948
949
pState->sAccelX = pStatePacket->sAccelX;
950
pState->sAccelY = pStatePacket->sAccelY;
951
pState->sAccelZ = pStatePacket->sAccelZ;
952
953
pState->sGyroQuatW = pStatePacket->sGyroQuatW;
954
pState->sGyroQuatX = pStatePacket->sGyroQuatX;
955
pState->sGyroQuatY = pStatePacket->sGyroQuatY;
956
pState->sGyroQuatZ = pStatePacket->sGyroQuatZ;
957
958
pState->sGyroX = pStatePacket->sGyroX;
959
pState->sGyroY = pStatePacket->sGyroY;
960
pState->sGyroZ = pStatePacket->sGyroZ;
961
962
} else if (pInReport->header.ucType == ID_CONTROLLER_BLE_STATE) {
963
ValveControllerBLEStatePacket_t *pBLEStatePacket = &pInReport->payload.controllerBLEState;
964
ValveControllerStatePacket_t *pStatePacket = &pInReport->payload.controllerState;
965
966
// No new data to process; indicate that we received a state packet, but otherwise do nothing.
967
if (pState->unPacketNum == pStatePacket->unPacketNum) {
968
return true;
969
}
970
971
FormatStatePacketUntilGyro(pState, pStatePacket);
972
973
switch (pBLEStatePacket->ucGyroDataType) {
974
case 1:
975
pState->sGyroQuatW = ((float)pBLEStatePacket->sGyro[0]);
976
pState->sGyroQuatX = ((float)pBLEStatePacket->sGyro[1]);
977
pState->sGyroQuatY = ((float)pBLEStatePacket->sGyro[2]);
978
pState->sGyroQuatZ = ((float)pBLEStatePacket->sGyro[3]);
979
break;
980
981
case 2:
982
pState->sAccelX = pBLEStatePacket->sGyro[0];
983
pState->sAccelY = pBLEStatePacket->sGyro[1];
984
pState->sAccelZ = pBLEStatePacket->sGyro[2];
985
break;
986
987
case 3:
988
pState->sGyroX = pBLEStatePacket->sGyro[0];
989
pState->sGyroY = pBLEStatePacket->sGyro[1];
990
pState->sGyroZ = pBLEStatePacket->sGyro[2];
991
break;
992
993
default:
994
break;
995
}
996
}
997
998
return true;
999
}
1000
1001
/*****************************************************************************************************/
1002
1003
typedef struct
1004
{
1005
SDL_HIDAPI_Device *device;
1006
bool connected;
1007
bool report_sensors;
1008
uint32_t update_rate_in_us;
1009
Uint64 sensor_timestamp;
1010
Uint64 pairing_time;
1011
1012
SteamControllerPacketAssembler m_assembler;
1013
SteamControllerStateInternal_t m_state;
1014
SteamControllerStateInternal_t m_last_state;
1015
} SDL_DriverSteam_Context;
1016
1017
static bool IsDongle(Uint16 product_id)
1018
{
1019
return (product_id == USB_PRODUCT_VALVE_STEAM_CONTROLLER_DONGLE);
1020
}
1021
1022
static void HIDAPI_DriverSteam_RegisterHints(SDL_HintCallback callback, void *userdata)
1023
{
1024
SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_STEAM, callback, userdata);
1025
}
1026
1027
static void HIDAPI_DriverSteam_UnregisterHints(SDL_HintCallback callback, void *userdata)
1028
{
1029
SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_STEAM, callback, userdata);
1030
}
1031
1032
static bool HIDAPI_DriverSteam_IsEnabled(void)
1033
{
1034
return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_STEAM, SDL_HINT_JOYSTICK_HIDAPI_STEAM_DEFAULT);
1035
}
1036
1037
static bool HIDAPI_DriverSteam_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)
1038
{
1039
if (!SDL_IsJoystickSteamController(vendor_id, product_id)) {
1040
return false;
1041
}
1042
1043
if (!device) {
1044
// Might be supported by this driver, enumerate and find out
1045
return true;
1046
}
1047
1048
if (device->is_bluetooth) {
1049
return true;
1050
}
1051
1052
if (IsDongle(product_id)) {
1053
if (interface_number >= 1 && interface_number <= 4) {
1054
// This is one of the wireless controller interfaces
1055
return true;
1056
}
1057
} else {
1058
if (interface_number == 2) {
1059
// This is the controller interface (not mouse or keyboard)
1060
return true;
1061
}
1062
}
1063
return false;
1064
}
1065
1066
static void HIDAPI_DriverSteam_SetPairingState(SDL_DriverSteam_Context *ctx, bool enabled)
1067
{
1068
// Only have one dongle in pairing mode at a time
1069
static SDL_DriverSteam_Context *s_PairingContext = NULL;
1070
1071
if (enabled && s_PairingContext != NULL) {
1072
return;
1073
}
1074
1075
if (!enabled && s_PairingContext != ctx) {
1076
return;
1077
}
1078
1079
if (ctx->connected) {
1080
return;
1081
}
1082
1083
SetPairingState(ctx->device, enabled);
1084
1085
if (enabled) {
1086
ctx->pairing_time = SDL_GetTicks();
1087
s_PairingContext = ctx;
1088
} else {
1089
ctx->pairing_time = 0;
1090
s_PairingContext = NULL;
1091
}
1092
}
1093
1094
static void HIDAPI_DriverSteam_RenewPairingState(SDL_DriverSteam_Context *ctx)
1095
{
1096
Uint64 now = SDL_GetTicks();
1097
1098
if (now >= ctx->pairing_time + PAIRING_STATE_DURATION_SECONDS * 1000) {
1099
SetPairingState(ctx->device, true);
1100
ctx->pairing_time = now;
1101
}
1102
}
1103
1104
static void HIDAPI_DriverSteam_CommitPairing(SDL_DriverSteam_Context *ctx)
1105
{
1106
CommitPairing(ctx->device);
1107
}
1108
1109
static void SDLCALL SDL_PairingEnabledHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
1110
{
1111
SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)userdata;
1112
bool enabled = SDL_GetStringBoolean(hint, false);
1113
1114
HIDAPI_DriverSteam_SetPairingState(ctx, enabled);
1115
}
1116
1117
static bool HIDAPI_DriverSteam_InitDevice(SDL_HIDAPI_Device *device)
1118
{
1119
SDL_DriverSteam_Context *ctx;
1120
1121
ctx = (SDL_DriverSteam_Context *)SDL_calloc(1, sizeof(*ctx));
1122
if (!ctx) {
1123
return false;
1124
}
1125
ctx->device = device;
1126
device->context = ctx;
1127
1128
#ifdef SDL_PLATFORM_WIN32
1129
if (device->serial) {
1130
// We get a garbage serial number on Windows
1131
SDL_free(device->serial);
1132
device->serial = NULL;
1133
}
1134
#endif // SDL_PLATFORM_WIN32
1135
1136
HIDAPI_SetDeviceName(device, "Steam Controller");
1137
1138
// If this is a wireless dongle, request a wireless state update
1139
if (IsDongle(device->product_id)) {
1140
unsigned char buf[65];
1141
int res;
1142
1143
buf[0] = 0;
1144
buf[1] = ID_DONGLE_GET_WIRELESS_STATE;
1145
res = SetFeatureReport(device, buf, 2);
1146
if (res < 0) {
1147
return SDL_SetError("Failed to send ID_DONGLE_GET_WIRELESS_STATE request");
1148
}
1149
1150
for (int attempt = 0; attempt < 5; ++attempt) {
1151
uint8_t data[128];
1152
1153
res = ReadSteamController(device->dev, data, sizeof(data));
1154
if (res == 0) {
1155
SDL_Delay(1);
1156
continue;
1157
}
1158
if (res < 0) {
1159
break;
1160
}
1161
1162
#ifdef DEBUG_STEAM_PROTOCOL
1163
HIDAPI_DumpPacket("Initial dongle packet: size = %d", data, res);
1164
#endif
1165
1166
if (D0G_IS_WIRELESS_CONNECT(data, res)) {
1167
ctx->connected = true;
1168
break;
1169
} else if (D0G_IS_WIRELESS_DISCONNECT(data, res)) {
1170
ctx->connected = false;
1171
break;
1172
}
1173
}
1174
1175
SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_STEAM_PAIRING_ENABLED,
1176
SDL_PairingEnabledHintChanged, ctx);
1177
} else {
1178
// Wired and BLE controllers are always connected if HIDAPI can see them
1179
ctx->connected = true;
1180
}
1181
1182
if (ctx->connected) {
1183
return HIDAPI_JoystickConnected(device, NULL);
1184
} else {
1185
// We will enumerate any attached controllers in UpdateDevice()
1186
return true;
1187
}
1188
}
1189
1190
static int HIDAPI_DriverSteam_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
1191
{
1192
return -1;
1193
}
1194
1195
static void HIDAPI_DriverSteam_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
1196
{
1197
}
1198
1199
static bool SetHomeLED(SDL_HIDAPI_Device *device, Uint8 value)
1200
{
1201
unsigned char buf[65];
1202
int nSettings = 0;
1203
1204
SDL_memset(buf, 0, 65);
1205
buf[1] = ID_SET_SETTINGS_VALUES;
1206
ADD_SETTING(SETTING_LED_USER_BRIGHTNESS, value);
1207
buf[2] = (unsigned char)(nSettings * 3);
1208
if (SetFeatureReport(device, buf, 3 + nSettings * 3) < 0) {
1209
return SDL_SetError("Couldn't write feature report");
1210
}
1211
return true;
1212
}
1213
1214
static void SDLCALL SDL_HomeLEDHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
1215
{
1216
SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)userdata;
1217
1218
if (hint && *hint) {
1219
int value;
1220
1221
if (SDL_strchr(hint, '.') != NULL) {
1222
value = (int)(100.0f * SDL_atof(hint));
1223
if (value > 255) {
1224
value = 255;
1225
}
1226
} else if (SDL_GetStringBoolean(hint, true)) {
1227
value = 100;
1228
} else {
1229
value = 0;
1230
}
1231
SetHomeLED(ctx->device, (Uint8)value);
1232
}
1233
}
1234
1235
static bool HIDAPI_DriverSteam_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
1236
{
1237
SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)device->context;
1238
float update_rate_in_hz = 0.0f;
1239
1240
SDL_AssertJoysticksLocked();
1241
1242
ctx->report_sensors = false;
1243
SDL_zero(ctx->m_assembler);
1244
SDL_zero(ctx->m_state);
1245
SDL_zero(ctx->m_last_state);
1246
1247
if (!ResetSteamController(device, false, &ctx->update_rate_in_us)) {
1248
SDL_SetError("Couldn't reset controller");
1249
return false;
1250
}
1251
if (ctx->update_rate_in_us > 0) {
1252
update_rate_in_hz = 1000000.0f / ctx->update_rate_in_us;
1253
}
1254
1255
InitializeSteamControllerPacketAssembler(&ctx->m_assembler, device->is_bluetooth);
1256
1257
// Initialize the joystick capabilities
1258
joystick->nbuttons = SDL_GAMEPAD_NUM_STEAM_BUTTONS;
1259
joystick->naxes = SDL_GAMEPAD_AXIS_COUNT;
1260
joystick->nhats = 1;
1261
1262
if (IsDongle(device->product_id)) {
1263
joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRELESS;
1264
}
1265
1266
SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, update_rate_in_hz);
1267
SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, update_rate_in_hz);
1268
1269
SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_STEAM_HOME_LED,
1270
SDL_HomeLEDHintChanged, ctx);
1271
1272
return true;
1273
}
1274
1275
static bool HIDAPI_DriverSteam_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
1276
{
1277
// You should use the full Steam Input API for rumble support
1278
return SDL_Unsupported();
1279
}
1280
1281
static bool HIDAPI_DriverSteam_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
1282
{
1283
return SDL_Unsupported();
1284
}
1285
1286
static Uint32 HIDAPI_DriverSteam_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
1287
{
1288
// You should use the full Steam Input API for extended capabilities
1289
return 0;
1290
}
1291
1292
static bool HIDAPI_DriverSteam_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
1293
{
1294
// You should use the full Steam Input API for LED support
1295
return SDL_Unsupported();
1296
}
1297
1298
static bool HIDAPI_DriverSteam_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *data, int size)
1299
{
1300
if (size == 65) {
1301
if (SetFeatureReport(device, data, size) < 0) {
1302
return SDL_SetError("Couldn't write feature report");
1303
}
1304
return true;
1305
}
1306
return SDL_Unsupported();
1307
}
1308
1309
static bool HIDAPI_DriverSteam_SetSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, bool enabled)
1310
{
1311
SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)device->context;
1312
unsigned char buf[65];
1313
int nSettings = 0;
1314
1315
SDL_memset(buf, 0, 65);
1316
buf[1] = ID_SET_SETTINGS_VALUES;
1317
if (enabled) {
1318
ADD_SETTING(SETTING_IMU_MODE, SETTING_GYRO_MODE_SEND_RAW_ACCEL | SETTING_GYRO_MODE_SEND_RAW_GYRO);
1319
} else {
1320
ADD_SETTING(SETTING_IMU_MODE, SETTING_GYRO_MODE_OFF);
1321
}
1322
buf[2] = (unsigned char)(nSettings * 3);
1323
if (SetFeatureReport(device, buf, 3 + nSettings * 3) < 0) {
1324
return SDL_SetError("Couldn't write feature report");
1325
}
1326
1327
ctx->report_sensors = enabled;
1328
1329
return true;
1330
}
1331
1332
static bool ControllerConnected(SDL_HIDAPI_Device *device, SDL_Joystick **joystick)
1333
{
1334
SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)device->context;
1335
1336
if (!HIDAPI_JoystickConnected(device, NULL)) {
1337
return false;
1338
}
1339
1340
// We'll automatically accept this controller if we're in pairing mode
1341
HIDAPI_DriverSteam_CommitPairing(ctx);
1342
1343
*joystick = SDL_GetJoystickFromID(device->joysticks[0]);
1344
ctx->connected = true;
1345
return true;
1346
}
1347
1348
static void ControllerDisconnected(SDL_HIDAPI_Device *device, SDL_Joystick **joystick)
1349
{
1350
SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)device->context;
1351
1352
if (device->joysticks) {
1353
HIDAPI_JoystickDisconnected(device, device->joysticks[0]);
1354
}
1355
ctx->connected = false;
1356
*joystick = NULL;
1357
}
1358
1359
static bool HIDAPI_DriverSteam_UpdateDevice(SDL_HIDAPI_Device *device)
1360
{
1361
SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)device->context;
1362
SDL_Joystick *joystick = NULL;
1363
1364
if (device->num_joysticks > 0) {
1365
joystick = SDL_GetJoystickFromID(device->joysticks[0]);
1366
}
1367
1368
if (ctx->pairing_time) {
1369
HIDAPI_DriverSteam_RenewPairingState(ctx);
1370
}
1371
1372
for (;;) {
1373
uint8_t data[128];
1374
int r, nPacketLength;
1375
const Uint8 *pPacket;
1376
1377
r = ReadSteamController(device->dev, data, sizeof(data));
1378
if (r == 0) {
1379
break;
1380
}
1381
if (r < 0) {
1382
// Failed to read from controller
1383
ControllerDisconnected(device, &joystick);
1384
return false;
1385
}
1386
1387
#ifdef DEBUG_STEAM_PROTOCOL
1388
HIDAPI_DumpPacket("Steam Controller packet: size = %d", data, r);
1389
#endif
1390
1391
nPacketLength = WriteSegmentToSteamControllerPacketAssembler(&ctx->m_assembler, data, r);
1392
pPacket = ctx->m_assembler.uBuffer;
1393
1394
if (nPacketLength > 0 && UpdateSteamControllerState(pPacket, nPacketLength, &ctx->m_state)) {
1395
Uint64 timestamp = SDL_GetTicksNS();
1396
1397
if (!ctx->connected) {
1398
// Maybe we missed a wireless status packet?
1399
ControllerConnected(device, &joystick);
1400
}
1401
1402
if (!joystick) {
1403
continue;
1404
}
1405
1406
if (ctx->m_state.ulButtons != ctx->m_last_state.ulButtons) {
1407
Uint8 hat = 0;
1408
1409
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SOUTH,
1410
((ctx->m_state.ulButtons & STEAM_BUTTON_SOUTH_MASK) != 0));
1411
1412
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_EAST,
1413
((ctx->m_state.ulButtons & STEAM_BUTTON_EAST_MASK) != 0));
1414
1415
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_WEST,
1416
((ctx->m_state.ulButtons & STEAM_BUTTON_WEST_MASK) != 0));
1417
1418
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_NORTH,
1419
((ctx->m_state.ulButtons & STEAM_BUTTON_NORTH_MASK) != 0));
1420
1421
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER,
1422
((ctx->m_state.ulButtons & STEAM_LEFT_BUMPER_MASK) != 0));
1423
1424
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER,
1425
((ctx->m_state.ulButtons & STEAM_RIGHT_BUMPER_MASK) != 0));
1426
1427
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK,
1428
((ctx->m_state.ulButtons & STEAM_BUTTON_MENU_MASK) != 0));
1429
1430
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START,
1431
((ctx->m_state.ulButtons & STEAM_BUTTON_ESCAPE_MASK) != 0));
1432
1433
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE,
1434
((ctx->m_state.ulButtons & STEAM_BUTTON_STEAM_MASK) != 0));
1435
1436
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK,
1437
((ctx->m_state.ulButtons & STEAM_JOYSTICK_BUTTON_MASK) != 0));
1438
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_STEAM_LEFT_PADDLE,
1439
((ctx->m_state.ulButtons & STEAM_BUTTON_BACK_LEFT_MASK) != 0));
1440
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_STEAM_RIGHT_PADDLE,
1441
((ctx->m_state.ulButtons & STEAM_BUTTON_BACK_RIGHT_MASK) != 0));
1442
1443
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK,
1444
((ctx->m_state.ulButtons & STEAM_BUTTON_RIGHTPAD_CLICKED_MASK) != 0));
1445
1446
if (ctx->m_state.ulButtons & STEAM_DPAD_UP_MASK) {
1447
hat |= SDL_HAT_UP;
1448
}
1449
if (ctx->m_state.ulButtons & STEAM_DPAD_DOWN_MASK) {
1450
hat |= SDL_HAT_DOWN;
1451
}
1452
if (ctx->m_state.ulButtons & STEAM_DPAD_LEFT_MASK) {
1453
hat |= SDL_HAT_LEFT;
1454
}
1455
if (ctx->m_state.ulButtons & STEAM_DPAD_RIGHT_MASK) {
1456
hat |= SDL_HAT_RIGHT;
1457
}
1458
SDL_SendJoystickHat(timestamp, joystick, 0, hat);
1459
}
1460
1461
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, (int)ctx->m_state.sTriggerL * 2 - 32768);
1462
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, (int)ctx->m_state.sTriggerR * 2 - 32768);
1463
1464
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, ctx->m_state.sLeftStickX);
1465
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, ~ctx->m_state.sLeftStickY);
1466
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, ctx->m_state.sRightPadX);
1467
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, ~ctx->m_state.sRightPadY);
1468
1469
if (ctx->report_sensors) {
1470
float values[3];
1471
1472
ctx->sensor_timestamp += SDL_US_TO_NS(ctx->update_rate_in_us);
1473
1474
values[0] = (ctx->m_state.sGyroX / 32768.0f) * (2000.0f * (SDL_PI_F / 180.0f));
1475
values[1] = (ctx->m_state.sGyroZ / 32768.0f) * (2000.0f * (SDL_PI_F / 180.0f));
1476
values[2] = (ctx->m_state.sGyroY / 32768.0f) * (2000.0f * (SDL_PI_F / 180.0f));
1477
SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_GYRO, ctx->sensor_timestamp, values, 3);
1478
1479
values[0] = (ctx->m_state.sAccelX / 32768.0f) * 2.0f * SDL_STANDARD_GRAVITY;
1480
values[1] = (ctx->m_state.sAccelZ / 32768.0f) * 2.0f * SDL_STANDARD_GRAVITY;
1481
values[2] = (-ctx->m_state.sAccelY / 32768.0f) * 2.0f * SDL_STANDARD_GRAVITY;
1482
SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_ACCEL, ctx->sensor_timestamp, values, 3);
1483
}
1484
1485
ctx->m_last_state = ctx->m_state;
1486
} else if (!ctx->connected && D0G_IS_WIRELESS_CONNECT(pPacket, nPacketLength)) {
1487
ControllerConnected(device, &joystick);
1488
} else if (ctx->connected && D0G_IS_WIRELESS_DISCONNECT(pPacket, nPacketLength)) {
1489
ControllerDisconnected(device, &joystick);
1490
}
1491
}
1492
return true;
1493
}
1494
1495
static void HIDAPI_DriverSteam_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
1496
{
1497
SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)device->context;
1498
1499
SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_STEAM_HOME_LED,
1500
SDL_HomeLEDHintChanged, ctx);
1501
1502
CloseSteamController(device);
1503
}
1504
1505
static void HIDAPI_DriverSteam_FreeDevice(SDL_HIDAPI_Device *device)
1506
{
1507
SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)device->context;
1508
1509
if (IsDongle(device->product_id)) {
1510
SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_STEAM_PAIRING_ENABLED,
1511
SDL_PairingEnabledHintChanged, ctx);
1512
1513
HIDAPI_DriverSteam_SetPairingState(ctx, false);
1514
}
1515
}
1516
1517
SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSteam = {
1518
SDL_HINT_JOYSTICK_HIDAPI_STEAM,
1519
true,
1520
HIDAPI_DriverSteam_RegisterHints,
1521
HIDAPI_DriverSteam_UnregisterHints,
1522
HIDAPI_DriverSteam_IsEnabled,
1523
HIDAPI_DriverSteam_IsSupportedDevice,
1524
HIDAPI_DriverSteam_InitDevice,
1525
HIDAPI_DriverSteam_GetDevicePlayerIndex,
1526
HIDAPI_DriverSteam_SetDevicePlayerIndex,
1527
HIDAPI_DriverSteam_UpdateDevice,
1528
HIDAPI_DriverSteam_OpenJoystick,
1529
HIDAPI_DriverSteam_RumbleJoystick,
1530
HIDAPI_DriverSteam_RumbleJoystickTriggers,
1531
HIDAPI_DriverSteam_GetJoystickCapabilities,
1532
HIDAPI_DriverSteam_SetJoystickLED,
1533
HIDAPI_DriverSteam_SendJoystickEffect,
1534
HIDAPI_DriverSteam_SetSensorsEnabled,
1535
HIDAPI_DriverSteam_CloseJoystick,
1536
HIDAPI_DriverSteam_FreeDevice,
1537
};
1538
1539
#endif // SDL_JOYSTICK_HIDAPI_STEAM
1540
1541
#endif // SDL_JOYSTICK_HIDAPI
1542
1543