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
9906 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
static void RotatePadShort(short *pX, short *pY, float flAngleInRad)
710
{
711
int origX = *pX, origY = *pY;
712
713
*pX = (short)(SDL_cosf(flAngleInRad) * origX - SDL_sinf(flAngleInRad) * origY);
714
*pY = (short)(SDL_sinf(flAngleInRad) * origX + SDL_cosf(flAngleInRad) * origY);
715
}
716
717
//---------------------------------------------------------------------------
718
// Format the first part of the state packet
719
//---------------------------------------------------------------------------
720
static void FormatStatePacketUntilGyro(SteamControllerStateInternal_t *pState, ValveControllerStatePacket_t *pStatePacket)
721
{
722
int nLeftPadX;
723
int nLeftPadY;
724
int nRightPadX;
725
int nRightPadY;
726
int nPadOffset;
727
728
// 15 degrees in rad
729
const float flRotationAngle = 0.261799f;
730
731
SDL_memset(pState, 0, offsetof(SteamControllerStateInternal_t, sBatteryLevel));
732
733
// pState->eControllerType = m_eControllerType;
734
pState->eControllerType = 2; // k_eControllerType_SteamController;
735
pState->unPacketNum = pStatePacket->unPacketNum;
736
737
// We have a chunk of trigger data in the packet format here, so zero it out afterwards
738
SDL_memcpy(&pState->ulButtons, &pStatePacket->ButtonTriggerData.ulButtons, 8);
739
pState->ulButtons &= ~0xFFFF000000LL;
740
741
// The firmware uses this bit to tell us what kind of data is packed into the left two axes
742
if (pStatePacket->ButtonTriggerData.ulButtons & STEAM_LEFTPAD_FINGERDOWN_MASK) {
743
// Finger-down bit not set; "left pad" is actually trackpad
744
pState->sLeftPadX = pState->sPrevLeftPad[0] = pStatePacket->sLeftPadX;
745
pState->sLeftPadY = pState->sPrevLeftPad[1] = pStatePacket->sLeftPadY;
746
747
if (pStatePacket->ButtonTriggerData.ulButtons & STEAM_LEFTPAD_AND_JOYSTICK_MASK) {
748
// The controller is interleaving both stick and pad data, both are active
749
pState->sLeftStickX = pState->sPrevLeftStick[0];
750
pState->sLeftStickY = pState->sPrevLeftStick[1];
751
} else {
752
// The stick is not active
753
pState->sPrevLeftStick[0] = 0;
754
pState->sPrevLeftStick[1] = 0;
755
}
756
} else {
757
// Finger-down bit not set; "left pad" is actually joystick
758
759
// XXX there's a firmware bug where sometimes padX is 0 and padY is a large number (actually the battery voltage)
760
// If that happens skip this packet and report last frames stick
761
/*
762
if ( m_eControllerType == k_eControllerType_SteamControllerV2 && pStatePacket->sLeftPadY > 900 ) {
763
pState->sLeftStickX = pState->sPrevLeftStick[0];
764
pState->sLeftStickY = pState->sPrevLeftStick[1];
765
} else
766
*/
767
{
768
pState->sPrevLeftStick[0] = pState->sLeftStickX = pStatePacket->sLeftPadX;
769
pState->sPrevLeftStick[1] = pState->sLeftStickY = pStatePacket->sLeftPadY;
770
}
771
/*
772
if (m_eControllerType == k_eControllerType_SteamControllerV2) {
773
UpdateV2JoystickCap(&state);
774
}
775
*/
776
777
if (pStatePacket->ButtonTriggerData.ulButtons & STEAM_LEFTPAD_AND_JOYSTICK_MASK) {
778
// The controller is interleaving both stick and pad data, both are active
779
pState->sLeftPadX = pState->sPrevLeftPad[0];
780
pState->sLeftPadY = pState->sPrevLeftPad[1];
781
} else {
782
// The trackpad is not active
783
pState->sPrevLeftPad[0] = 0;
784
pState->sPrevLeftPad[1] = 0;
785
786
// Old controllers send trackpad click for joystick button when trackpad is not active
787
if (pState->ulButtons & STEAM_BUTTON_LEFTPAD_CLICKED_MASK) {
788
pState->ulButtons &= ~STEAM_BUTTON_LEFTPAD_CLICKED_MASK;
789
pState->ulButtons |= STEAM_JOYSTICK_BUTTON_MASK;
790
}
791
}
792
}
793
794
// Fingerdown bit indicates if the packed left axis data was joystick or pad,
795
// but if we are interleaving both, the left finger is definitely on the pad.
796
if (pStatePacket->ButtonTriggerData.ulButtons & STEAM_LEFTPAD_AND_JOYSTICK_MASK) {
797
pState->ulButtons |= STEAM_LEFTPAD_FINGERDOWN_MASK;
798
}
799
800
pState->sRightPadX = pStatePacket->sRightPadX;
801
pState->sRightPadY = pStatePacket->sRightPadY;
802
803
nLeftPadX = pState->sLeftPadX;
804
nLeftPadY = pState->sLeftPadY;
805
nRightPadX = pState->sRightPadX;
806
nRightPadY = pState->sRightPadY;
807
808
RotatePad(&nLeftPadX, &nLeftPadY, -flRotationAngle);
809
RotatePad(&nRightPadX, &nRightPadY, flRotationAngle);
810
811
if (pState->ulButtons & STEAM_LEFTPAD_FINGERDOWN_MASK) {
812
nPadOffset = 1000;
813
} else {
814
nPadOffset = 0;
815
}
816
817
pState->sLeftPadX = (short)clamp(nLeftPadX + nPadOffset, SDL_MIN_SINT16, SDL_MAX_SINT16);
818
pState->sLeftPadY = (short)clamp(nLeftPadY + nPadOffset, SDL_MIN_SINT16, SDL_MAX_SINT16);
819
820
nPadOffset = 0;
821
if (pState->ulButtons & STEAM_RIGHTPAD_FINGERDOWN_MASK) {
822
nPadOffset = 1000;
823
} else {
824
nPadOffset = 0;
825
}
826
827
pState->sRightPadX = (short)clamp(nRightPadX + nPadOffset, SDL_MIN_SINT16, SDL_MAX_SINT16);
828
pState->sRightPadY = (short)clamp(nRightPadY + nPadOffset, SDL_MIN_SINT16, SDL_MAX_SINT16);
829
830
pState->sTriggerL = (unsigned short)RemapValClamped((float)((pStatePacket->ButtonTriggerData.Triggers.nLeft << 7) | pStatePacket->ButtonTriggerData.Triggers.nLeft), 0, STEAMCONTROLLER_TRIGGER_MAX_ANALOG, 0, SDL_MAX_SINT16);
831
pState->sTriggerR = (unsigned short)RemapValClamped((float)((pStatePacket->ButtonTriggerData.Triggers.nRight << 7) | pStatePacket->ButtonTriggerData.Triggers.nRight), 0, STEAMCONTROLLER_TRIGGER_MAX_ANALOG, 0, SDL_MAX_SINT16);
832
}
833
834
//---------------------------------------------------------------------------
835
// Update Steam Controller state from a BLE data packet, returns true if it parsed data
836
//---------------------------------------------------------------------------
837
static bool UpdateBLESteamControllerState(const uint8_t *pData, int nDataSize, SteamControllerStateInternal_t *pState)
838
{
839
const float flRotationAngle = 0.261799f;
840
uint32_t ucOptionDataMask;
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
int nPadOffset;
871
SDL_memcpy(&pState->sLeftPadX, pData, nLength);
872
if (pState->ulButtons & STEAM_LEFTPAD_FINGERDOWN_MASK) {
873
nPadOffset = 1000;
874
} else {
875
nPadOffset = 0;
876
}
877
878
RotatePadShort(&pState->sLeftPadX, &pState->sLeftPadY, -flRotationAngle);
879
pState->sLeftPadX = (short)clamp(pState->sLeftPadX + nPadOffset, SDL_MIN_SINT16, SDL_MAX_SINT16);
880
pState->sLeftPadY = (short)clamp(pState->sLeftPadY + nPadOffset, SDL_MIN_SINT16, SDL_MAX_SINT16);
881
pData += nLength;
882
}
883
if (ucOptionDataMask & k_EBLERightTrackpadChunk) {
884
int nLength = sizeof(pState->sRightPadX) + sizeof(pState->sRightPadY);
885
int nPadOffset = 0;
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
RotatePadShort(&pState->sRightPadX, &pState->sRightPadY, flRotationAngle);
896
pState->sRightPadX = (short)clamp(pState->sRightPadX + nPadOffset, SDL_MIN_SINT16, SDL_MAX_SINT16);
897
pState->sRightPadY = (short)clamp(pState->sRightPadY + nPadOffset, SDL_MIN_SINT16, SDL_MAX_SINT16);
898
pData += nLength;
899
}
900
if (ucOptionDataMask & k_EBLEIMUAccelChunk) {
901
int nLength = sizeof(pState->sAccelX) + sizeof(pState->sAccelY) + sizeof(pState->sAccelZ);
902
SDL_memcpy(&pState->sAccelX, pData, nLength);
903
pData += nLength;
904
}
905
if (ucOptionDataMask & k_EBLEIMUGyroChunk) {
906
int nLength = sizeof(pState->sAccelX) + sizeof(pState->sAccelY) + sizeof(pState->sAccelZ);
907
SDL_memcpy(&pState->sGyroX, pData, nLength);
908
pData += nLength;
909
}
910
if (ucOptionDataMask & k_EBLEIMUQuatChunk) {
911
int nLength = sizeof(pState->sGyroQuatW) + sizeof(pState->sGyroQuatX) + sizeof(pState->sGyroQuatY) + sizeof(pState->sGyroQuatZ);
912
SDL_memcpy(&pState->sGyroQuatW, pData, nLength);
913
pData += nLength;
914
}
915
return true;
916
}
917
918
//---------------------------------------------------------------------------
919
// Update Steam Controller state from a data packet, returns true if it parsed data
920
//---------------------------------------------------------------------------
921
static bool UpdateSteamControllerState(const uint8_t *pData, int nDataSize, SteamControllerStateInternal_t *pState)
922
{
923
ValveInReport_t *pInReport = (ValveInReport_t *)pData;
924
925
if (pInReport->header.unReportVersion != k_ValveInReportMsgVersion) {
926
if ((pData[0] & 0x0F) == k_EBLEReportState) {
927
return UpdateBLESteamControllerState(pData, nDataSize, pState);
928
}
929
return false;
930
}
931
932
if ((pInReport->header.ucType != ID_CONTROLLER_STATE) &&
933
(pInReport->header.ucType != ID_CONTROLLER_BLE_STATE)) {
934
return false;
935
}
936
937
if (pInReport->header.ucType == ID_CONTROLLER_STATE) {
938
ValveControllerStatePacket_t *pStatePacket = &pInReport->payload.controllerState;
939
940
// No new data to process; indicate that we received a state packet, but otherwise do nothing.
941
if (pState->unPacketNum == pStatePacket->unPacketNum) {
942
return true;
943
}
944
945
FormatStatePacketUntilGyro(pState, pStatePacket);
946
947
pState->sAccelX = pStatePacket->sAccelX;
948
pState->sAccelY = pStatePacket->sAccelY;
949
pState->sAccelZ = pStatePacket->sAccelZ;
950
951
pState->sGyroQuatW = pStatePacket->sGyroQuatW;
952
pState->sGyroQuatX = pStatePacket->sGyroQuatX;
953
pState->sGyroQuatY = pStatePacket->sGyroQuatY;
954
pState->sGyroQuatZ = pStatePacket->sGyroQuatZ;
955
956
pState->sGyroX = pStatePacket->sGyroX;
957
pState->sGyroY = pStatePacket->sGyroY;
958
pState->sGyroZ = pStatePacket->sGyroZ;
959
960
} else if (pInReport->header.ucType == ID_CONTROLLER_BLE_STATE) {
961
ValveControllerBLEStatePacket_t *pBLEStatePacket = &pInReport->payload.controllerBLEState;
962
ValveControllerStatePacket_t *pStatePacket = &pInReport->payload.controllerState;
963
964
// No new data to process; indicate that we received a state packet, but otherwise do nothing.
965
if (pState->unPacketNum == pStatePacket->unPacketNum) {
966
return true;
967
}
968
969
FormatStatePacketUntilGyro(pState, pStatePacket);
970
971
switch (pBLEStatePacket->ucGyroDataType) {
972
case 1:
973
pState->sGyroQuatW = ((float)pBLEStatePacket->sGyro[0]);
974
pState->sGyroQuatX = ((float)pBLEStatePacket->sGyro[1]);
975
pState->sGyroQuatY = ((float)pBLEStatePacket->sGyro[2]);
976
pState->sGyroQuatZ = ((float)pBLEStatePacket->sGyro[3]);
977
break;
978
979
case 2:
980
pState->sAccelX = pBLEStatePacket->sGyro[0];
981
pState->sAccelY = pBLEStatePacket->sGyro[1];
982
pState->sAccelZ = pBLEStatePacket->sGyro[2];
983
break;
984
985
case 3:
986
pState->sGyroX = pBLEStatePacket->sGyro[0];
987
pState->sGyroY = pBLEStatePacket->sGyro[1];
988
pState->sGyroZ = pBLEStatePacket->sGyro[2];
989
break;
990
991
default:
992
break;
993
}
994
}
995
996
return true;
997
}
998
999
/*****************************************************************************************************/
1000
1001
typedef struct
1002
{
1003
SDL_HIDAPI_Device *device;
1004
bool connected;
1005
bool report_sensors;
1006
uint32_t update_rate_in_us;
1007
Uint64 sensor_timestamp;
1008
Uint64 pairing_time;
1009
1010
SteamControllerPacketAssembler m_assembler;
1011
SteamControllerStateInternal_t m_state;
1012
SteamControllerStateInternal_t m_last_state;
1013
} SDL_DriverSteam_Context;
1014
1015
static bool IsDongle(Uint16 product_id)
1016
{
1017
return (product_id == USB_PRODUCT_VALVE_STEAM_CONTROLLER_DONGLE);
1018
}
1019
1020
static void HIDAPI_DriverSteam_RegisterHints(SDL_HintCallback callback, void *userdata)
1021
{
1022
SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_STEAM, callback, userdata);
1023
}
1024
1025
static void HIDAPI_DriverSteam_UnregisterHints(SDL_HintCallback callback, void *userdata)
1026
{
1027
SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_STEAM, callback, userdata);
1028
}
1029
1030
static bool HIDAPI_DriverSteam_IsEnabled(void)
1031
{
1032
return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_STEAM, SDL_HINT_JOYSTICK_HIDAPI_STEAM_DEFAULT);
1033
}
1034
1035
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)
1036
{
1037
if (!SDL_IsJoystickSteamController(vendor_id, product_id)) {
1038
return false;
1039
}
1040
1041
if (device->is_bluetooth) {
1042
return true;
1043
}
1044
1045
if (IsDongle(product_id)) {
1046
if (interface_number >= 1 && interface_number <= 4) {
1047
// This is one of the wireless controller interfaces
1048
return true;
1049
}
1050
} else {
1051
if (interface_number == 2) {
1052
// This is the controller interface (not mouse or keyboard)
1053
return true;
1054
}
1055
}
1056
return false;
1057
}
1058
1059
static void HIDAPI_DriverSteam_SetPairingState(SDL_DriverSteam_Context *ctx, bool enabled)
1060
{
1061
// Only have one dongle in pairing mode at a time
1062
static SDL_DriverSteam_Context *s_PairingContext = NULL;
1063
1064
if (enabled && s_PairingContext != NULL) {
1065
return;
1066
}
1067
1068
if (!enabled && s_PairingContext != ctx) {
1069
return;
1070
}
1071
1072
if (ctx->connected) {
1073
return;
1074
}
1075
1076
SetPairingState(ctx->device, enabled);
1077
1078
if (enabled) {
1079
ctx->pairing_time = SDL_GetTicks();
1080
s_PairingContext = ctx;
1081
} else {
1082
ctx->pairing_time = 0;
1083
s_PairingContext = NULL;
1084
}
1085
}
1086
1087
static void HIDAPI_DriverSteam_RenewPairingState(SDL_DriverSteam_Context *ctx)
1088
{
1089
Uint64 now = SDL_GetTicks();
1090
1091
if (now >= ctx->pairing_time + PAIRING_STATE_DURATION_SECONDS * 1000) {
1092
SetPairingState(ctx->device, true);
1093
ctx->pairing_time = now;
1094
}
1095
}
1096
1097
static void HIDAPI_DriverSteam_CommitPairing(SDL_DriverSteam_Context *ctx)
1098
{
1099
CommitPairing(ctx->device);
1100
}
1101
1102
static void SDLCALL SDL_PairingEnabledHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
1103
{
1104
SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)userdata;
1105
bool enabled = SDL_GetStringBoolean(hint, false);
1106
1107
HIDAPI_DriverSteam_SetPairingState(ctx, enabled);
1108
}
1109
1110
static bool HIDAPI_DriverSteam_InitDevice(SDL_HIDAPI_Device *device)
1111
{
1112
SDL_DriverSteam_Context *ctx;
1113
1114
ctx = (SDL_DriverSteam_Context *)SDL_calloc(1, sizeof(*ctx));
1115
if (!ctx) {
1116
return false;
1117
}
1118
ctx->device = device;
1119
device->context = ctx;
1120
1121
#ifdef SDL_PLATFORM_WIN32
1122
if (device->serial) {
1123
// We get a garbage serial number on Windows
1124
SDL_free(device->serial);
1125
device->serial = NULL;
1126
}
1127
#endif // SDL_PLATFORM_WIN32
1128
1129
HIDAPI_SetDeviceName(device, "Steam Controller");
1130
1131
// If this is a wireless dongle, request a wireless state update
1132
if (IsDongle(device->product_id)) {
1133
unsigned char buf[65];
1134
int res;
1135
1136
buf[0] = 0;
1137
buf[1] = ID_DONGLE_GET_WIRELESS_STATE;
1138
res = SetFeatureReport(device, buf, 2);
1139
if (res < 0) {
1140
return SDL_SetError("Failed to send ID_DONGLE_GET_WIRELESS_STATE request");
1141
}
1142
1143
for (int attempt = 0; attempt < 5; ++attempt) {
1144
uint8_t data[128];
1145
1146
res = ReadSteamController(device->dev, data, sizeof(data));
1147
if (res == 0) {
1148
SDL_Delay(1);
1149
continue;
1150
}
1151
if (res < 0) {
1152
break;
1153
}
1154
1155
#ifdef DEBUG_STEAM_PROTOCOL
1156
HIDAPI_DumpPacket("Initial dongle packet: size = %d", data, res);
1157
#endif
1158
1159
if (D0G_IS_WIRELESS_CONNECT(data, res)) {
1160
ctx->connected = true;
1161
break;
1162
} else if (D0G_IS_WIRELESS_DISCONNECT(data, res)) {
1163
ctx->connected = false;
1164
break;
1165
}
1166
}
1167
1168
SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_STEAM_PAIRING_ENABLED,
1169
SDL_PairingEnabledHintChanged, ctx);
1170
} else {
1171
// Wired and BLE controllers are always connected if HIDAPI can see them
1172
ctx->connected = true;
1173
}
1174
1175
if (ctx->connected) {
1176
return HIDAPI_JoystickConnected(device, NULL);
1177
} else {
1178
// We will enumerate any attached controllers in UpdateDevice()
1179
return true;
1180
}
1181
}
1182
1183
static int HIDAPI_DriverSteam_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
1184
{
1185
return -1;
1186
}
1187
1188
static void HIDAPI_DriverSteam_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
1189
{
1190
}
1191
1192
static bool SetHomeLED(SDL_HIDAPI_Device *device, Uint8 value)
1193
{
1194
unsigned char buf[65];
1195
int nSettings = 0;
1196
1197
SDL_memset(buf, 0, 65);
1198
buf[1] = ID_SET_SETTINGS_VALUES;
1199
ADD_SETTING(SETTING_LED_USER_BRIGHTNESS, value);
1200
buf[2] = (unsigned char)(nSettings * 3);
1201
if (SetFeatureReport(device, buf, 3 + nSettings * 3) < 0) {
1202
return SDL_SetError("Couldn't write feature report");
1203
}
1204
return true;
1205
}
1206
1207
static void SDLCALL SDL_HomeLEDHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
1208
{
1209
SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)userdata;
1210
1211
if (hint && *hint) {
1212
int value;
1213
1214
if (SDL_strchr(hint, '.') != NULL) {
1215
value = (int)(100.0f * SDL_atof(hint));
1216
if (value > 255) {
1217
value = 255;
1218
}
1219
} else if (SDL_GetStringBoolean(hint, true)) {
1220
value = 100;
1221
} else {
1222
value = 0;
1223
}
1224
SetHomeLED(ctx->device, (Uint8)value);
1225
}
1226
}
1227
1228
static bool HIDAPI_DriverSteam_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
1229
{
1230
SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)device->context;
1231
float update_rate_in_hz = 0.0f;
1232
1233
SDL_AssertJoysticksLocked();
1234
1235
ctx->report_sensors = false;
1236
SDL_zero(ctx->m_assembler);
1237
SDL_zero(ctx->m_state);
1238
SDL_zero(ctx->m_last_state);
1239
1240
if (!ResetSteamController(device, false, &ctx->update_rate_in_us)) {
1241
SDL_SetError("Couldn't reset controller");
1242
return false;
1243
}
1244
if (ctx->update_rate_in_us > 0) {
1245
update_rate_in_hz = 1000000.0f / ctx->update_rate_in_us;
1246
}
1247
1248
InitializeSteamControllerPacketAssembler(&ctx->m_assembler, device->is_bluetooth);
1249
1250
// Initialize the joystick capabilities
1251
joystick->nbuttons = SDL_GAMEPAD_NUM_STEAM_BUTTONS;
1252
joystick->naxes = SDL_GAMEPAD_AXIS_COUNT;
1253
joystick->nhats = 1;
1254
1255
if (IsDongle(device->product_id)) {
1256
joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRELESS;
1257
}
1258
1259
SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, update_rate_in_hz);
1260
SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, update_rate_in_hz);
1261
1262
SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_STEAM_HOME_LED,
1263
SDL_HomeLEDHintChanged, ctx);
1264
1265
return true;
1266
}
1267
1268
static bool HIDAPI_DriverSteam_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
1269
{
1270
// You should use the full Steam Input API for rumble support
1271
return SDL_Unsupported();
1272
}
1273
1274
static bool HIDAPI_DriverSteam_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
1275
{
1276
return SDL_Unsupported();
1277
}
1278
1279
static Uint32 HIDAPI_DriverSteam_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
1280
{
1281
// You should use the full Steam Input API for extended capabilities
1282
return 0;
1283
}
1284
1285
static bool HIDAPI_DriverSteam_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
1286
{
1287
// You should use the full Steam Input API for LED support
1288
return SDL_Unsupported();
1289
}
1290
1291
static bool HIDAPI_DriverSteam_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *data, int size)
1292
{
1293
if (size == 65) {
1294
if (SetFeatureReport(device, data, size) < 0) {
1295
return SDL_SetError("Couldn't write feature report");
1296
}
1297
return true;
1298
}
1299
return SDL_Unsupported();
1300
}
1301
1302
static bool HIDAPI_DriverSteam_SetSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, bool enabled)
1303
{
1304
SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)device->context;
1305
unsigned char buf[65];
1306
int nSettings = 0;
1307
1308
SDL_memset(buf, 0, 65);
1309
buf[1] = ID_SET_SETTINGS_VALUES;
1310
if (enabled) {
1311
ADD_SETTING(SETTING_IMU_MODE, SETTING_GYRO_MODE_SEND_RAW_ACCEL | SETTING_GYRO_MODE_SEND_RAW_GYRO);
1312
} else {
1313
ADD_SETTING(SETTING_IMU_MODE, SETTING_GYRO_MODE_OFF);
1314
}
1315
buf[2] = (unsigned char)(nSettings * 3);
1316
if (SetFeatureReport(device, buf, 3 + nSettings * 3) < 0) {
1317
return SDL_SetError("Couldn't write feature report");
1318
}
1319
1320
ctx->report_sensors = enabled;
1321
1322
return true;
1323
}
1324
1325
static bool ControllerConnected(SDL_HIDAPI_Device *device, SDL_Joystick **joystick)
1326
{
1327
SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)device->context;
1328
1329
if (!HIDAPI_JoystickConnected(device, NULL)) {
1330
return false;
1331
}
1332
1333
// We'll automatically accept this controller if we're in pairing mode
1334
HIDAPI_DriverSteam_CommitPairing(ctx);
1335
1336
*joystick = SDL_GetJoystickFromID(device->joysticks[0]);
1337
ctx->connected = true;
1338
return true;
1339
}
1340
1341
static void ControllerDisconnected(SDL_HIDAPI_Device *device, SDL_Joystick **joystick)
1342
{
1343
SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)device->context;
1344
1345
if (device->joysticks) {
1346
HIDAPI_JoystickDisconnected(device, device->joysticks[0]);
1347
}
1348
ctx->connected = false;
1349
*joystick = NULL;
1350
}
1351
1352
static bool HIDAPI_DriverSteam_UpdateDevice(SDL_HIDAPI_Device *device)
1353
{
1354
SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)device->context;
1355
SDL_Joystick *joystick = NULL;
1356
1357
if (device->num_joysticks > 0) {
1358
joystick = SDL_GetJoystickFromID(device->joysticks[0]);
1359
}
1360
1361
if (ctx->pairing_time) {
1362
HIDAPI_DriverSteam_RenewPairingState(ctx);
1363
}
1364
1365
for (;;) {
1366
uint8_t data[128];
1367
int r, nPacketLength;
1368
const Uint8 *pPacket;
1369
1370
r = ReadSteamController(device->dev, data, sizeof(data));
1371
if (r == 0) {
1372
break;
1373
}
1374
if (r < 0) {
1375
// Failed to read from controller
1376
ControllerDisconnected(device, &joystick);
1377
return false;
1378
}
1379
1380
#ifdef DEBUG_STEAM_PROTOCOL
1381
HIDAPI_DumpPacket("Steam Controller packet: size = %d", data, r);
1382
#endif
1383
1384
nPacketLength = WriteSegmentToSteamControllerPacketAssembler(&ctx->m_assembler, data, r);
1385
pPacket = ctx->m_assembler.uBuffer;
1386
1387
if (nPacketLength > 0 && UpdateSteamControllerState(pPacket, nPacketLength, &ctx->m_state)) {
1388
Uint64 timestamp = SDL_GetTicksNS();
1389
1390
if (!ctx->connected) {
1391
// Maybe we missed a wireless status packet?
1392
ControllerConnected(device, &joystick);
1393
}
1394
1395
if (!joystick) {
1396
continue;
1397
}
1398
1399
if (ctx->m_state.ulButtons != ctx->m_last_state.ulButtons) {
1400
Uint8 hat = 0;
1401
1402
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SOUTH,
1403
((ctx->m_state.ulButtons & STEAM_BUTTON_SOUTH_MASK) != 0));
1404
1405
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_EAST,
1406
((ctx->m_state.ulButtons & STEAM_BUTTON_EAST_MASK) != 0));
1407
1408
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_WEST,
1409
((ctx->m_state.ulButtons & STEAM_BUTTON_WEST_MASK) != 0));
1410
1411
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_NORTH,
1412
((ctx->m_state.ulButtons & STEAM_BUTTON_NORTH_MASK) != 0));
1413
1414
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER,
1415
((ctx->m_state.ulButtons & STEAM_LEFT_BUMPER_MASK) != 0));
1416
1417
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER,
1418
((ctx->m_state.ulButtons & STEAM_RIGHT_BUMPER_MASK) != 0));
1419
1420
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK,
1421
((ctx->m_state.ulButtons & STEAM_BUTTON_MENU_MASK) != 0));
1422
1423
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START,
1424
((ctx->m_state.ulButtons & STEAM_BUTTON_ESCAPE_MASK) != 0));
1425
1426
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE,
1427
((ctx->m_state.ulButtons & STEAM_BUTTON_STEAM_MASK) != 0));
1428
1429
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK,
1430
((ctx->m_state.ulButtons & STEAM_JOYSTICK_BUTTON_MASK) != 0));
1431
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_STEAM_LEFT_PADDLE,
1432
((ctx->m_state.ulButtons & STEAM_BUTTON_BACK_LEFT_MASK) != 0));
1433
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_STEAM_RIGHT_PADDLE,
1434
((ctx->m_state.ulButtons & STEAM_BUTTON_BACK_RIGHT_MASK) != 0));
1435
1436
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK,
1437
((ctx->m_state.ulButtons & STEAM_BUTTON_RIGHTPAD_CLICKED_MASK) != 0));
1438
1439
if (ctx->m_state.ulButtons & STEAM_DPAD_UP_MASK) {
1440
hat |= SDL_HAT_UP;
1441
}
1442
if (ctx->m_state.ulButtons & STEAM_DPAD_DOWN_MASK) {
1443
hat |= SDL_HAT_DOWN;
1444
}
1445
if (ctx->m_state.ulButtons & STEAM_DPAD_LEFT_MASK) {
1446
hat |= SDL_HAT_LEFT;
1447
}
1448
if (ctx->m_state.ulButtons & STEAM_DPAD_RIGHT_MASK) {
1449
hat |= SDL_HAT_RIGHT;
1450
}
1451
SDL_SendJoystickHat(timestamp, joystick, 0, hat);
1452
}
1453
1454
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, (int)ctx->m_state.sTriggerL * 2 - 32768);
1455
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, (int)ctx->m_state.sTriggerR * 2 - 32768);
1456
1457
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, ctx->m_state.sLeftStickX);
1458
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, ~ctx->m_state.sLeftStickY);
1459
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, ctx->m_state.sRightPadX);
1460
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, ~ctx->m_state.sRightPadY);
1461
1462
if (ctx->report_sensors) {
1463
float values[3];
1464
1465
ctx->sensor_timestamp += SDL_US_TO_NS(ctx->update_rate_in_us);
1466
1467
values[0] = (ctx->m_state.sGyroX / 32768.0f) * (2000.0f * (SDL_PI_F / 180.0f));
1468
values[1] = (ctx->m_state.sGyroZ / 32768.0f) * (2000.0f * (SDL_PI_F / 180.0f));
1469
values[2] = (ctx->m_state.sGyroY / 32768.0f) * (2000.0f * (SDL_PI_F / 180.0f));
1470
SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_GYRO, ctx->sensor_timestamp, values, 3);
1471
1472
values[0] = (ctx->m_state.sAccelX / 32768.0f) * 2.0f * SDL_STANDARD_GRAVITY;
1473
values[1] = (ctx->m_state.sAccelZ / 32768.0f) * 2.0f * SDL_STANDARD_GRAVITY;
1474
values[2] = (-ctx->m_state.sAccelY / 32768.0f) * 2.0f * SDL_STANDARD_GRAVITY;
1475
SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_ACCEL, ctx->sensor_timestamp, values, 3);
1476
}
1477
1478
ctx->m_last_state = ctx->m_state;
1479
} else if (!ctx->connected && D0G_IS_WIRELESS_CONNECT(pPacket, nPacketLength)) {
1480
ControllerConnected(device, &joystick);
1481
} else if (ctx->connected && D0G_IS_WIRELESS_DISCONNECT(pPacket, nPacketLength)) {
1482
ControllerDisconnected(device, &joystick);
1483
}
1484
}
1485
return true;
1486
}
1487
1488
static void HIDAPI_DriverSteam_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
1489
{
1490
SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)device->context;
1491
1492
SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_STEAM_HOME_LED,
1493
SDL_HomeLEDHintChanged, ctx);
1494
1495
CloseSteamController(device);
1496
}
1497
1498
static void HIDAPI_DriverSteam_FreeDevice(SDL_HIDAPI_Device *device)
1499
{
1500
SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)device->context;
1501
1502
if (IsDongle(device->product_id)) {
1503
SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_STEAM_PAIRING_ENABLED,
1504
SDL_PairingEnabledHintChanged, ctx);
1505
1506
HIDAPI_DriverSteam_SetPairingState(ctx, false);
1507
}
1508
}
1509
1510
SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSteam = {
1511
SDL_HINT_JOYSTICK_HIDAPI_STEAM,
1512
true,
1513
HIDAPI_DriverSteam_RegisterHints,
1514
HIDAPI_DriverSteam_UnregisterHints,
1515
HIDAPI_DriverSteam_IsEnabled,
1516
HIDAPI_DriverSteam_IsSupportedDevice,
1517
HIDAPI_DriverSteam_InitDevice,
1518
HIDAPI_DriverSteam_GetDevicePlayerIndex,
1519
HIDAPI_DriverSteam_SetDevicePlayerIndex,
1520
HIDAPI_DriverSteam_UpdateDevice,
1521
HIDAPI_DriverSteam_OpenJoystick,
1522
HIDAPI_DriverSteam_RumbleJoystick,
1523
HIDAPI_DriverSteam_RumbleJoystickTriggers,
1524
HIDAPI_DriverSteam_GetJoystickCapabilities,
1525
HIDAPI_DriverSteam_SetJoystickLED,
1526
HIDAPI_DriverSteam_SendJoystickEffect,
1527
HIDAPI_DriverSteam_SetSensorsEnabled,
1528
HIDAPI_DriverSteam_CloseJoystick,
1529
HIDAPI_DriverSteam_FreeDevice,
1530
};
1531
1532
#endif // SDL_JOYSTICK_HIDAPI_STEAM
1533
1534
#endif // SDL_JOYSTICK_HIDAPI
1535
1536