Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/sdl/joystick/hidapi/SDL_hidapi_switch.c
21520 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
/* This driver supports the Nintendo Switch Pro controller.
22
Code and logic contributed by Valve Corporation under the SDL zlib license.
23
*/
24
#include "SDL_internal.h"
25
26
#ifdef SDL_JOYSTICK_HIDAPI
27
28
#include "../../SDL_hints_c.h"
29
#include "../SDL_sysjoystick.h"
30
#include "SDL_hidapijoystick_c.h"
31
#include "SDL_hidapi_rumble.h"
32
#include "SDL_hidapi_nintendo.h"
33
34
#ifdef SDL_JOYSTICK_HIDAPI_SWITCH
35
36
// Define this if you want to log all packets from the controller
37
// #define DEBUG_SWITCH_PROTOCOL
38
39
// Define this to get log output for rumble logic
40
// #define DEBUG_RUMBLE
41
42
/* The initialization sequence doesn't appear to work correctly on Windows unless
43
the reads and writes are on the same thread.
44
45
... and now I can't reproduce this, so I'm leaving it in, but disabled for now.
46
*/
47
// #define SWITCH_SYNCHRONOUS_WRITES
48
49
/* How often you can write rumble commands to the controller.
50
If you send commands more frequently than this, you can turn off the controller
51
in Bluetooth mode, or the motors can miss the command in USB mode.
52
*/
53
#define RUMBLE_WRITE_FREQUENCY_MS 30
54
55
// How often you have to refresh a long duration rumble to keep the motors running
56
#define RUMBLE_REFRESH_FREQUENCY_MS 50
57
58
#define SWITCH_GYRO_SCALE 14.2842f
59
#define SWITCH_ACCEL_SCALE 4096.f
60
61
#define SWITCH_GYRO_SCALE_MULT 936.0f
62
#define SWITCH_ACCEL_SCALE_MULT 4.0f
63
64
enum
65
{
66
SDL_GAMEPAD_BUTTON_SWITCH_SHARE = 11,
67
SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE1,
68
SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE1,
69
SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE2,
70
SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE2,
71
SDL_GAMEPAD_NUM_SWITCH_BUTTONS,
72
};
73
74
typedef enum
75
{
76
k_eSwitchInputReportIDs_SubcommandReply = 0x21,
77
k_eSwitchInputReportIDs_FullControllerState = 0x30,
78
k_eSwitchInputReportIDs_FullControllerAndMcuState = 0x31,
79
k_eSwitchInputReportIDs_SimpleControllerState = 0x3F,
80
k_eSwitchInputReportIDs_CommandAck = 0x81,
81
} ESwitchInputReportIDs;
82
83
typedef enum
84
{
85
k_eSwitchOutputReportIDs_RumbleAndSubcommand = 0x01,
86
k_eSwitchOutputReportIDs_Rumble = 0x10,
87
k_eSwitchOutputReportIDs_Proprietary = 0x80,
88
} ESwitchOutputReportIDs;
89
90
typedef enum
91
{
92
k_eSwitchSubcommandIDs_BluetoothManualPair = 0x01,
93
k_eSwitchSubcommandIDs_RequestDeviceInfo = 0x02,
94
k_eSwitchSubcommandIDs_SetInputReportMode = 0x03,
95
k_eSwitchSubcommandIDs_SetHCIState = 0x06,
96
k_eSwitchSubcommandIDs_SPIFlashRead = 0x10,
97
k_eSwitchSubcommandIDs_SetPlayerLights = 0x30,
98
k_eSwitchSubcommandIDs_SetHomeLight = 0x38,
99
k_eSwitchSubcommandIDs_EnableIMU = 0x40,
100
k_eSwitchSubcommandIDs_SetIMUSensitivity = 0x41,
101
k_eSwitchSubcommandIDs_EnableVibration = 0x48,
102
} ESwitchSubcommandIDs;
103
104
typedef enum
105
{
106
k_eSwitchProprietaryCommandIDs_Status = 0x01,
107
k_eSwitchProprietaryCommandIDs_Handshake = 0x02,
108
k_eSwitchProprietaryCommandIDs_HighSpeed = 0x03,
109
k_eSwitchProprietaryCommandIDs_ForceUSB = 0x04,
110
k_eSwitchProprietaryCommandIDs_ClearUSB = 0x05,
111
k_eSwitchProprietaryCommandIDs_ResetMCU = 0x06,
112
} ESwitchProprietaryCommandIDs;
113
114
#define k_unSwitchOutputPacketDataLength 49
115
#define k_unSwitchMaxOutputPacketLength 64
116
#define k_unSwitchBluetoothPacketLength k_unSwitchOutputPacketDataLength
117
#define k_unSwitchUSBPacketLength k_unSwitchMaxOutputPacketLength
118
119
#define k_unSPIStickFactoryCalibrationStartOffset 0x603D
120
#define k_unSPIStickFactoryCalibrationEndOffset 0x604E
121
#define k_unSPIStickFactoryCalibrationLength (k_unSPIStickFactoryCalibrationEndOffset - k_unSPIStickFactoryCalibrationStartOffset + 1)
122
123
#define k_unSPIStickUserCalibrationStartOffset 0x8010
124
#define k_unSPIStickUserCalibrationEndOffset 0x8025
125
#define k_unSPIStickUserCalibrationLength (k_unSPIStickUserCalibrationEndOffset - k_unSPIStickUserCalibrationStartOffset + 1)
126
127
#define k_unSPIIMUScaleStartOffset 0x6020
128
#define k_unSPIIMUScaleEndOffset 0x6037
129
#define k_unSPIIMUScaleLength (k_unSPIIMUScaleEndOffset - k_unSPIIMUScaleStartOffset + 1)
130
131
#define k_unSPIIMUUserScaleStartOffset 0x8026
132
#define k_unSPIIMUUserScaleEndOffset 0x8039
133
#define k_unSPIIMUUserScaleLength (k_unSPIIMUUserScaleEndOffset - k_unSPIIMUUserScaleStartOffset + 1)
134
135
#pragma pack(1)
136
typedef struct
137
{
138
Uint8 rgucButtons[2];
139
Uint8 ucStickHat;
140
Uint8 rgucJoystickLeft[2];
141
Uint8 rgucJoystickRight[2];
142
} SwitchInputOnlyControllerStatePacket_t;
143
144
typedef struct
145
{
146
Uint8 rgucButtons[2];
147
Uint8 ucStickHat;
148
Sint16 sJoystickLeft[2];
149
Sint16 sJoystickRight[2];
150
} SwitchSimpleStatePacket_t;
151
152
typedef struct
153
{
154
Uint8 ucCounter;
155
Uint8 ucBatteryAndConnection;
156
Uint8 rgucButtons[3];
157
Uint8 rgucJoystickLeft[3];
158
Uint8 rgucJoystickRight[3];
159
Uint8 ucVibrationCode;
160
} SwitchControllerStatePacket_t;
161
162
typedef struct
163
{
164
Sint16 sAccelX;
165
Sint16 sAccelY;
166
Sint16 sAccelZ;
167
168
Sint16 sGyroX;
169
Sint16 sGyroY;
170
Sint16 sGyroZ;
171
} SwitchControllerIMUState_t;
172
173
typedef struct
174
{
175
SwitchControllerStatePacket_t controllerState;
176
SwitchControllerIMUState_t imuState[3];
177
} SwitchStatePacket_t;
178
179
typedef struct
180
{
181
Uint32 unAddress;
182
Uint8 ucLength;
183
} SwitchSPIOpData_t;
184
185
typedef struct
186
{
187
SwitchControllerStatePacket_t m_controllerState;
188
189
Uint8 ucSubcommandAck;
190
Uint8 ucSubcommandID;
191
192
#define k_unSubcommandDataBytes 35
193
union
194
{
195
Uint8 rgucSubcommandData[k_unSubcommandDataBytes];
196
197
struct
198
{
199
SwitchSPIOpData_t opData;
200
Uint8 rgucReadData[k_unSubcommandDataBytes - sizeof(SwitchSPIOpData_t)];
201
} spiReadData;
202
203
struct
204
{
205
Uint8 rgucFirmwareVersion[2];
206
Uint8 ucDeviceType;
207
Uint8 ucFiller1;
208
Uint8 rgucMACAddress[6];
209
Uint8 ucFiller2;
210
Uint8 ucColorLocation;
211
} deviceInfo;
212
213
struct
214
{
215
SwitchSPIOpData_t opData;
216
Uint8 rgucLeftCalibration[9];
217
Uint8 rgucRightCalibration[9];
218
} stickFactoryCalibration;
219
220
struct
221
{
222
SwitchSPIOpData_t opData;
223
Uint8 rgucLeftMagic[2];
224
Uint8 rgucLeftCalibration[9];
225
Uint8 rgucRightMagic[2];
226
Uint8 rgucRightCalibration[9];
227
} stickUserCalibration;
228
};
229
} SwitchSubcommandInputPacket_t;
230
231
typedef struct
232
{
233
Uint8 ucPacketType;
234
Uint8 ucCommandID;
235
Uint8 ucFiller;
236
237
Uint8 ucDeviceType;
238
Uint8 rgucMACAddress[6];
239
} SwitchProprietaryStatusPacket_t;
240
241
typedef struct
242
{
243
Uint8 rgucData[4];
244
} SwitchRumbleData_t;
245
246
typedef struct
247
{
248
Uint8 ucPacketType;
249
Uint8 ucPacketNumber;
250
SwitchRumbleData_t rumbleData[2];
251
} SwitchCommonOutputPacket_t;
252
253
typedef struct
254
{
255
SwitchCommonOutputPacket_t commonData;
256
257
Uint8 ucSubcommandID;
258
Uint8 rgucSubcommandData[k_unSwitchOutputPacketDataLength - sizeof(SwitchCommonOutputPacket_t) - 1];
259
} SwitchSubcommandOutputPacket_t;
260
261
typedef struct
262
{
263
Uint8 ucPacketType;
264
Uint8 ucProprietaryID;
265
266
Uint8 rgucProprietaryData[k_unSwitchOutputPacketDataLength - 1 - 1];
267
} SwitchProprietaryOutputPacket_t;
268
#pragma pack()
269
270
/* Enhanced report hint mode:
271
* "0": enhanced features are never used
272
* "1": enhanced features are always used
273
* "auto": enhanced features are advertised to the application, but SDL doesn't touch the controller state unless the application explicitly requests it.
274
*/
275
typedef enum
276
{
277
SWITCH_ENHANCED_REPORT_HINT_OFF,
278
SWITCH_ENHANCED_REPORT_HINT_ON,
279
SWITCH_ENHANCED_REPORT_HINT_AUTO
280
} HIDAPI_Switch_EnhancedReportHint;
281
282
typedef struct
283
{
284
SDL_HIDAPI_Device *device;
285
SDL_Joystick *joystick;
286
bool m_bInputOnly;
287
bool m_bUseButtonLabels;
288
bool m_bPlayerLights;
289
int m_nPlayerIndex;
290
bool m_bSyncWrite;
291
int m_nMaxWriteAttempts;
292
ESwitchDeviceInfoControllerType m_eControllerType;
293
Uint8 m_nInitialInputMode;
294
Uint8 m_nCurrentInputMode;
295
Uint8 m_rgucMACAddress[6];
296
Uint8 m_nCommandNumber;
297
HIDAPI_Switch_EnhancedReportHint m_eEnhancedReportHint;
298
bool m_bEnhancedMode;
299
bool m_bEnhancedModeAvailable;
300
SwitchCommonOutputPacket_t m_RumblePacket;
301
Uint8 m_rgucReadBuffer[k_unSwitchMaxOutputPacketLength];
302
bool m_bRumbleActive;
303
Uint64 m_ulRumbleSent;
304
bool m_bRumblePending;
305
bool m_bRumbleZeroPending;
306
Uint32 m_unRumblePending;
307
bool m_bSensorsSupported;
308
bool m_bReportSensors;
309
bool m_bHasSensorData;
310
Uint64 m_ulLastInput;
311
Uint64 m_ulLastIMUReset;
312
Uint64 m_ulIMUSampleTimestampNS;
313
Uint32 m_unIMUSamples;
314
Uint64 m_ulIMUUpdateIntervalNS;
315
Uint64 m_ulTimestampNS;
316
bool m_bVerticalMode;
317
318
SwitchInputOnlyControllerStatePacket_t m_lastInputOnlyState;
319
SwitchSimpleStatePacket_t m_lastSimpleState;
320
SwitchStatePacket_t m_lastFullState;
321
322
struct StickCalibrationData
323
{
324
struct
325
{
326
Sint16 sCenter;
327
Sint16 sMin;
328
Sint16 sMax;
329
} axis[2];
330
} m_StickCalData[2];
331
332
struct StickExtents
333
{
334
struct
335
{
336
Sint16 sMin;
337
Sint16 sMax;
338
} axis[2];
339
} m_StickExtents[2], m_SimpleStickExtents[2];
340
341
struct IMUScaleData
342
{
343
float fAccelScaleX;
344
float fAccelScaleY;
345
float fAccelScaleZ;
346
347
float fGyroScaleX;
348
float fGyroScaleY;
349
float fGyroScaleZ;
350
} m_IMUScaleData;
351
} SDL_DriverSwitch_Context;
352
353
static int ReadInput(SDL_DriverSwitch_Context *ctx)
354
{
355
int result;
356
357
// Make sure we don't try to read at the same time a write is happening
358
if (SDL_GetAtomicInt(&ctx->device->rumble_pending) > 0) {
359
return 0;
360
}
361
362
result = SDL_hid_read_timeout(ctx->device->dev, ctx->m_rgucReadBuffer, sizeof(ctx->m_rgucReadBuffer), 0);
363
364
// See if we can guess the initial input mode
365
if (result > 0 && !ctx->m_bInputOnly && !ctx->m_nInitialInputMode) {
366
switch (ctx->m_rgucReadBuffer[0]) {
367
case k_eSwitchInputReportIDs_FullControllerState:
368
case k_eSwitchInputReportIDs_FullControllerAndMcuState:
369
case k_eSwitchInputReportIDs_SimpleControllerState:
370
ctx->m_nInitialInputMode = ctx->m_rgucReadBuffer[0];
371
break;
372
default:
373
break;
374
}
375
}
376
return result;
377
}
378
379
static int WriteOutput(SDL_DriverSwitch_Context *ctx, const Uint8 *data, int size)
380
{
381
#ifdef SWITCH_SYNCHRONOUS_WRITES
382
return SDL_hid_write(ctx->device->dev, data, size);
383
#else
384
// Use the rumble thread for general asynchronous writes
385
if (!SDL_HIDAPI_LockRumble()) {
386
return -1;
387
}
388
return SDL_HIDAPI_SendRumbleAndUnlock(ctx->device, data, size);
389
#endif // SWITCH_SYNCHRONOUS_WRITES
390
}
391
392
static SwitchSubcommandInputPacket_t *ReadSubcommandReply(SDL_DriverSwitch_Context *ctx, ESwitchSubcommandIDs expectedID, const Uint8 *pBuf, Uint8 ucLen)
393
{
394
// Average response time for messages is ~30ms
395
Uint64 endTicks = SDL_GetTicks() + 100;
396
397
int nRead = 0;
398
while ((nRead = ReadInput(ctx)) != -1) {
399
if (nRead > 0) {
400
if (ctx->m_rgucReadBuffer[0] == k_eSwitchInputReportIDs_SubcommandReply) {
401
SwitchSubcommandInputPacket_t *reply = (SwitchSubcommandInputPacket_t *)&ctx->m_rgucReadBuffer[1];
402
if (reply->ucSubcommandID != expectedID || !(reply->ucSubcommandAck & 0x80)) {
403
continue;
404
}
405
if (reply->ucSubcommandID == k_eSwitchSubcommandIDs_SPIFlashRead) {
406
SDL_assert(ucLen == sizeof(reply->spiReadData.opData));
407
if (SDL_memcmp(&reply->spiReadData.opData, pBuf, ucLen) != 0) {
408
// This was a reply for another SPI read command
409
continue;
410
}
411
}
412
return reply;
413
}
414
} else {
415
SDL_Delay(1);
416
}
417
418
if (SDL_GetTicks() >= endTicks) {
419
break;
420
}
421
}
422
return NULL;
423
}
424
425
static bool ReadProprietaryReply(SDL_DriverSwitch_Context *ctx, ESwitchProprietaryCommandIDs expectedID)
426
{
427
// Average response time for messages is ~30ms
428
Uint64 endTicks = SDL_GetTicks() + 100;
429
430
int nRead = 0;
431
while ((nRead = ReadInput(ctx)) != -1) {
432
if (nRead > 0) {
433
if (ctx->m_rgucReadBuffer[0] == k_eSwitchInputReportIDs_CommandAck && ctx->m_rgucReadBuffer[1] == expectedID) {
434
return true;
435
}
436
} else {
437
SDL_Delay(1);
438
}
439
440
if (SDL_GetTicks() >= endTicks) {
441
break;
442
}
443
}
444
return false;
445
}
446
447
static void ConstructSubcommand(SDL_DriverSwitch_Context *ctx, ESwitchSubcommandIDs ucCommandID, const Uint8 *pBuf, Uint8 ucLen, SwitchSubcommandOutputPacket_t *outPacket)
448
{
449
SDL_memset(outPacket, 0, sizeof(*outPacket));
450
451
outPacket->commonData.ucPacketType = k_eSwitchOutputReportIDs_RumbleAndSubcommand;
452
outPacket->commonData.ucPacketNumber = ctx->m_nCommandNumber;
453
454
SDL_memcpy(outPacket->commonData.rumbleData, ctx->m_RumblePacket.rumbleData, sizeof(ctx->m_RumblePacket.rumbleData));
455
456
outPacket->ucSubcommandID = ucCommandID;
457
if (pBuf) {
458
SDL_memcpy(outPacket->rgucSubcommandData, pBuf, ucLen);
459
}
460
461
ctx->m_nCommandNumber = (ctx->m_nCommandNumber + 1) & 0xF;
462
}
463
464
static bool WritePacket(SDL_DriverSwitch_Context *ctx, void *pBuf, Uint8 ucLen)
465
{
466
Uint8 rgucBuf[k_unSwitchMaxOutputPacketLength];
467
const size_t unWriteSize = ctx->device->is_bluetooth ? k_unSwitchBluetoothPacketLength : k_unSwitchUSBPacketLength;
468
469
if (ucLen > k_unSwitchOutputPacketDataLength) {
470
return false;
471
}
472
473
if (ucLen < unWriteSize) {
474
SDL_memcpy(rgucBuf, pBuf, ucLen);
475
SDL_memset(rgucBuf + ucLen, 0, unWriteSize - ucLen);
476
pBuf = rgucBuf;
477
ucLen = (Uint8)unWriteSize;
478
}
479
if (ctx->m_bSyncWrite) {
480
return SDL_hid_write(ctx->device->dev, (Uint8 *)pBuf, ucLen) >= 0;
481
} else {
482
return WriteOutput(ctx, (Uint8 *)pBuf, ucLen) >= 0;
483
}
484
}
485
486
static bool WriteSubcommand(SDL_DriverSwitch_Context *ctx, ESwitchSubcommandIDs ucCommandID, const Uint8 *pBuf, Uint8 ucLen, SwitchSubcommandInputPacket_t **ppReply)
487
{
488
SwitchSubcommandInputPacket_t *reply = NULL;
489
int nTries;
490
491
for (nTries = 1; !reply && nTries <= ctx->m_nMaxWriteAttempts; ++nTries) {
492
SwitchSubcommandOutputPacket_t commandPacket;
493
ConstructSubcommand(ctx, ucCommandID, pBuf, ucLen, &commandPacket);
494
495
if (!WritePacket(ctx, &commandPacket, sizeof(commandPacket))) {
496
continue;
497
}
498
499
reply = ReadSubcommandReply(ctx, ucCommandID, pBuf, ucLen);
500
}
501
502
if (ppReply) {
503
*ppReply = reply;
504
}
505
return reply != NULL;
506
}
507
508
static bool WriteProprietary(SDL_DriverSwitch_Context *ctx, ESwitchProprietaryCommandIDs ucCommand, Uint8 *pBuf, Uint8 ucLen, bool waitForReply)
509
{
510
int nTries;
511
512
for (nTries = 1; nTries <= ctx->m_nMaxWriteAttempts; ++nTries) {
513
SwitchProprietaryOutputPacket_t packet;
514
515
if ((!pBuf && ucLen > 0) || ucLen > sizeof(packet.rgucProprietaryData)) {
516
return false;
517
}
518
519
SDL_zero(packet);
520
packet.ucPacketType = k_eSwitchOutputReportIDs_Proprietary;
521
packet.ucProprietaryID = ucCommand;
522
if (pBuf) {
523
SDL_memcpy(packet.rgucProprietaryData, pBuf, ucLen);
524
}
525
526
if (!WritePacket(ctx, &packet, sizeof(packet))) {
527
continue;
528
}
529
530
if (!waitForReply || ReadProprietaryReply(ctx, ucCommand)) {
531
// SDL_Log("Succeeded%s after %d tries", ctx->m_bSyncWrite ? " (sync)" : "", nTries);
532
return true;
533
}
534
}
535
// SDL_Log("Failed%s after %d tries", ctx->m_bSyncWrite ? " (sync)" : "", nTries);
536
return false;
537
}
538
539
static Uint8 EncodeRumbleHighAmplitude(Uint16 amplitude)
540
{
541
/* More information about these values can be found here:
542
* https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md
543
*/
544
Uint16 hfa[101][2] = { { 0, 0x0 }, { 514, 0x2 }, { 775, 0x4 }, { 921, 0x6 }, { 1096, 0x8 }, { 1303, 0x0a }, { 1550, 0x0c }, { 1843, 0x0e }, { 2192, 0x10 }, { 2606, 0x12 }, { 3100, 0x14 }, { 3686, 0x16 }, { 4383, 0x18 }, { 5213, 0x1a }, { 6199, 0x1c }, { 7372, 0x1e }, { 7698, 0x20 }, { 8039, 0x22 }, { 8395, 0x24 }, { 8767, 0x26 }, { 9155, 0x28 }, { 9560, 0x2a }, { 9984, 0x2c }, { 10426, 0x2e }, { 10887, 0x30 }, { 11369, 0x32 }, { 11873, 0x34 }, { 12398, 0x36 }, { 12947, 0x38 }, { 13520, 0x3a }, { 14119, 0x3c }, { 14744, 0x3e }, { 15067, 0x40 }, { 15397, 0x42 }, { 15734, 0x44 }, { 16079, 0x46 }, { 16431, 0x48 }, { 16790, 0x4a }, { 17158, 0x4c }, { 17534, 0x4e }, { 17918, 0x50 }, { 18310, 0x52 }, { 18711, 0x54 }, { 19121, 0x56 }, { 19540, 0x58 }, { 19967, 0x5a }, { 20405, 0x5c }, { 20851, 0x5e }, { 21308, 0x60 }, { 21775, 0x62 }, { 22251, 0x64 }, { 22739, 0x66 }, { 23236, 0x68 }, { 23745, 0x6a }, { 24265, 0x6c }, { 24797, 0x6e }, { 25340, 0x70 }, { 25894, 0x72 }, { 26462, 0x74 }, { 27041, 0x76 }, { 27633, 0x78 }, { 28238, 0x7a }, { 28856, 0x7c }, { 29488, 0x7e }, { 30134, 0x80 }, { 30794, 0x82 }, { 31468, 0x84 }, { 32157, 0x86 }, { 32861, 0x88 }, { 33581, 0x8a }, { 34316, 0x8c }, { 35068, 0x8e }, { 35836, 0x90 }, { 36620, 0x92 }, { 37422, 0x94 }, { 38242, 0x96 }, { 39079, 0x98 }, { 39935, 0x9a }, { 40809, 0x9c }, { 41703, 0x9e }, { 42616, 0xa0 }, { 43549, 0xa2 }, { 44503, 0xa4 }, { 45477, 0xa6 }, { 46473, 0xa8 }, { 47491, 0xaa }, { 48531, 0xac }, { 49593, 0xae }, { 50679, 0xb0 }, { 51789, 0xb2 }, { 52923, 0xb4 }, { 54082, 0xb6 }, { 55266, 0xb8 }, { 56476, 0xba }, { 57713, 0xbc }, { 58977, 0xbe }, { 60268, 0xc0 }, { 61588, 0xc2 }, { 62936, 0xc4 }, { 64315, 0xc6 }, { 65535, 0xc8 } };
545
int index = 0;
546
for (; index < 101; index++) {
547
if (amplitude <= hfa[index][0]) {
548
return (Uint8)hfa[index][1];
549
}
550
}
551
return (Uint8)hfa[100][1];
552
}
553
554
static Uint16 EncodeRumbleLowAmplitude(Uint16 amplitude)
555
{
556
/* More information about these values can be found here:
557
* https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md
558
*/
559
Uint16 lfa[101][2] = { { 0, 0x0040 }, { 514, 0x8040 }, { 775, 0x0041 }, { 921, 0x8041 }, { 1096, 0x0042 }, { 1303, 0x8042 }, { 1550, 0x0043 }, { 1843, 0x8043 }, { 2192, 0x0044 }, { 2606, 0x8044 }, { 3100, 0x0045 }, { 3686, 0x8045 }, { 4383, 0x0046 }, { 5213, 0x8046 }, { 6199, 0x0047 }, { 7372, 0x8047 }, { 7698, 0x0048 }, { 8039, 0x8048 }, { 8395, 0x0049 }, { 8767, 0x8049 }, { 9155, 0x004a }, { 9560, 0x804a }, { 9984, 0x004b }, { 10426, 0x804b }, { 10887, 0x004c }, { 11369, 0x804c }, { 11873, 0x004d }, { 12398, 0x804d }, { 12947, 0x004e }, { 13520, 0x804e }, { 14119, 0x004f }, { 14744, 0x804f }, { 15067, 0x0050 }, { 15397, 0x8050 }, { 15734, 0x0051 }, { 16079, 0x8051 }, { 16431, 0x0052 }, { 16790, 0x8052 }, { 17158, 0x0053 }, { 17534, 0x8053 }, { 17918, 0x0054 }, { 18310, 0x8054 }, { 18711, 0x0055 }, { 19121, 0x8055 }, { 19540, 0x0056 }, { 19967, 0x8056 }, { 20405, 0x0057 }, { 20851, 0x8057 }, { 21308, 0x0058 }, { 21775, 0x8058 }, { 22251, 0x0059 }, { 22739, 0x8059 }, { 23236, 0x005a }, { 23745, 0x805a }, { 24265, 0x005b }, { 24797, 0x805b }, { 25340, 0x005c }, { 25894, 0x805c }, { 26462, 0x005d }, { 27041, 0x805d }, { 27633, 0x005e }, { 28238, 0x805e }, { 28856, 0x005f }, { 29488, 0x805f }, { 30134, 0x0060 }, { 30794, 0x8060 }, { 31468, 0x0061 }, { 32157, 0x8061 }, { 32861, 0x0062 }, { 33581, 0x8062 }, { 34316, 0x0063 }, { 35068, 0x8063 }, { 35836, 0x0064 }, { 36620, 0x8064 }, { 37422, 0x0065 }, { 38242, 0x8065 }, { 39079, 0x0066 }, { 39935, 0x8066 }, { 40809, 0x0067 }, { 41703, 0x8067 }, { 42616, 0x0068 }, { 43549, 0x8068 }, { 44503, 0x0069 }, { 45477, 0x8069 }, { 46473, 0x006a }, { 47491, 0x806a }, { 48531, 0x006b }, { 49593, 0x806b }, { 50679, 0x006c }, { 51789, 0x806c }, { 52923, 0x006d }, { 54082, 0x806d }, { 55266, 0x006e }, { 56476, 0x806e }, { 57713, 0x006f }, { 58977, 0x806f }, { 60268, 0x0070 }, { 61588, 0x8070 }, { 62936, 0x0071 }, { 64315, 0x8071 }, { 65535, 0x0072 } };
560
int index = 0;
561
for (; index < 101; index++) {
562
if (amplitude <= lfa[index][0]) {
563
return lfa[index][1];
564
}
565
}
566
return lfa[100][1];
567
}
568
569
static void SetNeutralRumble(SwitchRumbleData_t *pRumble)
570
{
571
pRumble->rgucData[0] = 0x00;
572
pRumble->rgucData[1] = 0x01;
573
pRumble->rgucData[2] = 0x40;
574
pRumble->rgucData[3] = 0x40;
575
}
576
577
static void EncodeRumble(SwitchRumbleData_t *pRumble, Uint16 usHighFreq, Uint8 ucHighFreqAmp, Uint8 ucLowFreq, Uint16 usLowFreqAmp)
578
{
579
if (ucHighFreqAmp > 0 || usLowFreqAmp > 0) {
580
// High-band frequency and low-band amplitude are actually nine-bits each so they
581
// take a bit from the high-band amplitude and low-band frequency bytes respectively
582
pRumble->rgucData[0] = usHighFreq & 0xFF;
583
pRumble->rgucData[1] = ucHighFreqAmp | ((usHighFreq >> 8) & 0x01);
584
585
pRumble->rgucData[2] = ucLowFreq | ((usLowFreqAmp >> 8) & 0x80);
586
pRumble->rgucData[3] = usLowFreqAmp & 0xFF;
587
588
#ifdef DEBUG_RUMBLE
589
SDL_Log("Freq: %.2X %.2X %.2X, Amp: %.2X %.2X %.2X",
590
usHighFreq & 0xFF, ((usHighFreq >> 8) & 0x01), ucLowFreq,
591
ucHighFreqAmp, ((usLowFreqAmp >> 8) & 0x80), usLowFreqAmp & 0xFF);
592
#endif
593
} else {
594
SetNeutralRumble(pRumble);
595
}
596
}
597
598
static bool WriteRumble(SDL_DriverSwitch_Context *ctx)
599
{
600
/* Write into m_RumblePacket rather than a temporary buffer to allow the current rumble state
601
* to be retained for subsequent rumble or subcommand packets sent to the controller
602
*/
603
ctx->m_RumblePacket.ucPacketType = k_eSwitchOutputReportIDs_Rumble;
604
ctx->m_RumblePacket.ucPacketNumber = ctx->m_nCommandNumber;
605
ctx->m_nCommandNumber = (ctx->m_nCommandNumber + 1) & 0xF;
606
607
// Refresh the rumble state periodically
608
ctx->m_ulRumbleSent = SDL_GetTicks();
609
610
return WritePacket(ctx, (Uint8 *)&ctx->m_RumblePacket, sizeof(ctx->m_RumblePacket));
611
}
612
613
static ESwitchDeviceInfoControllerType CalculateControllerType(SDL_DriverSwitch_Context *ctx, ESwitchDeviceInfoControllerType eControllerType)
614
{
615
SDL_HIDAPI_Device *device = ctx->device;
616
617
// The N64 controller reports as a Pro controller over USB
618
if (eControllerType == k_eSwitchDeviceInfoControllerType_ProController &&
619
device->product_id == USB_PRODUCT_NINTENDO_N64_CONTROLLER) {
620
eControllerType = k_eSwitchDeviceInfoControllerType_N64;
621
}
622
623
if (eControllerType == k_eSwitchDeviceInfoControllerType_Unknown) {
624
// This might be a Joy-Con that's missing from a charging grip slot
625
if (device->product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_GRIP) {
626
if (device->interface_number == 1) {
627
eControllerType = k_eSwitchDeviceInfoControllerType_JoyConLeft;
628
} else {
629
eControllerType = k_eSwitchDeviceInfoControllerType_JoyConRight;
630
}
631
}
632
}
633
return eControllerType;
634
}
635
636
static bool BReadDeviceInfo(SDL_DriverSwitch_Context *ctx)
637
{
638
SwitchSubcommandInputPacket_t *reply = NULL;
639
640
if (ctx->device->is_bluetooth) {
641
if (WriteSubcommand(ctx, k_eSwitchSubcommandIDs_RequestDeviceInfo, NULL, 0, &reply)) {
642
// Byte 2: Controller ID (1=LJC, 2=RJC, 3=Pro)
643
ctx->m_eControllerType = CalculateControllerType(ctx, (ESwitchDeviceInfoControllerType)reply->deviceInfo.ucDeviceType);
644
645
// Bytes 4-9: MAC address (big-endian)
646
SDL_memcpy(ctx->m_rgucMACAddress, reply->deviceInfo.rgucMACAddress, sizeof(ctx->m_rgucMACAddress));
647
648
return true;
649
}
650
} else {
651
if (WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_Status, NULL, 0, true)) {
652
SwitchProprietaryStatusPacket_t *status = (SwitchProprietaryStatusPacket_t *)&ctx->m_rgucReadBuffer[0];
653
size_t i;
654
655
ctx->m_eControllerType = CalculateControllerType(ctx, (ESwitchDeviceInfoControllerType)status->ucDeviceType);
656
657
for (i = 0; i < sizeof(ctx->m_rgucMACAddress); ++i) {
658
ctx->m_rgucMACAddress[i] = status->rgucMACAddress[sizeof(ctx->m_rgucMACAddress) - i - 1];
659
}
660
661
return true;
662
}
663
}
664
return false;
665
}
666
667
static bool BTrySetupUSB(SDL_DriverSwitch_Context *ctx)
668
{
669
/* We have to send a connection handshake to the controller when communicating over USB
670
* before we're able to send it other commands. Luckily this command is not supported
671
* over Bluetooth, so we can use the controller's lack of response as a way to
672
* determine if the connection is over USB or Bluetooth
673
*/
674
if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_Handshake, NULL, 0, true)) {
675
return false;
676
}
677
if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_HighSpeed, NULL, 0, true)) {
678
// The 8BitDo M30 and SF30 Pro don't respond to this command, but otherwise work correctly
679
// return false;
680
}
681
if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_Handshake, NULL, 0, true)) {
682
// This fails on the right Joy-Con when plugged into the charging grip
683
// return false;
684
}
685
if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_ForceUSB, NULL, 0, false)) {
686
return false;
687
}
688
return true;
689
}
690
691
static bool SetVibrationEnabled(SDL_DriverSwitch_Context *ctx, Uint8 enabled)
692
{
693
return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_EnableVibration, &enabled, sizeof(enabled), NULL);
694
}
695
static bool SetInputMode(SDL_DriverSwitch_Context *ctx, Uint8 input_mode)
696
{
697
#ifdef FORCE_SIMPLE_REPORTS
698
input_mode = k_eSwitchInputReportIDs_SimpleControllerState;
699
#endif
700
#ifdef FORCE_FULL_REPORTS
701
input_mode = k_eSwitchInputReportIDs_FullControllerState;
702
#endif
703
704
if (input_mode == ctx->m_nCurrentInputMode) {
705
return true;
706
} else {
707
ctx->m_nCurrentInputMode = input_mode;
708
709
return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SetInputReportMode, &input_mode, sizeof(input_mode), NULL);
710
}
711
}
712
713
static bool SetHomeLED(SDL_DriverSwitch_Context *ctx, Uint8 brightness)
714
{
715
Uint8 ucLedIntensity = 0;
716
Uint8 rgucBuffer[4];
717
718
if (brightness > 0) {
719
if (brightness < 65) {
720
ucLedIntensity = (brightness + 5) / 10;
721
} else {
722
ucLedIntensity = (Uint8)SDL_ceilf(0xF * SDL_powf((float)brightness / 100.f, 2.13f));
723
}
724
}
725
726
rgucBuffer[0] = (0x0 << 4) | 0x1; // 0 mini cycles (besides first), cycle duration 8ms
727
rgucBuffer[1] = ((ucLedIntensity & 0xF) << 4) | 0x0; // LED start intensity (0x0-0xF), 0 cycles (LED stays on at start intensity after first cycle)
728
rgucBuffer[2] = ((ucLedIntensity & 0xF) << 4) | 0x0; // First cycle LED intensity, 0x0 intensity for second cycle
729
rgucBuffer[3] = (0x0 << 4) | 0x0; // 8ms fade transition to first cycle, 8ms first cycle LED duration
730
731
return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SetHomeLight, rgucBuffer, sizeof(rgucBuffer), NULL);
732
}
733
734
static void SDLCALL SDL_HomeLEDHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
735
{
736
SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)userdata;
737
738
if (hint && *hint) {
739
int value;
740
741
if (SDL_strchr(hint, '.') != NULL) {
742
value = (int)(100.0f * SDL_atof(hint));
743
if (value > 255) {
744
value = 255;
745
}
746
} else if (SDL_GetStringBoolean(hint, true)) {
747
value = 100;
748
} else {
749
value = 0;
750
}
751
SetHomeLED(ctx, (Uint8)value);
752
}
753
}
754
755
static void UpdateSlotLED(SDL_DriverSwitch_Context *ctx)
756
{
757
if (!ctx->m_bInputOnly) {
758
Uint8 led_data = 0;
759
760
if (ctx->m_bPlayerLights && ctx->m_nPlayerIndex >= 0) {
761
led_data = (1 << (ctx->m_nPlayerIndex % 4));
762
}
763
WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SetPlayerLights, &led_data, sizeof(led_data), NULL);
764
}
765
}
766
767
static void SDLCALL SDL_PlayerLEDHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
768
{
769
SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)userdata;
770
bool bPlayerLights = SDL_GetStringBoolean(hint, true);
771
772
if (bPlayerLights != ctx->m_bPlayerLights) {
773
ctx->m_bPlayerLights = bPlayerLights;
774
775
UpdateSlotLED(ctx);
776
HIDAPI_UpdateDeviceProperties(ctx->device);
777
}
778
}
779
780
static void GetInitialInputMode(SDL_DriverSwitch_Context *ctx)
781
{
782
if (!ctx->m_nInitialInputMode) {
783
// This will set the initial input mode if it can
784
ReadInput(ctx);
785
}
786
}
787
788
static Uint8 GetDefaultInputMode(SDL_DriverSwitch_Context *ctx)
789
{
790
Uint8 input_mode;
791
792
// Determine the desired input mode
793
if (ctx->m_nInitialInputMode) {
794
input_mode = ctx->m_nInitialInputMode;
795
} else {
796
if (ctx->device->is_bluetooth) {
797
input_mode = k_eSwitchInputReportIDs_SimpleControllerState;
798
} else {
799
input_mode = k_eSwitchInputReportIDs_FullControllerState;
800
}
801
}
802
803
switch (ctx->m_eEnhancedReportHint) {
804
case SWITCH_ENHANCED_REPORT_HINT_OFF:
805
input_mode = k_eSwitchInputReportIDs_SimpleControllerState;
806
break;
807
case SWITCH_ENHANCED_REPORT_HINT_ON:
808
if (input_mode == k_eSwitchInputReportIDs_SimpleControllerState) {
809
input_mode = k_eSwitchInputReportIDs_FullControllerState;
810
}
811
break;
812
case SWITCH_ENHANCED_REPORT_HINT_AUTO:
813
/* Joy-Con controllers switch their thumbsticks into D-pad mode in simple mode,
814
* so let's enable full controller state for them.
815
*/
816
if (ctx->device->vendor_id == USB_VENDOR_NINTENDO &&
817
(ctx->device->product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT ||
818
ctx->device->product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT)) {
819
input_mode = k_eSwitchInputReportIDs_FullControllerState;
820
}
821
break;
822
}
823
824
// Wired controllers break if they are put into simple controller state
825
if (input_mode == k_eSwitchInputReportIDs_SimpleControllerState &&
826
!ctx->device->is_bluetooth) {
827
input_mode = k_eSwitchInputReportIDs_FullControllerState;
828
}
829
return input_mode;
830
}
831
832
static Uint8 GetSensorInputMode(SDL_DriverSwitch_Context *ctx)
833
{
834
Uint8 input_mode;
835
836
// Determine the desired input mode
837
if (!ctx->m_nInitialInputMode ||
838
ctx->m_nInitialInputMode == k_eSwitchInputReportIDs_SimpleControllerState) {
839
input_mode = k_eSwitchInputReportIDs_FullControllerState;
840
} else {
841
input_mode = ctx->m_nInitialInputMode;
842
}
843
return input_mode;
844
}
845
846
static void UpdateInputMode(SDL_DriverSwitch_Context *ctx)
847
{
848
Uint8 input_mode;
849
850
if (ctx->m_bReportSensors) {
851
input_mode = GetSensorInputMode(ctx);
852
} else {
853
input_mode = GetDefaultInputMode(ctx);
854
}
855
SetInputMode(ctx, input_mode);
856
}
857
858
static void SetEnhancedModeAvailable(SDL_DriverSwitch_Context *ctx)
859
{
860
if (ctx->m_bEnhancedModeAvailable) {
861
return;
862
}
863
ctx->m_bEnhancedModeAvailable = true;
864
865
if (ctx->m_bSensorsSupported) {
866
// Use the right sensor in the combined Joy-Con pair
867
if (!ctx->device->parent ||
868
ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {
869
SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_GYRO, 200.0f);
870
SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_ACCEL, 200.0f);
871
}
872
if (ctx->device->parent &&
873
ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft) {
874
SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_GYRO_L, 200.0f);
875
SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_ACCEL_L, 200.0f);
876
}
877
if (ctx->device->parent &&
878
ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {
879
SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_GYRO_R, 200.0f);
880
SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_ACCEL_R, 200.0f);
881
}
882
}
883
}
884
885
static void SetEnhancedReportHint(SDL_DriverSwitch_Context *ctx, HIDAPI_Switch_EnhancedReportHint eEnhancedReportHint)
886
{
887
ctx->m_eEnhancedReportHint = eEnhancedReportHint;
888
889
switch (eEnhancedReportHint) {
890
case SWITCH_ENHANCED_REPORT_HINT_OFF:
891
ctx->m_bEnhancedMode = false;
892
break;
893
case SWITCH_ENHANCED_REPORT_HINT_ON:
894
SetEnhancedModeAvailable(ctx);
895
ctx->m_bEnhancedMode = true;
896
break;
897
case SWITCH_ENHANCED_REPORT_HINT_AUTO:
898
SetEnhancedModeAvailable(ctx);
899
break;
900
}
901
902
UpdateInputMode(ctx);
903
}
904
905
static void UpdateEnhancedModeOnEnhancedReport(SDL_DriverSwitch_Context *ctx)
906
{
907
if (ctx->m_eEnhancedReportHint == SWITCH_ENHANCED_REPORT_HINT_AUTO) {
908
SetEnhancedReportHint(ctx, SWITCH_ENHANCED_REPORT_HINT_ON);
909
}
910
}
911
912
static void UpdateEnhancedModeOnApplicationUsage(SDL_DriverSwitch_Context *ctx)
913
{
914
if (ctx->m_eEnhancedReportHint == SWITCH_ENHANCED_REPORT_HINT_AUTO) {
915
SetEnhancedReportHint(ctx, SWITCH_ENHANCED_REPORT_HINT_ON);
916
}
917
}
918
919
static void SDLCALL SDL_EnhancedReportsChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
920
{
921
SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)userdata;
922
923
if (hint && SDL_strcasecmp(hint, "auto") == 0) {
924
SetEnhancedReportHint(ctx, SWITCH_ENHANCED_REPORT_HINT_AUTO);
925
} else if (SDL_GetStringBoolean(hint, true)) {
926
SetEnhancedReportHint(ctx, SWITCH_ENHANCED_REPORT_HINT_ON);
927
} else {
928
SetEnhancedReportHint(ctx, SWITCH_ENHANCED_REPORT_HINT_OFF);
929
}
930
}
931
932
static bool SetIMUEnabled(SDL_DriverSwitch_Context *ctx, bool enabled)
933
{
934
Uint8 imu_data = enabled ? 1 : 0;
935
return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_EnableIMU, &imu_data, sizeof(imu_data), NULL);
936
}
937
938
static bool LoadStickCalibration(SDL_DriverSwitch_Context *ctx)
939
{
940
Uint8 *pLeftStickCal = NULL;
941
Uint8 *pRightStickCal = NULL;
942
size_t stick, axis;
943
SwitchSubcommandInputPacket_t *user_reply = NULL;
944
SwitchSubcommandInputPacket_t *factory_reply = NULL;
945
SwitchSPIOpData_t readUserParams;
946
SwitchSPIOpData_t readFactoryParams;
947
Uint8 userParamsReadSuccessCount = 0;
948
949
// Read User Calibration Info
950
readUserParams.unAddress = k_unSPIStickUserCalibrationStartOffset;
951
readUserParams.ucLength = k_unSPIStickUserCalibrationLength;
952
953
// This isn't readable on all controllers, so ignore failure
954
WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SPIFlashRead, (uint8_t *)&readUserParams, sizeof(readUserParams), &user_reply);
955
956
// Read Factory Calibration Info
957
readFactoryParams.unAddress = k_unSPIStickFactoryCalibrationStartOffset;
958
readFactoryParams.ucLength = k_unSPIStickFactoryCalibrationLength;
959
960
// Automatically select the user calibration if magic bytes are set
961
if (user_reply && user_reply->stickUserCalibration.rgucLeftMagic[0] == 0xB2 && user_reply->stickUserCalibration.rgucLeftMagic[1] == 0xA1) {
962
userParamsReadSuccessCount += 1;
963
pLeftStickCal = user_reply->stickUserCalibration.rgucLeftCalibration;
964
}
965
966
if (user_reply && user_reply->stickUserCalibration.rgucRightMagic[0] == 0xB2 && user_reply->stickUserCalibration.rgucRightMagic[1] == 0xA1) {
967
userParamsReadSuccessCount += 1;
968
pRightStickCal = user_reply->stickUserCalibration.rgucRightCalibration;
969
}
970
971
// Only read the factory calibration info if we failed to receive the correct magic bytes
972
if (userParamsReadSuccessCount < 2) {
973
// Read Factory Calibration Info
974
readFactoryParams.unAddress = k_unSPIStickFactoryCalibrationStartOffset;
975
readFactoryParams.ucLength = k_unSPIStickFactoryCalibrationLength;
976
977
const int MAX_ATTEMPTS = 3;
978
for (int attempt = 0;; ++attempt) {
979
if (!WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SPIFlashRead, (uint8_t *)&readFactoryParams, sizeof(readFactoryParams), &factory_reply)) {
980
return false;
981
}
982
983
if (factory_reply->stickFactoryCalibration.opData.unAddress == k_unSPIStickFactoryCalibrationStartOffset) {
984
// We successfully read the calibration data
985
pLeftStickCal = factory_reply->stickFactoryCalibration.rgucLeftCalibration;
986
pRightStickCal = factory_reply->stickFactoryCalibration.rgucRightCalibration;
987
break;
988
}
989
990
if (attempt == MAX_ATTEMPTS) {
991
return false;
992
}
993
}
994
}
995
996
// If we still don't have calibration data, return false
997
if (pLeftStickCal == NULL || pRightStickCal == NULL)
998
{
999
return false;
1000
}
1001
1002
/* Stick calibration values are 12-bits each and are packed by bit
1003
* For whatever reason the fields are in a different order for each stick
1004
* Left: X-Max, Y-Max, X-Center, Y-Center, X-Min, Y-Min
1005
* Right: X-Center, Y-Center, X-Min, Y-Min, X-Max, Y-Max
1006
*/
1007
1008
// Left stick
1009
ctx->m_StickCalData[0].axis[0].sMax = ((pLeftStickCal[1] << 8) & 0xF00) | pLeftStickCal[0]; // X Axis max above center
1010
ctx->m_StickCalData[0].axis[1].sMax = (pLeftStickCal[2] << 4) | (pLeftStickCal[1] >> 4); // Y Axis max above center
1011
ctx->m_StickCalData[0].axis[0].sCenter = ((pLeftStickCal[4] << 8) & 0xF00) | pLeftStickCal[3]; // X Axis center
1012
ctx->m_StickCalData[0].axis[1].sCenter = (pLeftStickCal[5] << 4) | (pLeftStickCal[4] >> 4); // Y Axis center
1013
ctx->m_StickCalData[0].axis[0].sMin = ((pLeftStickCal[7] << 8) & 0xF00) | pLeftStickCal[6]; // X Axis min below center
1014
ctx->m_StickCalData[0].axis[1].sMin = (pLeftStickCal[8] << 4) | (pLeftStickCal[7] >> 4); // Y Axis min below center
1015
1016
// Right stick
1017
ctx->m_StickCalData[1].axis[0].sCenter = ((pRightStickCal[1] << 8) & 0xF00) | pRightStickCal[0]; // X Axis center
1018
ctx->m_StickCalData[1].axis[1].sCenter = (pRightStickCal[2] << 4) | (pRightStickCal[1] >> 4); // Y Axis center
1019
ctx->m_StickCalData[1].axis[0].sMin = ((pRightStickCal[4] << 8) & 0xF00) | pRightStickCal[3]; // X Axis min below center
1020
ctx->m_StickCalData[1].axis[1].sMin = (pRightStickCal[5] << 4) | (pRightStickCal[4] >> 4); // Y Axis min below center
1021
ctx->m_StickCalData[1].axis[0].sMax = ((pRightStickCal[7] << 8) & 0xF00) | pRightStickCal[6]; // X Axis max above center
1022
ctx->m_StickCalData[1].axis[1].sMax = (pRightStickCal[8] << 4) | (pRightStickCal[7] >> 4); // Y Axis max above center
1023
1024
// Filter out any values that were uninitialized (0xFFF) in the SPI read
1025
for (stick = 0; stick < 2; ++stick) {
1026
for (axis = 0; axis < 2; ++axis) {
1027
if (ctx->m_StickCalData[stick].axis[axis].sCenter == 0xFFF) {
1028
ctx->m_StickCalData[stick].axis[axis].sCenter = 2048;
1029
}
1030
if (ctx->m_StickCalData[stick].axis[axis].sMax == 0xFFF) {
1031
ctx->m_StickCalData[stick].axis[axis].sMax = (Sint16)(ctx->m_StickCalData[stick].axis[axis].sCenter * 0.7f);
1032
}
1033
if (ctx->m_StickCalData[stick].axis[axis].sMin == 0xFFF) {
1034
ctx->m_StickCalData[stick].axis[axis].sMin = (Sint16)(ctx->m_StickCalData[stick].axis[axis].sCenter * 0.7f);
1035
}
1036
}
1037
}
1038
1039
for (stick = 0; stick < 2; ++stick) {
1040
for (axis = 0; axis < 2; ++axis) {
1041
ctx->m_StickExtents[stick].axis[axis].sMin = -(Sint16)(ctx->m_StickCalData[stick].axis[axis].sMin * 0.7f);
1042
ctx->m_StickExtents[stick].axis[axis].sMax = (Sint16)(ctx->m_StickCalData[stick].axis[axis].sMax * 0.7f);
1043
}
1044
}
1045
1046
for (stick = 0; stick < 2; ++stick) {
1047
for (axis = 0; axis < 2; ++axis) {
1048
ctx->m_SimpleStickExtents[stick].axis[axis].sMin = (Sint16)(SDL_MIN_SINT16 * 0.5f);
1049
ctx->m_SimpleStickExtents[stick].axis[axis].sMax = (Sint16)(SDL_MAX_SINT16 * 0.5f);
1050
}
1051
}
1052
1053
return true;
1054
}
1055
1056
static bool LoadIMUCalibration(SDL_DriverSwitch_Context *ctx)
1057
{
1058
SwitchSubcommandInputPacket_t *reply = NULL;
1059
1060
// Read Calibration Info
1061
SwitchSPIOpData_t readParams;
1062
readParams.unAddress = k_unSPIIMUScaleStartOffset;
1063
readParams.ucLength = k_unSPIIMUScaleLength;
1064
1065
if (WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SPIFlashRead, (uint8_t *)&readParams, sizeof(readParams), &reply)) {
1066
Uint8 *pIMUScale;
1067
Sint16 sAccelRawX, sAccelRawY, sAccelRawZ, sGyroRawX, sGyroRawY, sGyroRawZ;
1068
Sint16 sAccelSensCoeffX, sAccelSensCoeffY, sAccelSensCoeffZ;
1069
Sint16 sGyroSensCoeffX, sGyroSensCoeffY, sGyroSensCoeffZ;
1070
1071
// IMU scale gives us multipliers for converting raw values to real world values
1072
pIMUScale = reply->spiReadData.rgucReadData;
1073
1074
sAccelRawX = (pIMUScale[1] << 8) | pIMUScale[0];
1075
sAccelRawY = (pIMUScale[3] << 8) | pIMUScale[2];
1076
sAccelRawZ = (pIMUScale[5] << 8) | pIMUScale[4];
1077
1078
sAccelSensCoeffX = (pIMUScale[7] << 8) | pIMUScale[6];
1079
sAccelSensCoeffY = (pIMUScale[9] << 8) | pIMUScale[8];
1080
sAccelSensCoeffZ = (pIMUScale[11] << 8) | pIMUScale[10];
1081
1082
sGyroRawX = (pIMUScale[13] << 8) | pIMUScale[12];
1083
sGyroRawY = (pIMUScale[15] << 8) | pIMUScale[14];
1084
sGyroRawZ = (pIMUScale[17] << 8) | pIMUScale[16];
1085
1086
sGyroSensCoeffX = (pIMUScale[19] << 8) | pIMUScale[18];
1087
sGyroSensCoeffY = (pIMUScale[21] << 8) | pIMUScale[20];
1088
sGyroSensCoeffZ = (pIMUScale[23] << 8) | pIMUScale[22];
1089
1090
// Check for user calibration data. If it's present and set, it'll override the factory settings
1091
readParams.unAddress = k_unSPIIMUUserScaleStartOffset;
1092
readParams.ucLength = k_unSPIIMUUserScaleLength;
1093
if (WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SPIFlashRead, (uint8_t *)&readParams, sizeof(readParams), &reply) && (pIMUScale[0] | pIMUScale[1] << 8) == 0xA1B2) {
1094
pIMUScale = reply->spiReadData.rgucReadData;
1095
1096
sAccelRawX = (pIMUScale[3] << 8) | pIMUScale[2];
1097
sAccelRawY = (pIMUScale[5] << 8) | pIMUScale[4];
1098
sAccelRawZ = (pIMUScale[7] << 8) | pIMUScale[6];
1099
1100
sGyroRawX = (pIMUScale[15] << 8) | pIMUScale[14];
1101
sGyroRawY = (pIMUScale[17] << 8) | pIMUScale[16];
1102
sGyroRawZ = (pIMUScale[19] << 8) | pIMUScale[18];
1103
}
1104
1105
// Accelerometer scale
1106
ctx->m_IMUScaleData.fAccelScaleX = SWITCH_ACCEL_SCALE_MULT / ((float)sAccelSensCoeffX - (float)sAccelRawX) * SDL_STANDARD_GRAVITY;
1107
ctx->m_IMUScaleData.fAccelScaleY = SWITCH_ACCEL_SCALE_MULT / ((float)sAccelSensCoeffY - (float)sAccelRawY) * SDL_STANDARD_GRAVITY;
1108
ctx->m_IMUScaleData.fAccelScaleZ = SWITCH_ACCEL_SCALE_MULT / ((float)sAccelSensCoeffZ - (float)sAccelRawZ) * SDL_STANDARD_GRAVITY;
1109
1110
// Gyro scale
1111
ctx->m_IMUScaleData.fGyroScaleX = SWITCH_GYRO_SCALE_MULT / ((float)sGyroSensCoeffX - (float)sGyroRawX) * SDL_PI_F / 180.0f;
1112
ctx->m_IMUScaleData.fGyroScaleY = SWITCH_GYRO_SCALE_MULT / ((float)sGyroSensCoeffY - (float)sGyroRawY) * SDL_PI_F / 180.0f;
1113
ctx->m_IMUScaleData.fGyroScaleZ = SWITCH_GYRO_SCALE_MULT / ((float)sGyroSensCoeffZ - (float)sGyroRawZ) * SDL_PI_F / 180.0f;
1114
1115
} else {
1116
// Use default values
1117
const float accelScale = SDL_STANDARD_GRAVITY / SWITCH_ACCEL_SCALE;
1118
const float gyroScale = SDL_PI_F / 180.0f / SWITCH_GYRO_SCALE;
1119
1120
ctx->m_IMUScaleData.fAccelScaleX = accelScale;
1121
ctx->m_IMUScaleData.fAccelScaleY = accelScale;
1122
ctx->m_IMUScaleData.fAccelScaleZ = accelScale;
1123
1124
ctx->m_IMUScaleData.fGyroScaleX = gyroScale;
1125
ctx->m_IMUScaleData.fGyroScaleY = gyroScale;
1126
ctx->m_IMUScaleData.fGyroScaleZ = gyroScale;
1127
}
1128
return true;
1129
}
1130
1131
static Sint16 ApplyStickCalibration(SDL_DriverSwitch_Context *ctx, int nStick, int nAxis, Sint16 sRawValue)
1132
{
1133
sRawValue -= ctx->m_StickCalData[nStick].axis[nAxis].sCenter;
1134
1135
if (sRawValue >= 0) {
1136
if (sRawValue > ctx->m_StickExtents[nStick].axis[nAxis].sMax) {
1137
ctx->m_StickExtents[nStick].axis[nAxis].sMax = sRawValue;
1138
}
1139
return (Sint16)HIDAPI_RemapVal(sRawValue, 0, ctx->m_StickExtents[nStick].axis[nAxis].sMax, 0, SDL_MAX_SINT16);
1140
} else {
1141
if (sRawValue < ctx->m_StickExtents[nStick].axis[nAxis].sMin) {
1142
ctx->m_StickExtents[nStick].axis[nAxis].sMin = sRawValue;
1143
}
1144
return (Sint16)HIDAPI_RemapVal(sRawValue, ctx->m_StickExtents[nStick].axis[nAxis].sMin, 0, SDL_MIN_SINT16, 0);
1145
}
1146
}
1147
1148
static Sint16 ApplySimpleStickCalibration(SDL_DriverSwitch_Context *ctx, int nStick, int nAxis, Sint16 sRawValue)
1149
{
1150
// 0x8000 is the neutral value for all joystick axes
1151
const Uint16 usJoystickCenter = 0x8000;
1152
1153
sRawValue -= usJoystickCenter;
1154
1155
if (sRawValue >= 0) {
1156
if (sRawValue > ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMax) {
1157
ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMax = sRawValue;
1158
}
1159
return (Sint16)HIDAPI_RemapVal(sRawValue, 0, ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMax, 0, SDL_MAX_SINT16);
1160
} else {
1161
if (sRawValue < ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMin) {
1162
ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMin = sRawValue;
1163
}
1164
return (Sint16)HIDAPI_RemapVal(sRawValue, ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMin, 0, SDL_MIN_SINT16, 0);
1165
}
1166
}
1167
1168
static Uint8 RemapButton(SDL_DriverSwitch_Context *ctx, Uint8 button)
1169
{
1170
if (ctx->m_bUseButtonLabels) {
1171
// Use button labels instead of positions, e.g. Nintendo Online Classic controllers
1172
switch (button) {
1173
case SDL_GAMEPAD_BUTTON_SOUTH:
1174
return SDL_GAMEPAD_BUTTON_EAST;
1175
case SDL_GAMEPAD_BUTTON_EAST:
1176
return SDL_GAMEPAD_BUTTON_SOUTH;
1177
case SDL_GAMEPAD_BUTTON_WEST:
1178
return SDL_GAMEPAD_BUTTON_NORTH;
1179
case SDL_GAMEPAD_BUTTON_NORTH:
1180
return SDL_GAMEPAD_BUTTON_WEST;
1181
default:
1182
break;
1183
}
1184
}
1185
return button;
1186
}
1187
1188
static int GetMaxWriteAttempts(SDL_HIDAPI_Device *device)
1189
{
1190
if (device->vendor_id == USB_VENDOR_NINTENDO &&
1191
device->product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_GRIP) {
1192
// This device is a little slow and we know we're always on USB
1193
return 20;
1194
} else {
1195
return 5;
1196
}
1197
}
1198
1199
static ESwitchDeviceInfoControllerType ReadJoyConControllerType(SDL_HIDAPI_Device *device)
1200
{
1201
ESwitchDeviceInfoControllerType eControllerType = k_eSwitchDeviceInfoControllerType_Unknown;
1202
const int MAX_ATTEMPTS = 1; // Don't try too long, in case this is a zombie Bluetooth controller
1203
int attempts = 0;
1204
1205
// Create enough of a context to read the controller type from the device
1206
SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)SDL_calloc(1, sizeof(*ctx));
1207
if (ctx) {
1208
ctx->device = device;
1209
ctx->m_bSyncWrite = true;
1210
ctx->m_nMaxWriteAttempts = GetMaxWriteAttempts(device);
1211
1212
for ( ; ; ) {
1213
++attempts;
1214
if (device->is_bluetooth) {
1215
SwitchSubcommandInputPacket_t *reply = NULL;
1216
1217
if (WriteSubcommand(ctx, k_eSwitchSubcommandIDs_RequestDeviceInfo, NULL, 0, &reply)) {
1218
eControllerType = CalculateControllerType(ctx, (ESwitchDeviceInfoControllerType)reply->deviceInfo.ucDeviceType);
1219
}
1220
} else {
1221
if (WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_Status, NULL, 0, true)) {
1222
SwitchProprietaryStatusPacket_t *status = (SwitchProprietaryStatusPacket_t *)&ctx->m_rgucReadBuffer[0];
1223
1224
eControllerType = CalculateControllerType(ctx, (ESwitchDeviceInfoControllerType)status->ucDeviceType);
1225
}
1226
}
1227
if (eControllerType == k_eSwitchDeviceInfoControllerType_Unknown && attempts < MAX_ATTEMPTS) {
1228
// Wait a bit and try again
1229
SDL_Delay(100);
1230
continue;
1231
}
1232
break;
1233
}
1234
SDL_free(ctx);
1235
}
1236
return eControllerType;
1237
}
1238
1239
static bool HasHomeLED(SDL_DriverSwitch_Context *ctx)
1240
{
1241
Uint16 vendor_id = ctx->device->vendor_id;
1242
Uint16 product_id = ctx->device->product_id;
1243
1244
// The Power A Nintendo Switch Pro controllers don't have a Home LED
1245
if (vendor_id == 0 && product_id == 0) {
1246
return false;
1247
}
1248
1249
// HORI Wireless Switch Pad
1250
if (vendor_id == 0x0f0d && product_id == 0x00f6) {
1251
return false;
1252
}
1253
1254
// Third party controllers don't have a home LED and will shut off if we try to set it
1255
if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_Unknown ||
1256
ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_LicProController) {
1257
return false;
1258
}
1259
1260
// The Nintendo Online classic controllers don't have a Home LED
1261
if (vendor_id == USB_VENDOR_NINTENDO &&
1262
ctx->m_eControllerType > k_eSwitchDeviceInfoControllerType_ProController) {
1263
return false;
1264
}
1265
1266
return true;
1267
}
1268
1269
static bool AlwaysUsesLabels(Uint16 vendor_id, Uint16 product_id, ESwitchDeviceInfoControllerType eControllerType)
1270
{
1271
// Some controllers don't have a diamond button configuration, so should always use labels
1272
if (SDL_IsJoystickGameCube(vendor_id, product_id)) {
1273
return true;
1274
}
1275
switch (eControllerType) {
1276
case k_eSwitchDeviceInfoControllerType_HVCLeft:
1277
case k_eSwitchDeviceInfoControllerType_HVCRight:
1278
case k_eSwitchDeviceInfoControllerType_NESLeft:
1279
case k_eSwitchDeviceInfoControllerType_NESRight:
1280
case k_eSwitchDeviceInfoControllerType_N64:
1281
case k_eSwitchDeviceInfoControllerType_SEGA_Genesis:
1282
return true;
1283
default:
1284
return false;
1285
}
1286
}
1287
1288
static void HIDAPI_DriverNintendoClassic_RegisterHints(SDL_HintCallback callback, void *userdata)
1289
{
1290
SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_NINTENDO_CLASSIC, callback, userdata);
1291
}
1292
1293
static void HIDAPI_DriverNintendoClassic_UnregisterHints(SDL_HintCallback callback, void *userdata)
1294
{
1295
SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_NINTENDO_CLASSIC, callback, userdata);
1296
}
1297
1298
static bool HIDAPI_DriverNintendoClassic_IsEnabled(void)
1299
{
1300
return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_NINTENDO_CLASSIC, SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI, SDL_HIDAPI_DEFAULT));
1301
}
1302
1303
static bool HIDAPI_DriverNintendoClassic_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)
1304
{
1305
if (vendor_id == USB_VENDOR_NINTENDO) {
1306
if (product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT) {
1307
if (SDL_strncmp(name, "NES Controller", 14) == 0 ||
1308
SDL_strncmp(name, "HVC Controller", 14) == 0) {
1309
return true;
1310
}
1311
}
1312
1313
if (product_id == USB_PRODUCT_NINTENDO_N64_CONTROLLER) {
1314
return true;
1315
}
1316
1317
if (product_id == USB_PRODUCT_NINTENDO_SEGA_GENESIS_CONTROLLER) {
1318
return true;
1319
}
1320
1321
if (product_id == USB_PRODUCT_NINTENDO_SNES_CONTROLLER) {
1322
return true;
1323
}
1324
}
1325
1326
return false;
1327
}
1328
1329
static void HIDAPI_DriverJoyCons_RegisterHints(SDL_HintCallback callback, void *userdata)
1330
{
1331
SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, callback, userdata);
1332
}
1333
1334
static void HIDAPI_DriverJoyCons_UnregisterHints(SDL_HintCallback callback, void *userdata)
1335
{
1336
SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, callback, userdata);
1337
}
1338
1339
static bool HIDAPI_DriverJoyCons_IsEnabled(void)
1340
{
1341
return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI, SDL_HIDAPI_DEFAULT));
1342
}
1343
1344
static bool HIDAPI_DriverJoyCons_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)
1345
{
1346
if (vendor_id == USB_VENDOR_NINTENDO) {
1347
if (product_id == USB_PRODUCT_NINTENDO_SWITCH_PRO && device && device->dev) {
1348
// This might be a Kinvoca Joy-Con that reports VID/PID as a Switch Pro controller
1349
ESwitchDeviceInfoControllerType eControllerType = ReadJoyConControllerType(device);
1350
if (eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft ||
1351
eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {
1352
return true;
1353
}
1354
}
1355
1356
if (product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT ||
1357
product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT ||
1358
product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_GRIP) {
1359
return true;
1360
}
1361
}
1362
return false;
1363
}
1364
1365
static void HIDAPI_DriverSwitch_RegisterHints(SDL_HintCallback callback, void *userdata)
1366
{
1367
SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, callback, userdata);
1368
}
1369
1370
static void HIDAPI_DriverSwitch_UnregisterHints(SDL_HintCallback callback, void *userdata)
1371
{
1372
SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, callback, userdata);
1373
}
1374
1375
static bool HIDAPI_DriverSwitch_IsEnabled(void)
1376
{
1377
return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI, SDL_HIDAPI_DEFAULT));
1378
}
1379
1380
static bool HIDAPI_DriverSwitch_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)
1381
{
1382
/* The HORI Wireless Switch Pad enumerates as a HID device when connected via USB
1383
with the same VID/PID as when connected over Bluetooth but doesn't actually
1384
support communication over USB. The most reliable way to block this without allowing the
1385
controller to continually attempt to reconnect is to filter it out by manufacturer/product string.
1386
Note that the controller does have a different product string when connected over Bluetooth.
1387
*/
1388
if (SDL_strcmp(name, "HORI Wireless Switch Pad") == 0) {
1389
return false;
1390
}
1391
1392
// If it's handled by another driver, it's not handled here
1393
if (HIDAPI_DriverNintendoClassic_IsSupportedDevice(device, name, type, vendor_id, product_id, version, interface_number, interface_class, interface_subclass, interface_protocol) ||
1394
HIDAPI_DriverJoyCons_IsSupportedDevice(device, name, type, vendor_id, product_id, version, interface_number, interface_class, interface_subclass, interface_protocol)) {
1395
return false;
1396
}
1397
1398
return (type == SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO);
1399
}
1400
1401
static void UpdateDeviceIdentity(SDL_HIDAPI_Device *device)
1402
{
1403
SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;
1404
1405
if (ctx->m_bInputOnly) {
1406
if (SDL_IsJoystickGameCube(device->vendor_id, device->product_id)) {
1407
device->type = SDL_GAMEPAD_TYPE_STANDARD;
1408
}
1409
} else {
1410
char serial[18];
1411
1412
switch (ctx->m_eControllerType) {
1413
case k_eSwitchDeviceInfoControllerType_JoyConLeft:
1414
HIDAPI_SetDeviceName(device, "Nintendo Switch Joy-Con (L)");
1415
HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT);
1416
device->type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT;
1417
break;
1418
case k_eSwitchDeviceInfoControllerType_JoyConRight:
1419
HIDAPI_SetDeviceName(device, "Nintendo Switch Joy-Con (R)");
1420
HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT);
1421
device->type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT;
1422
break;
1423
case k_eSwitchDeviceInfoControllerType_ProController:
1424
case k_eSwitchDeviceInfoControllerType_LicProController:
1425
HIDAPI_SetDeviceName(device, "Nintendo Switch Pro Controller");
1426
HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SWITCH_PRO);
1427
device->type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO;
1428
break;
1429
case k_eSwitchDeviceInfoControllerType_HVCLeft:
1430
HIDAPI_SetDeviceName(device, "Nintendo HVC Controller (1)");
1431
device->type = SDL_GAMEPAD_TYPE_STANDARD;
1432
break;
1433
case k_eSwitchDeviceInfoControllerType_HVCRight:
1434
HIDAPI_SetDeviceName(device, "Nintendo HVC Controller (2)");
1435
device->type = SDL_GAMEPAD_TYPE_STANDARD;
1436
break;
1437
case k_eSwitchDeviceInfoControllerType_NESLeft:
1438
HIDAPI_SetDeviceName(device, "Nintendo NES Controller (L)");
1439
device->type = SDL_GAMEPAD_TYPE_STANDARD;
1440
break;
1441
case k_eSwitchDeviceInfoControllerType_NESRight:
1442
HIDAPI_SetDeviceName(device, "Nintendo NES Controller (R)");
1443
device->type = SDL_GAMEPAD_TYPE_STANDARD;
1444
break;
1445
case k_eSwitchDeviceInfoControllerType_SNES:
1446
HIDAPI_SetDeviceName(device, "Nintendo SNES Controller");
1447
HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SNES_CONTROLLER);
1448
device->type = SDL_GAMEPAD_TYPE_STANDARD;
1449
break;
1450
case k_eSwitchDeviceInfoControllerType_N64:
1451
HIDAPI_SetDeviceName(device, "Nintendo N64 Controller");
1452
HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_N64_CONTROLLER);
1453
device->type = SDL_GAMEPAD_TYPE_STANDARD;
1454
break;
1455
case k_eSwitchDeviceInfoControllerType_SEGA_Genesis:
1456
HIDAPI_SetDeviceName(device, "Nintendo SEGA Genesis Controller");
1457
HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SEGA_GENESIS_CONTROLLER);
1458
device->type = SDL_GAMEPAD_TYPE_STANDARD;
1459
break;
1460
case k_eSwitchDeviceInfoControllerType_Unknown:
1461
// We couldn't read the device info for this controller, might not be fully compliant
1462
if (device->vendor_id == USB_VENDOR_NINTENDO) {
1463
switch (device->product_id) {
1464
case USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT:
1465
ctx->m_eControllerType = k_eSwitchDeviceInfoControllerType_JoyConLeft;
1466
HIDAPI_SetDeviceName(device, "Nintendo Switch Joy-Con (L)");
1467
device->type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT;
1468
break;
1469
case USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT:
1470
ctx->m_eControllerType = k_eSwitchDeviceInfoControllerType_JoyConRight;
1471
HIDAPI_SetDeviceName(device, "Nintendo Switch Joy-Con (R)");
1472
device->type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT;
1473
break;
1474
case USB_PRODUCT_NINTENDO_SWITCH_PRO:
1475
ctx->m_eControllerType = k_eSwitchDeviceInfoControllerType_ProController;
1476
HIDAPI_SetDeviceName(device, "Nintendo Switch Pro Controller");
1477
device->type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO;
1478
break;
1479
default:
1480
break;
1481
}
1482
}
1483
return;
1484
default:
1485
device->type = SDL_GAMEPAD_TYPE_STANDARD;
1486
break;
1487
}
1488
device->guid.data[15] = ctx->m_eControllerType;
1489
1490
(void)SDL_snprintf(serial, sizeof(serial), "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x",
1491
ctx->m_rgucMACAddress[0],
1492
ctx->m_rgucMACAddress[1],
1493
ctx->m_rgucMACAddress[2],
1494
ctx->m_rgucMACAddress[3],
1495
ctx->m_rgucMACAddress[4],
1496
ctx->m_rgucMACAddress[5]);
1497
HIDAPI_SetDeviceSerial(device, serial);
1498
}
1499
}
1500
1501
static bool HIDAPI_DriverSwitch_InitDevice(SDL_HIDAPI_Device *device)
1502
{
1503
SDL_DriverSwitch_Context *ctx;
1504
1505
ctx = (SDL_DriverSwitch_Context *)SDL_calloc(1, sizeof(*ctx));
1506
if (!ctx) {
1507
return false;
1508
}
1509
ctx->device = device;
1510
device->context = ctx;
1511
1512
ctx->m_nMaxWriteAttempts = GetMaxWriteAttempts(device);
1513
ctx->m_bSyncWrite = true;
1514
1515
// Find out whether or not we can send output reports
1516
ctx->m_bInputOnly = SDL_IsJoystickNintendoSwitchProInputOnly(device->vendor_id, device->product_id);
1517
if (!ctx->m_bInputOnly) {
1518
// Initialize rumble data, important for reading device info on the MOBAPAD M073
1519
SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[0]);
1520
SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[1]);
1521
1522
BReadDeviceInfo(ctx);
1523
}
1524
UpdateDeviceIdentity(device);
1525
1526
// Prefer the USB device over the Bluetooth device
1527
if (device->is_bluetooth) {
1528
if (HIDAPI_HasConnectedUSBDevice(device->serial)) {
1529
return true;
1530
}
1531
} else {
1532
HIDAPI_DisconnectBluetoothDevice(device->serial);
1533
}
1534
return HIDAPI_JoystickConnected(device, NULL);
1535
}
1536
1537
static int HIDAPI_DriverSwitch_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
1538
{
1539
return -1;
1540
}
1541
1542
static void HIDAPI_DriverSwitch_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
1543
{
1544
SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;
1545
1546
if (!ctx->joystick) {
1547
return;
1548
}
1549
1550
ctx->m_nPlayerIndex = player_index;
1551
1552
UpdateSlotLED(ctx);
1553
}
1554
1555
static bool HIDAPI_DriverSwitch_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
1556
{
1557
SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;
1558
1559
SDL_AssertJoysticksLocked();
1560
1561
ctx->joystick = joystick;
1562
1563
ctx->m_bSyncWrite = true;
1564
1565
if (!ctx->m_bInputOnly) {
1566
#ifdef SDL_PLATFORM_MACOS
1567
// Wait for the OS to finish its handshake with the controller
1568
SDL_Delay(250);
1569
#endif
1570
GetInitialInputMode(ctx);
1571
ctx->m_nCurrentInputMode = ctx->m_nInitialInputMode;
1572
1573
// Initialize rumble data
1574
SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[0]);
1575
SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[1]);
1576
1577
if (!device->is_bluetooth) {
1578
if (!BTrySetupUSB(ctx)) {
1579
SDL_SetError("Couldn't setup USB mode");
1580
return false;
1581
}
1582
}
1583
1584
if (!LoadStickCalibration(ctx)) {
1585
SDL_SetError("Couldn't load stick calibration");
1586
return false;
1587
}
1588
1589
if (ctx->m_eControllerType != k_eSwitchDeviceInfoControllerType_HVCLeft &&
1590
ctx->m_eControllerType != k_eSwitchDeviceInfoControllerType_HVCRight &&
1591
ctx->m_eControllerType != k_eSwitchDeviceInfoControllerType_NESLeft &&
1592
ctx->m_eControllerType != k_eSwitchDeviceInfoControllerType_NESRight &&
1593
ctx->m_eControllerType != k_eSwitchDeviceInfoControllerType_SNES &&
1594
ctx->m_eControllerType != k_eSwitchDeviceInfoControllerType_N64 &&
1595
ctx->m_eControllerType != k_eSwitchDeviceInfoControllerType_SEGA_Genesis) {
1596
if (LoadIMUCalibration(ctx)) {
1597
ctx->m_bSensorsSupported = true;
1598
}
1599
}
1600
1601
// Enable vibration
1602
SetVibrationEnabled(ctx, 1);
1603
1604
// Set desired input mode
1605
SDL_AddHintCallback(SDL_HINT_JOYSTICK_ENHANCED_REPORTS,
1606
SDL_EnhancedReportsChanged, ctx);
1607
1608
// Start sending USB reports
1609
if (!device->is_bluetooth) {
1610
// ForceUSB doesn't generate an ACK, so don't wait for a reply
1611
if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_ForceUSB, NULL, 0, false)) {
1612
SDL_SetError("Couldn't start USB reports");
1613
return false;
1614
}
1615
}
1616
1617
// Set the LED state
1618
if (HasHomeLED(ctx)) {
1619
if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft ||
1620
ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {
1621
SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_JOYCON_HOME_LED,
1622
SDL_HomeLEDHintChanged, ctx);
1623
} else {
1624
SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED,
1625
SDL_HomeLEDHintChanged, ctx);
1626
}
1627
}
1628
}
1629
1630
if (AlwaysUsesLabels(device->vendor_id, device->product_id, ctx->m_eControllerType)) {
1631
ctx->m_bUseButtonLabels = true;
1632
}
1633
1634
// Initialize player index (needed for setting LEDs)
1635
ctx->m_nPlayerIndex = SDL_GetJoystickPlayerIndex(joystick);
1636
ctx->m_bPlayerLights = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED, true);
1637
UpdateSlotLED(ctx);
1638
1639
SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED,
1640
SDL_PlayerLEDHintChanged, ctx);
1641
1642
// Initialize the joystick capabilities
1643
joystick->nbuttons = SDL_GAMEPAD_NUM_SWITCH_BUTTONS;
1644
joystick->naxes = SDL_GAMEPAD_AXIS_COUNT;
1645
joystick->nhats = 1;
1646
1647
// Set up for input
1648
ctx->m_bSyncWrite = false;
1649
ctx->m_ulLastIMUReset = ctx->m_ulLastInput = SDL_GetTicks();
1650
ctx->m_ulIMUUpdateIntervalNS = SDL_MS_TO_NS(5); // Start off at 5 ms update rate
1651
1652
// Set up for vertical mode
1653
ctx->m_bVerticalMode = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS, false);
1654
1655
return true;
1656
}
1657
1658
static bool HIDAPI_DriverSwitch_ActuallyRumbleJoystick(SDL_DriverSwitch_Context *ctx, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
1659
{
1660
/* Experimentally determined rumble values. These will only matter on some controllers as tested ones
1661
* seem to disregard these and just use any non-zero rumble values as a binary flag for constant rumble
1662
*
1663
* More information about these values can be found here:
1664
* https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md
1665
*/
1666
const Uint16 k_usHighFreq = 0x0074;
1667
const Uint8 k_ucHighFreqAmp = EncodeRumbleHighAmplitude(high_frequency_rumble);
1668
const Uint8 k_ucLowFreq = 0x3D;
1669
const Uint16 k_usLowFreqAmp = EncodeRumbleLowAmplitude(low_frequency_rumble);
1670
1671
if (low_frequency_rumble || high_frequency_rumble) {
1672
EncodeRumble(&ctx->m_RumblePacket.rumbleData[0], k_usHighFreq, k_ucHighFreqAmp, k_ucLowFreq, k_usLowFreqAmp);
1673
EncodeRumble(&ctx->m_RumblePacket.rumbleData[1], k_usHighFreq, k_ucHighFreqAmp, k_ucLowFreq, k_usLowFreqAmp);
1674
} else {
1675
SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[0]);
1676
SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[1]);
1677
}
1678
1679
ctx->m_bRumbleActive = (low_frequency_rumble || high_frequency_rumble);
1680
1681
if (!WriteRumble(ctx)) {
1682
return SDL_SetError("Couldn't send rumble packet");
1683
}
1684
return true;
1685
}
1686
1687
static bool HIDAPI_DriverSwitch_SendPendingRumble(SDL_DriverSwitch_Context *ctx)
1688
{
1689
if (SDL_GetTicks() < (ctx->m_ulRumbleSent + RUMBLE_WRITE_FREQUENCY_MS)) {
1690
return true;
1691
}
1692
1693
if (ctx->m_bRumblePending) {
1694
Uint16 low_frequency_rumble = (Uint16)(ctx->m_unRumblePending >> 16);
1695
Uint16 high_frequency_rumble = (Uint16)ctx->m_unRumblePending;
1696
1697
#ifdef DEBUG_RUMBLE
1698
SDL_Log("Sent pending rumble %d/%d, %d ms after previous rumble", low_frequency_rumble, high_frequency_rumble, SDL_GetTicks() - ctx->m_ulRumbleSent);
1699
#endif
1700
ctx->m_bRumblePending = false;
1701
ctx->m_unRumblePending = 0;
1702
1703
return HIDAPI_DriverSwitch_ActuallyRumbleJoystick(ctx, low_frequency_rumble, high_frequency_rumble);
1704
}
1705
1706
if (ctx->m_bRumbleZeroPending) {
1707
ctx->m_bRumbleZeroPending = false;
1708
1709
#ifdef DEBUG_RUMBLE
1710
SDL_Log("Sent pending zero rumble, %d ms after previous rumble", SDL_GetTicks() - ctx->m_ulRumbleSent);
1711
#endif
1712
return HIDAPI_DriverSwitch_ActuallyRumbleJoystick(ctx, 0, 0);
1713
}
1714
1715
return true;
1716
}
1717
1718
static bool HIDAPI_DriverSwitch_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
1719
{
1720
SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;
1721
1722
if (ctx->m_bInputOnly) {
1723
return SDL_Unsupported();
1724
}
1725
1726
if (device->parent) {
1727
if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft) {
1728
// Just handle low frequency rumble
1729
high_frequency_rumble = 0;
1730
} else if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {
1731
// Just handle high frequency rumble
1732
low_frequency_rumble = 0;
1733
}
1734
}
1735
1736
if (ctx->m_bRumblePending) {
1737
if (!HIDAPI_DriverSwitch_SendPendingRumble(ctx)) {
1738
return false;
1739
}
1740
}
1741
1742
if (SDL_GetTicks() < (ctx->m_ulRumbleSent + RUMBLE_WRITE_FREQUENCY_MS)) {
1743
if (low_frequency_rumble || high_frequency_rumble) {
1744
Uint32 unRumblePending = ((Uint32)low_frequency_rumble << 16) | high_frequency_rumble;
1745
1746
// Keep the highest rumble intensity in the given interval
1747
if (unRumblePending > ctx->m_unRumblePending) {
1748
ctx->m_unRumblePending = unRumblePending;
1749
}
1750
ctx->m_bRumblePending = true;
1751
ctx->m_bRumbleZeroPending = false;
1752
} else {
1753
// When rumble is complete, turn it off
1754
ctx->m_bRumbleZeroPending = true;
1755
}
1756
return true;
1757
}
1758
1759
#ifdef DEBUG_RUMBLE
1760
SDL_Log("Sent rumble %d/%d", low_frequency_rumble, high_frequency_rumble);
1761
#endif
1762
1763
return HIDAPI_DriverSwitch_ActuallyRumbleJoystick(ctx, low_frequency_rumble, high_frequency_rumble);
1764
}
1765
1766
static bool HIDAPI_DriverSwitch_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
1767
{
1768
return SDL_Unsupported();
1769
}
1770
1771
static Uint32 HIDAPI_DriverSwitch_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
1772
{
1773
SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;
1774
Uint32 result = 0;
1775
1776
if (ctx->m_bPlayerLights && !ctx->m_bInputOnly) {
1777
result |= SDL_JOYSTICK_CAP_PLAYER_LED;
1778
}
1779
1780
if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_ProController && !ctx->m_bInputOnly) {
1781
// Doesn't have an RGB LED, so don't return SDL_JOYSTICK_CAP_RGB_LED here
1782
result |= SDL_JOYSTICK_CAP_RUMBLE;
1783
// But has the HOME LED, so
1784
result |= SDL_JOYSTICK_CAP_MONO_LED;
1785
} else if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft ||
1786
ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {
1787
result |= SDL_JOYSTICK_CAP_RUMBLE;
1788
if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {
1789
result |= SDL_JOYSTICK_CAP_MONO_LED; // Those types of controllers also have the Home LED.
1790
}
1791
}
1792
1793
return result;
1794
}
1795
1796
static bool HIDAPI_DriverSwitch_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
1797
{
1798
SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;
1799
int value = (int)((SDL_max(red, SDL_max(green, blue)) / 255.0f) * 100.0f); // The colors are received between 0-255 and we need them to be 0-100.
1800
return SetHomeLED(ctx, (Uint8)value);
1801
}
1802
1803
static bool HIDAPI_DriverSwitch_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *data, int size)
1804
{
1805
SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;
1806
1807
if (size == sizeof(SwitchCommonOutputPacket_t)) {
1808
const SwitchCommonOutputPacket_t *packet = (SwitchCommonOutputPacket_t *)data;
1809
1810
if (packet->ucPacketType != k_eSwitchOutputReportIDs_Rumble) {
1811
return SDL_SetError("Unknown Nintendo Switch Pro effect type");
1812
}
1813
1814
SDL_copyp(&ctx->m_RumblePacket.rumbleData[0], &packet->rumbleData[0]);
1815
SDL_copyp(&ctx->m_RumblePacket.rumbleData[1], &packet->rumbleData[1]);
1816
if (!WriteRumble(ctx)) {
1817
return false;
1818
}
1819
1820
// This overwrites any internal rumble
1821
ctx->m_bRumblePending = false;
1822
ctx->m_bRumbleZeroPending = false;
1823
return true;
1824
} else if (size >= 2 && size <= 256) {
1825
const Uint8 *payload = (const Uint8 *)data;
1826
ESwitchSubcommandIDs cmd = (ESwitchSubcommandIDs)payload[0];
1827
1828
if (cmd == k_eSwitchSubcommandIDs_SetInputReportMode && !device->is_bluetooth) {
1829
// Going into simple mode over USB disables input reports, so don't do that
1830
return true;
1831
}
1832
if (cmd == k_eSwitchSubcommandIDs_SetHomeLight && !HasHomeLED(ctx)) {
1833
// Setting the home LED when it's not supported can cause the controller to reset
1834
return true;
1835
}
1836
1837
if (!WriteSubcommand(ctx, cmd, &payload[1], (Uint8)(size - 1), NULL)) {
1838
return false;
1839
}
1840
return true;
1841
}
1842
return SDL_Unsupported();
1843
}
1844
1845
static bool HIDAPI_DriverSwitch_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, bool enabled)
1846
{
1847
SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;
1848
1849
UpdateEnhancedModeOnApplicationUsage(ctx);
1850
1851
if (!ctx->m_bSensorsSupported || (enabled && !ctx->m_bEnhancedMode)) {
1852
return SDL_Unsupported();
1853
}
1854
1855
ctx->m_bReportSensors = enabled;
1856
ctx->m_unIMUSamples = 0;
1857
ctx->m_ulIMUSampleTimestampNS = SDL_GetTicksNS();
1858
1859
UpdateInputMode(ctx);
1860
SetIMUEnabled(ctx, enabled);
1861
1862
return true;
1863
}
1864
1865
static void HandleInputOnlyControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchInputOnlyControllerStatePacket_t *packet)
1866
{
1867
Sint16 axis;
1868
Uint64 timestamp = SDL_GetTicksNS();
1869
1870
if (packet->rgucButtons[0] != ctx->m_lastInputOnlyState.rgucButtons[0]) {
1871
Uint8 data = packet->rgucButtons[0];
1872
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_SOUTH), ((data & 0x02) != 0));
1873
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_EAST), ((data & 0x04) != 0));
1874
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_WEST), ((data & 0x01) != 0));
1875
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_NORTH), ((data & 0x08) != 0));
1876
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x10) != 0));
1877
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x20) != 0));
1878
}
1879
1880
if (packet->rgucButtons[1] != ctx->m_lastInputOnlyState.rgucButtons[1]) {
1881
Uint8 data = packet->rgucButtons[1];
1882
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data & 0x01) != 0));
1883
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x02) != 0));
1884
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x04) != 0));
1885
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data & 0x08) != 0));
1886
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x10) != 0));
1887
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_SHARE, ((data & 0x20) != 0));
1888
}
1889
1890
if (packet->ucStickHat != ctx->m_lastInputOnlyState.ucStickHat) {
1891
Uint8 hat;
1892
1893
switch (packet->ucStickHat) {
1894
case 0:
1895
hat = SDL_HAT_UP;
1896
break;
1897
case 1:
1898
hat = SDL_HAT_RIGHTUP;
1899
break;
1900
case 2:
1901
hat = SDL_HAT_RIGHT;
1902
break;
1903
case 3:
1904
hat = SDL_HAT_RIGHTDOWN;
1905
break;
1906
case 4:
1907
hat = SDL_HAT_DOWN;
1908
break;
1909
case 5:
1910
hat = SDL_HAT_LEFTDOWN;
1911
break;
1912
case 6:
1913
hat = SDL_HAT_LEFT;
1914
break;
1915
case 7:
1916
hat = SDL_HAT_LEFTUP;
1917
break;
1918
default:
1919
hat = SDL_HAT_CENTERED;
1920
break;
1921
}
1922
SDL_SendJoystickHat(timestamp, joystick, 0, hat);
1923
}
1924
1925
axis = (packet->rgucButtons[0] & 0x40) ? 32767 : -32768;
1926
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis);
1927
1928
axis = (packet->rgucButtons[0] & 0x80) ? 32767 : -32768;
1929
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis);
1930
1931
if (packet->rgucJoystickLeft[0] != ctx->m_lastInputOnlyState.rgucJoystickLeft[0]) {
1932
axis = (Sint16)HIDAPI_RemapVal(packet->rgucJoystickLeft[0], SDL_MIN_UINT8, SDL_MAX_UINT8, SDL_MIN_SINT16, SDL_MAX_SINT16);
1933
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis);
1934
}
1935
1936
if (packet->rgucJoystickLeft[1] != ctx->m_lastInputOnlyState.rgucJoystickLeft[1]) {
1937
axis = (Sint16)HIDAPI_RemapVal(packet->rgucJoystickLeft[1], SDL_MIN_UINT8, SDL_MAX_UINT8, SDL_MIN_SINT16, SDL_MAX_SINT16);
1938
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, axis);
1939
}
1940
1941
if (packet->rgucJoystickRight[0] != ctx->m_lastInputOnlyState.rgucJoystickRight[0]) {
1942
axis = (Sint16)HIDAPI_RemapVal(packet->rgucJoystickRight[0], SDL_MIN_UINT8, SDL_MAX_UINT8, SDL_MIN_SINT16, SDL_MAX_SINT16);
1943
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis);
1944
}
1945
1946
if (packet->rgucJoystickRight[1] != ctx->m_lastInputOnlyState.rgucJoystickRight[1]) {
1947
axis = (Sint16)HIDAPI_RemapVal(packet->rgucJoystickRight[1], SDL_MIN_UINT8, SDL_MAX_UINT8, SDL_MIN_SINT16, SDL_MAX_SINT16);
1948
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis);
1949
}
1950
1951
ctx->m_lastInputOnlyState = *packet;
1952
}
1953
1954
static void HandleCombinedSimpleControllerStateL(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchSimpleStatePacket_t *packet)
1955
{
1956
if (packet->rgucButtons[0] != ctx->m_lastSimpleState.rgucButtons[0]) {
1957
Uint8 data = packet->rgucButtons[0];
1958
Uint8 hat = 0;
1959
1960
if (data & 0x01) {
1961
hat |= SDL_HAT_LEFT;
1962
}
1963
if (data & 0x02) {
1964
hat |= SDL_HAT_DOWN;
1965
}
1966
if (data & 0x04) {
1967
hat |= SDL_HAT_UP;
1968
}
1969
if (data & 0x08) {
1970
hat |= SDL_HAT_RIGHT;
1971
}
1972
SDL_SendJoystickHat(timestamp, joystick, 0, hat);
1973
1974
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE1, ((data & 0x10) != 0));
1975
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE2, ((data & 0x20) != 0));
1976
}
1977
1978
if (packet->rgucButtons[1] != ctx->m_lastSimpleState.rgucButtons[1]) {
1979
Uint8 data = packet->rgucButtons[1];
1980
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data & 0x01) != 0));
1981
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x04) != 0));
1982
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_SHARE, ((data & 0x20) != 0));
1983
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x40) != 0));
1984
}
1985
1986
Sint16 axis = (packet->rgucButtons[1] & 0x80) ? 32767 : -32768;
1987
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis);
1988
1989
if (packet->ucStickHat != ctx->m_lastSimpleState.ucStickHat) {
1990
switch (packet->ucStickHat) {
1991
case 0:
1992
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MAX);
1993
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, 0);
1994
break;
1995
case 1:
1996
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MAX);
1997
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MAX);
1998
break;
1999
case 2:
2000
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, 0);
2001
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MAX);
2002
break;
2003
case 3:
2004
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MIN);
2005
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MAX);
2006
break;
2007
case 4:
2008
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MIN);
2009
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, 0);
2010
break;
2011
case 5:
2012
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MIN);
2013
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MIN);
2014
break;
2015
case 6:
2016
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, 0);
2017
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MIN);
2018
break;
2019
case 7:
2020
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MAX);
2021
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MIN);
2022
break;
2023
default:
2024
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, 0);
2025
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, 0);
2026
break;
2027
}
2028
}
2029
}
2030
2031
static void HandleCombinedSimpleControllerStateR(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchSimpleStatePacket_t *packet)
2032
{
2033
if (packet->rgucButtons[0] != ctx->m_lastSimpleState.rgucButtons[0]) {
2034
Uint8 data = packet->rgucButtons[0];
2035
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_EAST), ((data & 0x01) != 0));
2036
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_NORTH), ((data & 0x02) != 0));
2037
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_SOUTH), ((data & 0x04) != 0));
2038
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_WEST), ((data & 0x08) != 0));
2039
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE2, ((data & 0x10) != 0));
2040
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE1, ((data & 0x20) != 0));
2041
}
2042
2043
if (packet->rgucButtons[1] != ctx->m_lastSimpleState.rgucButtons[1]) {
2044
Uint8 data = packet->rgucButtons[1];
2045
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x02) != 0));
2046
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data & 0x08) != 0));
2047
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x10) != 0));
2048
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x40) != 0));
2049
}
2050
2051
Sint16 axis = (packet->rgucButtons[1] & 0x80) ? 32767 : -32768;
2052
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis);
2053
2054
if (packet->ucStickHat != ctx->m_lastSimpleState.ucStickHat) {
2055
switch (packet->ucStickHat) {
2056
case 0:
2057
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, SDL_JOYSTICK_AXIS_MIN);
2058
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, 0);
2059
break;
2060
case 1:
2061
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, SDL_JOYSTICK_AXIS_MIN);
2062
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, SDL_JOYSTICK_AXIS_MIN);
2063
break;
2064
case 2:
2065
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, 0);
2066
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, SDL_JOYSTICK_AXIS_MIN);
2067
break;
2068
case 3:
2069
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, SDL_JOYSTICK_AXIS_MAX);
2070
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, SDL_JOYSTICK_AXIS_MIN);
2071
break;
2072
case 4:
2073
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, SDL_JOYSTICK_AXIS_MAX);
2074
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, 0);
2075
break;
2076
case 5:
2077
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, SDL_JOYSTICK_AXIS_MAX);
2078
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, SDL_JOYSTICK_AXIS_MAX);
2079
break;
2080
case 6:
2081
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, 0);
2082
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, SDL_JOYSTICK_AXIS_MAX);
2083
break;
2084
case 7:
2085
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, SDL_JOYSTICK_AXIS_MIN);
2086
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, SDL_JOYSTICK_AXIS_MAX);
2087
break;
2088
default:
2089
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, 0);
2090
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, 0);
2091
break;
2092
}
2093
}
2094
}
2095
2096
static void HandleMiniSimpleControllerStateL(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchSimpleStatePacket_t *packet)
2097
{
2098
if (packet->rgucButtons[0] != ctx->m_lastSimpleState.rgucButtons[0]) {
2099
Uint8 data = packet->rgucButtons[0];
2100
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_SOUTH), ((data & 0x01) != 0));
2101
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_EAST), ((data & 0x02) != 0));
2102
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_WEST), ((data & 0x04) != 0));
2103
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_NORTH), ((data & 0x08) != 0));
2104
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x10) != 0));
2105
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x20) != 0));
2106
}
2107
2108
if (packet->rgucButtons[1] != ctx->m_lastSimpleState.rgucButtons[1]) {
2109
Uint8 data = packet->rgucButtons[1];
2110
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x01) != 0));
2111
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x04) != 0));
2112
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x20) != 0));
2113
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE1, ((data & 0x40) != 0));
2114
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE2, ((data & 0x80) != 0));
2115
}
2116
2117
if (packet->ucStickHat != ctx->m_lastSimpleState.ucStickHat) {
2118
switch (packet->ucStickHat) {
2119
case 0:
2120
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, 0);
2121
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MIN);
2122
break;
2123
case 1:
2124
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MAX);
2125
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MIN);
2126
break;
2127
case 2:
2128
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MAX);
2129
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, 0);
2130
break;
2131
case 3:
2132
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MAX);
2133
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MAX);
2134
break;
2135
case 4:
2136
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, 0);
2137
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MAX);
2138
break;
2139
case 5:
2140
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MIN);
2141
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MAX);
2142
break;
2143
case 6:
2144
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MIN);
2145
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, 0);
2146
break;
2147
case 7:
2148
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MIN);
2149
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MIN);
2150
break;
2151
default:
2152
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, 0);
2153
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, 0);
2154
break;
2155
}
2156
}
2157
}
2158
2159
static void HandleMiniSimpleControllerStateR(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchSimpleStatePacket_t *packet)
2160
{
2161
if (packet->rgucButtons[0] != ctx->m_lastSimpleState.rgucButtons[0]) {
2162
Uint8 data = packet->rgucButtons[0];
2163
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_SOUTH), ((data & 0x01) != 0));
2164
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_EAST), ((data & 0x02) != 0));
2165
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_WEST), ((data & 0x04) != 0));
2166
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_NORTH), ((data & 0x08) != 0));
2167
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x10) != 0));
2168
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x20) != 0));
2169
}
2170
2171
if (packet->rgucButtons[1] != ctx->m_lastSimpleState.rgucButtons[1]) {
2172
Uint8 data = packet->rgucButtons[1];
2173
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x02) != 0));
2174
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x08) != 0));
2175
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x10) != 0));
2176
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_SHARE, ((data & 0x20) != 0));
2177
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE1, ((data & 0x40) != 0));
2178
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE2, ((data & 0x80) != 0));
2179
}
2180
2181
if (packet->ucStickHat != ctx->m_lastSimpleState.ucStickHat) {
2182
switch (packet->ucStickHat) {
2183
case 0:
2184
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, 0);
2185
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MIN);
2186
break;
2187
case 1:
2188
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MAX);
2189
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MIN);
2190
break;
2191
case 2:
2192
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MAX);
2193
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, 0);
2194
break;
2195
case 3:
2196
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MAX);
2197
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MAX);
2198
break;
2199
case 4:
2200
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, 0);
2201
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MAX);
2202
break;
2203
case 5:
2204
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MIN);
2205
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MAX);
2206
break;
2207
case 6:
2208
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MIN);
2209
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, 0);
2210
break;
2211
case 7:
2212
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MIN);
2213
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MIN);
2214
break;
2215
default:
2216
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, 0);
2217
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, 0);
2218
break;
2219
}
2220
}
2221
}
2222
2223
static void HandleSimpleControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchSimpleStatePacket_t *packet)
2224
{
2225
Uint64 timestamp = SDL_GetTicksNS();
2226
2227
if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft) {
2228
if (ctx->device->parent || ctx->m_bVerticalMode) {
2229
HandleCombinedSimpleControllerStateL(timestamp, joystick, ctx, packet);
2230
} else {
2231
HandleMiniSimpleControllerStateL(timestamp, joystick, ctx, packet);
2232
}
2233
} else if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {
2234
if (ctx->device->parent || ctx->m_bVerticalMode) {
2235
HandleCombinedSimpleControllerStateR(timestamp, joystick, ctx, packet);
2236
} else {
2237
HandleMiniSimpleControllerStateR(timestamp, joystick, ctx, packet);
2238
}
2239
} else {
2240
Sint16 axis;
2241
2242
if (packet->rgucButtons[0] != ctx->m_lastSimpleState.rgucButtons[0]) {
2243
Uint8 data = packet->rgucButtons[0];
2244
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_SOUTH), ((data & 0x01) != 0));
2245
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_EAST), ((data & 0x02) != 0));
2246
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_WEST), ((data & 0x04) != 0));
2247
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_NORTH), ((data & 0x08) != 0));
2248
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x10) != 0));
2249
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x20) != 0));
2250
}
2251
2252
if (packet->rgucButtons[1] != ctx->m_lastSimpleState.rgucButtons[1]) {
2253
Uint8 data = packet->rgucButtons[1];
2254
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data & 0x01) != 0));
2255
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x02) != 0));
2256
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x04) != 0));
2257
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data & 0x08) != 0));
2258
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x10) != 0));
2259
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_SHARE, ((data & 0x20) != 0));
2260
}
2261
2262
if (packet->ucStickHat != ctx->m_lastSimpleState.ucStickHat) {
2263
Uint8 hat;
2264
2265
switch (packet->ucStickHat) {
2266
case 0:
2267
hat = SDL_HAT_UP;
2268
break;
2269
case 1:
2270
hat = SDL_HAT_RIGHTUP;
2271
break;
2272
case 2:
2273
hat = SDL_HAT_RIGHT;
2274
break;
2275
case 3:
2276
hat = SDL_HAT_RIGHTDOWN;
2277
break;
2278
case 4:
2279
hat = SDL_HAT_DOWN;
2280
break;
2281
case 5:
2282
hat = SDL_HAT_LEFTDOWN;
2283
break;
2284
case 6:
2285
hat = SDL_HAT_LEFT;
2286
break;
2287
case 7:
2288
hat = SDL_HAT_LEFTUP;
2289
break;
2290
default:
2291
hat = SDL_HAT_CENTERED;
2292
break;
2293
}
2294
SDL_SendJoystickHat(timestamp, joystick, 0, hat);
2295
}
2296
2297
axis = (packet->rgucButtons[0] & 0x40) ? 32767 : -32768;
2298
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis);
2299
2300
axis = ((packet->rgucButtons[0] & 0x80) || (packet->rgucButtons[1] & 0x80)) ? 32767 : -32768;
2301
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis);
2302
2303
axis = ApplySimpleStickCalibration(ctx, 0, 0, packet->sJoystickLeft[0]);
2304
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis);
2305
2306
axis = ApplySimpleStickCalibration(ctx, 0, 1, packet->sJoystickLeft[1]);
2307
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, axis);
2308
2309
axis = ApplySimpleStickCalibration(ctx, 1, 0, packet->sJoystickRight[0]);
2310
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis);
2311
2312
axis = ApplySimpleStickCalibration(ctx, 1, 1, packet->sJoystickRight[1]);
2313
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis);
2314
}
2315
2316
ctx->m_lastSimpleState = *packet;
2317
}
2318
2319
static void SendSensorUpdate(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SDL_SensorType type, Uint64 sensor_timestamp, const Sint16 *values)
2320
{
2321
float data[3];
2322
2323
/* Note the order of components has been shuffled to match PlayStation controllers,
2324
* since that's our de facto standard from already supporting those controllers, and
2325
* users will want consistent axis mappings across devices.
2326
*/
2327
if (type == SDL_SENSOR_GYRO || type == SDL_SENSOR_GYRO_L || type == SDL_SENSOR_GYRO_R) {
2328
data[0] = -(ctx->m_IMUScaleData.fGyroScaleY * (float)values[1]);
2329
data[1] = ctx->m_IMUScaleData.fGyroScaleZ * (float)values[2];
2330
data[2] = -(ctx->m_IMUScaleData.fGyroScaleX * (float)values[0]);
2331
} else {
2332
data[0] = -(ctx->m_IMUScaleData.fAccelScaleY * (float)values[1]);
2333
data[1] = ctx->m_IMUScaleData.fAccelScaleZ * (float)values[2];
2334
data[2] = -(ctx->m_IMUScaleData.fAccelScaleX * (float)values[0]);
2335
}
2336
2337
// Right Joy-Con flips some axes, so let's flip them back for consistency
2338
if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {
2339
data[0] = -data[0];
2340
data[1] = -data[1];
2341
}
2342
2343
if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft &&
2344
!ctx->device->parent && !ctx->m_bVerticalMode) {
2345
// Mini-gamepad mode, swap some axes around
2346
float tmp = data[2];
2347
data[2] = -data[0];
2348
data[0] = tmp;
2349
}
2350
2351
if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight &&
2352
!ctx->device->parent && !ctx->m_bVerticalMode) {
2353
// Mini-gamepad mode, swap some axes around
2354
float tmp = data[2];
2355
data[2] = data[0];
2356
data[0] = -tmp;
2357
}
2358
2359
SDL_SendJoystickSensor(timestamp, joystick, type, sensor_timestamp, data, 3);
2360
}
2361
2362
static void HandleCombinedControllerStateL(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchStatePacket_t *packet)
2363
{
2364
Sint16 axis;
2365
2366
if (packet->controllerState.rgucButtons[1] != ctx->m_lastFullState.controllerState.rgucButtons[1]) {
2367
Uint8 data = packet->controllerState.rgucButtons[1];
2368
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data & 0x01) != 0));
2369
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x08) != 0));
2370
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_SHARE, ((data & 0x20) != 0));
2371
}
2372
2373
if (packet->controllerState.rgucButtons[2] != ctx->m_lastFullState.controllerState.rgucButtons[2]) {
2374
Uint8 data = packet->controllerState.rgucButtons[2];
2375
Uint8 hat = 0;
2376
2377
if (data & 0x01) {
2378
hat |= SDL_HAT_DOWN;
2379
}
2380
if (data & 0x02) {
2381
hat |= SDL_HAT_UP;
2382
}
2383
if (data & 0x04) {
2384
hat |= SDL_HAT_RIGHT;
2385
}
2386
if (data & 0x08) {
2387
hat |= SDL_HAT_LEFT;
2388
}
2389
SDL_SendJoystickHat(timestamp, joystick, 0, hat);
2390
2391
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE2, ((data & 0x10) != 0));
2392
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE1, ((data & 0x20) != 0));
2393
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x40) != 0));
2394
axis = (data & 0x80) ? 32767 : -32768;
2395
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis);
2396
}
2397
2398
axis = packet->controllerState.rgucJoystickLeft[0] | ((packet->controllerState.rgucJoystickLeft[1] & 0xF) << 8);
2399
axis = ApplyStickCalibration(ctx, 0, 0, axis);
2400
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis);
2401
2402
axis = ((packet->controllerState.rgucJoystickLeft[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickLeft[2] << 4);
2403
axis = ApplyStickCalibration(ctx, 0, 1, axis);
2404
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, ~axis);
2405
}
2406
2407
static void HandleCombinedControllerStateR(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchStatePacket_t *packet)
2408
{
2409
Sint16 axis;
2410
2411
if (packet->controllerState.rgucButtons[0] != ctx->m_lastFullState.controllerState.rgucButtons[0]) {
2412
Uint8 data = packet->controllerState.rgucButtons[0];
2413
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_SOUTH), ((data & 0x04) != 0));
2414
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_EAST), ((data & 0x08) != 0));
2415
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_WEST), ((data & 0x01) != 0));
2416
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_NORTH), ((data & 0x02) != 0));
2417
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE1, ((data & 0x10) != 0));
2418
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE2, ((data & 0x20) != 0));
2419
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x40) != 0));
2420
axis = (data & 0x80) ? 32767 : -32768;
2421
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis);
2422
}
2423
2424
if (packet->controllerState.rgucButtons[1] != ctx->m_lastFullState.controllerState.rgucButtons[1]) {
2425
Uint8 data = packet->controllerState.rgucButtons[1];
2426
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x02) != 0));
2427
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data & 0x04) != 0));
2428
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x10) != 0));
2429
}
2430
2431
axis = packet->controllerState.rgucJoystickRight[0] | ((packet->controllerState.rgucJoystickRight[1] & 0xF) << 8);
2432
axis = ApplyStickCalibration(ctx, 1, 0, axis);
2433
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis);
2434
2435
axis = ((packet->controllerState.rgucJoystickRight[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickRight[2] << 4);
2436
axis = ApplyStickCalibration(ctx, 1, 1, axis);
2437
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, ~axis);
2438
}
2439
2440
static void HandleMiniControllerStateL(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchStatePacket_t *packet)
2441
{
2442
Sint16 axis;
2443
2444
if (packet->controllerState.rgucButtons[1] != ctx->m_lastFullState.controllerState.rgucButtons[1]) {
2445
Uint8 data = packet->controllerState.rgucButtons[1];
2446
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x01) != 0));
2447
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x08) != 0));
2448
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x20) != 0));
2449
}
2450
2451
if (packet->controllerState.rgucButtons[2] != ctx->m_lastFullState.controllerState.rgucButtons[2]) {
2452
Uint8 data = packet->controllerState.rgucButtons[2];
2453
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_SOUTH), ((data & 0x08) != 0));
2454
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_EAST), ((data & 0x01) != 0));
2455
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_WEST), ((data & 0x02) != 0));
2456
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_NORTH), ((data & 0x04) != 0));
2457
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x10) != 0));
2458
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x20) != 0));
2459
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE1, ((data & 0x40) != 0));
2460
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE2, ((data & 0x80) != 0));
2461
}
2462
2463
axis = packet->controllerState.rgucJoystickLeft[0] | ((packet->controllerState.rgucJoystickLeft[1] & 0xF) << 8);
2464
axis = ApplyStickCalibration(ctx, 0, 0, axis);
2465
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, ~axis);
2466
2467
axis = ((packet->controllerState.rgucJoystickLeft[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickLeft[2] << 4);
2468
axis = ApplyStickCalibration(ctx, 0, 1, axis);
2469
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, ~axis);
2470
}
2471
2472
static void HandleMiniControllerStateR(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchStatePacket_t *packet)
2473
{
2474
Sint16 axis;
2475
2476
if (packet->controllerState.rgucButtons[0] != ctx->m_lastFullState.controllerState.rgucButtons[0]) {
2477
Uint8 data = packet->controllerState.rgucButtons[0];
2478
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_SOUTH), ((data & 0x08) != 0));
2479
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_EAST), ((data & 0x02) != 0));
2480
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_WEST), ((data & 0x04) != 0));
2481
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_NORTH), ((data & 0x01) != 0));
2482
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x10) != 0));
2483
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x20) != 0));
2484
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE1, ((data & 0x40) != 0));
2485
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE2, ((data & 0x80) != 0));
2486
}
2487
2488
if (packet->controllerState.rgucButtons[1] != ctx->m_lastFullState.controllerState.rgucButtons[1]) {
2489
Uint8 data = packet->controllerState.rgucButtons[1];
2490
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x02) != 0));
2491
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x04) != 0));
2492
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x10) != 0));
2493
}
2494
2495
axis = packet->controllerState.rgucJoystickRight[0] | ((packet->controllerState.rgucJoystickRight[1] & 0xF) << 8);
2496
axis = ApplyStickCalibration(ctx, 1, 0, axis);
2497
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, axis);
2498
2499
axis = ((packet->controllerState.rgucJoystickRight[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickRight[2] << 4);
2500
axis = ApplyStickCalibration(ctx, 1, 1, axis);
2501
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis);
2502
}
2503
2504
static void HandleFullControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchStatePacket_t *packet) SDL_NO_THREAD_SAFETY_ANALYSIS // We unlock and lock the device lock to be able to change IMU state
2505
{
2506
Uint64 timestamp = SDL_GetTicksNS();
2507
2508
if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft) {
2509
if (ctx->device->parent || ctx->m_bVerticalMode) {
2510
HandleCombinedControllerStateL(timestamp, joystick, ctx, packet);
2511
} else {
2512
HandleMiniControllerStateL(timestamp, joystick, ctx, packet);
2513
}
2514
} else if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {
2515
if (ctx->device->parent || ctx->m_bVerticalMode) {
2516
HandleCombinedControllerStateR(timestamp, joystick, ctx, packet);
2517
} else {
2518
HandleMiniControllerStateR(timestamp, joystick, ctx, packet);
2519
}
2520
} else {
2521
Sint16 axis;
2522
2523
if (packet->controllerState.rgucButtons[0] != ctx->m_lastFullState.controllerState.rgucButtons[0]) {
2524
Uint8 data = packet->controllerState.rgucButtons[0];
2525
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_SOUTH), ((data & 0x04) != 0));
2526
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_EAST), ((data & 0x08) != 0));
2527
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_WEST), ((data & 0x01) != 0));
2528
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_NORTH), ((data & 0x02) != 0));
2529
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x40) != 0));
2530
}
2531
2532
if (packet->controllerState.rgucButtons[1] != ctx->m_lastFullState.controllerState.rgucButtons[1]) {
2533
Uint8 data = packet->controllerState.rgucButtons[1];
2534
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data & 0x01) != 0));
2535
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x02) != 0));
2536
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data & 0x04) != 0));
2537
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x08) != 0));
2538
2539
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x10) != 0));
2540
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_SHARE, ((data & 0x20) != 0));
2541
}
2542
2543
if (packet->controllerState.rgucButtons[2] != ctx->m_lastFullState.controllerState.rgucButtons[2]) {
2544
Uint8 data = packet->controllerState.rgucButtons[2];
2545
Uint8 hat = 0;
2546
2547
if (data & 0x01) {
2548
hat |= SDL_HAT_DOWN;
2549
}
2550
if (data & 0x02) {
2551
hat |= SDL_HAT_UP;
2552
}
2553
if (data & 0x04) {
2554
hat |= SDL_HAT_RIGHT;
2555
}
2556
if (data & 0x08) {
2557
hat |= SDL_HAT_LEFT;
2558
}
2559
SDL_SendJoystickHat(timestamp, joystick, 0, hat);
2560
2561
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x40) != 0));
2562
}
2563
2564
axis = (packet->controllerState.rgucButtons[0] & 0x80) ? 32767 : -32768;
2565
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis);
2566
2567
axis = (packet->controllerState.rgucButtons[2] & 0x80) ? 32767 : -32768;
2568
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis);
2569
2570
axis = packet->controllerState.rgucJoystickLeft[0] | ((packet->controllerState.rgucJoystickLeft[1] & 0xF) << 8);
2571
axis = ApplyStickCalibration(ctx, 0, 0, axis);
2572
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis);
2573
2574
axis = ((packet->controllerState.rgucJoystickLeft[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickLeft[2] << 4);
2575
axis = ApplyStickCalibration(ctx, 0, 1, axis);
2576
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, ~axis);
2577
2578
axis = packet->controllerState.rgucJoystickRight[0] | ((packet->controllerState.rgucJoystickRight[1] & 0xF) << 8);
2579
axis = ApplyStickCalibration(ctx, 1, 0, axis);
2580
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis);
2581
2582
axis = ((packet->controllerState.rgucJoystickRight[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickRight[2] << 4);
2583
axis = ApplyStickCalibration(ctx, 1, 1, axis);
2584
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, ~axis);
2585
}
2586
2587
/* High nibble of battery/connection byte is battery level, low nibble is connection status (always 0 on 8BitDo Pro 2)
2588
* LSB of connection nibble is USB/Switch connection status
2589
* LSB of the battery nibble is used to report charging.
2590
* The battery level is reported from 0(empty)-8(full)
2591
*/
2592
SDL_PowerState state;
2593
int charging = (packet->controllerState.ucBatteryAndConnection & 0x10);
2594
int level = (packet->controllerState.ucBatteryAndConnection & 0xE0) >> 4;
2595
int percent = (int)SDL_roundf((level / 8.0f) * 100.0f);
2596
2597
if (charging) {
2598
if (level == 8) {
2599
state = SDL_POWERSTATE_CHARGED;
2600
} else {
2601
state = SDL_POWERSTATE_CHARGING;
2602
}
2603
} else {
2604
state = SDL_POWERSTATE_ON_BATTERY;
2605
}
2606
SDL_SendJoystickPowerInfo(joystick, state, percent);
2607
2608
if (ctx->m_bReportSensors) {
2609
// Need to copy the imuState to an aligned variable
2610
SwitchControllerIMUState_t imuState[3];
2611
SDL_assert(sizeof(imuState) == sizeof(packet->imuState));
2612
SDL_memcpy(imuState, packet->imuState, sizeof(imuState));
2613
2614
bool bHasSensorData = (imuState[0].sAccelZ != 0 ||
2615
imuState[0].sAccelY != 0 ||
2616
imuState[0].sAccelX != 0);
2617
if (bHasSensorData) {
2618
const Uint32 IMU_UPDATE_RATE_SAMPLE_FREQUENCY = 1000;
2619
Uint64 sensor_timestamp[3];
2620
2621
ctx->m_bHasSensorData = true;
2622
2623
// We got three IMU samples, calculate the IMU update rate and timestamps
2624
ctx->m_unIMUSamples += 3;
2625
if (ctx->m_unIMUSamples >= IMU_UPDATE_RATE_SAMPLE_FREQUENCY) {
2626
Uint64 now = SDL_GetTicksNS();
2627
Uint64 elapsed = (now - ctx->m_ulIMUSampleTimestampNS);
2628
2629
if (elapsed > 0) {
2630
ctx->m_ulIMUUpdateIntervalNS = elapsed / ctx->m_unIMUSamples;
2631
}
2632
ctx->m_unIMUSamples = 0;
2633
ctx->m_ulIMUSampleTimestampNS = now;
2634
}
2635
2636
ctx->m_ulTimestampNS += ctx->m_ulIMUUpdateIntervalNS;
2637
sensor_timestamp[0] = ctx->m_ulTimestampNS;
2638
ctx->m_ulTimestampNS += ctx->m_ulIMUUpdateIntervalNS;
2639
sensor_timestamp[1] = ctx->m_ulTimestampNS;
2640
ctx->m_ulTimestampNS += ctx->m_ulIMUUpdateIntervalNS;
2641
sensor_timestamp[2] = ctx->m_ulTimestampNS;
2642
2643
if (!ctx->device->parent ||
2644
ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {
2645
SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_GYRO, sensor_timestamp[0], &imuState[2].sGyroX);
2646
SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_ACCEL, sensor_timestamp[0], &imuState[2].sAccelX);
2647
2648
SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_GYRO, sensor_timestamp[1], &imuState[1].sGyroX);
2649
SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_ACCEL, sensor_timestamp[1], &imuState[1].sAccelX);
2650
2651
SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_GYRO, sensor_timestamp[2], &imuState[0].sGyroX);
2652
SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_ACCEL, sensor_timestamp[2], &imuState[0].sAccelX);
2653
}
2654
2655
if (ctx->device->parent &&
2656
ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft) {
2657
SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_GYRO_L, sensor_timestamp[0], &imuState[2].sGyroX);
2658
SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_ACCEL_L, sensor_timestamp[0], &imuState[2].sAccelX);
2659
2660
SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_GYRO_L, sensor_timestamp[1], &imuState[1].sGyroX);
2661
SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_ACCEL_L, sensor_timestamp[1], &imuState[1].sAccelX);
2662
2663
SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_GYRO_L, sensor_timestamp[2], &imuState[0].sGyroX);
2664
SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_ACCEL_L, sensor_timestamp[2], &imuState[0].sAccelX);
2665
}
2666
if (ctx->device->parent &&
2667
ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {
2668
SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_GYRO_R, sensor_timestamp[0], &imuState[2].sGyroX);
2669
SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_ACCEL_R, sensor_timestamp[0], &imuState[2].sAccelX);
2670
2671
SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_GYRO_R, sensor_timestamp[1], &imuState[1].sGyroX);
2672
SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_ACCEL_R, sensor_timestamp[1], &imuState[1].sAccelX);
2673
2674
SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_GYRO_R, sensor_timestamp[2], &imuState[0].sGyroX);
2675
SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_ACCEL_R, sensor_timestamp[2], &imuState[0].sAccelX);
2676
}
2677
2678
} else if (ctx->m_bHasSensorData) {
2679
// Uh oh, someone turned off the IMU?
2680
const int IMU_RESET_DELAY_MS = 3000;
2681
Uint64 now = SDL_GetTicks();
2682
2683
if (now >= (ctx->m_ulLastIMUReset + IMU_RESET_DELAY_MS)) {
2684
SDL_HIDAPI_Device *device = ctx->device;
2685
2686
if (device->updating) {
2687
SDL_UnlockMutex(device->dev_lock);
2688
}
2689
2690
SetIMUEnabled(ctx, true);
2691
2692
if (device->updating) {
2693
SDL_LockMutex(device->dev_lock);
2694
}
2695
ctx->m_ulLastIMUReset = now;
2696
}
2697
2698
} else {
2699
// We have never gotten IMU data, probably not supported on this device
2700
}
2701
}
2702
2703
ctx->m_lastFullState = *packet;
2704
}
2705
2706
static bool HIDAPI_DriverSwitch_UpdateDevice(SDL_HIDAPI_Device *device)
2707
{
2708
SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;
2709
SDL_Joystick *joystick = NULL;
2710
int size;
2711
int packet_count = 0;
2712
Uint64 now = SDL_GetTicks();
2713
2714
if (device->num_joysticks > 0) {
2715
joystick = SDL_GetJoystickFromID(device->joysticks[0]);
2716
}
2717
2718
while ((size = ReadInput(ctx)) > 0) {
2719
#ifdef DEBUG_SWITCH_PROTOCOL
2720
HIDAPI_DumpPacket("Nintendo Switch packet: size = %d", ctx->m_rgucReadBuffer, size);
2721
#endif
2722
++packet_count;
2723
ctx->m_ulLastInput = now;
2724
2725
if (!joystick) {
2726
continue;
2727
}
2728
2729
if (ctx->m_bInputOnly) {
2730
HandleInputOnlyControllerState(joystick, ctx, (SwitchInputOnlyControllerStatePacket_t *)&ctx->m_rgucReadBuffer[0]);
2731
} else {
2732
if (ctx->m_rgucReadBuffer[0] == k_eSwitchInputReportIDs_SubcommandReply) {
2733
continue;
2734
}
2735
2736
ctx->m_nCurrentInputMode = ctx->m_rgucReadBuffer[0];
2737
2738
switch (ctx->m_rgucReadBuffer[0]) {
2739
case k_eSwitchInputReportIDs_SimpleControllerState:
2740
HandleSimpleControllerState(joystick, ctx, (SwitchSimpleStatePacket_t *)&ctx->m_rgucReadBuffer[1]);
2741
break;
2742
case k_eSwitchInputReportIDs_FullControllerState:
2743
case k_eSwitchInputReportIDs_FullControllerAndMcuState:
2744
// This is the extended report, we can enable sensors now in auto mode
2745
UpdateEnhancedModeOnEnhancedReport(ctx);
2746
2747
HandleFullControllerState(joystick, ctx, (SwitchStatePacket_t *)&ctx->m_rgucReadBuffer[1]);
2748
break;
2749
default:
2750
break;
2751
}
2752
}
2753
}
2754
2755
if (joystick) {
2756
if (packet_count == 0) {
2757
if (!ctx->m_bInputOnly && !device->is_bluetooth &&
2758
ctx->device->product_id != USB_PRODUCT_NINTENDO_SWITCH_JOYCON_GRIP) {
2759
const int INPUT_WAIT_TIMEOUT_MS = 100;
2760
if (now >= (ctx->m_ulLastInput + INPUT_WAIT_TIMEOUT_MS)) {
2761
// Steam may have put the controller back into non-reporting mode
2762
bool wasSyncWrite = ctx->m_bSyncWrite;
2763
2764
ctx->m_bSyncWrite = true;
2765
WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_ForceUSB, NULL, 0, false);
2766
ctx->m_bSyncWrite = wasSyncWrite;
2767
}
2768
} else if (device->is_bluetooth &&
2769
ctx->m_nCurrentInputMode != k_eSwitchInputReportIDs_SimpleControllerState) {
2770
const int INPUT_WAIT_TIMEOUT_MS = 3000;
2771
if (now >= (ctx->m_ulLastInput + INPUT_WAIT_TIMEOUT_MS)) {
2772
// Bluetooth may have disconnected, try reopening the controller
2773
size = -1;
2774
}
2775
}
2776
}
2777
2778
if (ctx->m_bRumblePending || ctx->m_bRumbleZeroPending) {
2779
HIDAPI_DriverSwitch_SendPendingRumble(ctx);
2780
} else if (ctx->m_bRumbleActive &&
2781
now >= (ctx->m_ulRumbleSent + RUMBLE_REFRESH_FREQUENCY_MS)) {
2782
#ifdef DEBUG_RUMBLE
2783
SDL_Log("Sent continuing rumble, %d ms after previous rumble", now - ctx->m_ulRumbleSent);
2784
#endif
2785
WriteRumble(ctx);
2786
}
2787
}
2788
2789
// Reconnect the Bluetooth device once the USB device is gone
2790
if (device->num_joysticks == 0 && device->is_bluetooth && packet_count > 0 &&
2791
!device->parent &&
2792
!HIDAPI_HasConnectedUSBDevice(device->serial)) {
2793
HIDAPI_JoystickConnected(device, NULL);
2794
}
2795
2796
if (size < 0 && device->num_joysticks > 0) {
2797
// Read error, device is disconnected
2798
HIDAPI_JoystickDisconnected(device, device->joysticks[0]);
2799
}
2800
return (size >= 0);
2801
}
2802
2803
static void HIDAPI_DriverSwitch_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
2804
{
2805
SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;
2806
2807
if (!ctx->m_bInputOnly) {
2808
// Restore simple input mode for other applications
2809
if (!ctx->m_nInitialInputMode ||
2810
ctx->m_nInitialInputMode == k_eSwitchInputReportIDs_SimpleControllerState) {
2811
SetInputMode(ctx, k_eSwitchInputReportIDs_SimpleControllerState);
2812
}
2813
}
2814
2815
SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_ENHANCED_REPORTS,
2816
SDL_EnhancedReportsChanged, ctx);
2817
2818
if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft ||
2819
ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {
2820
SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_JOYCON_HOME_LED,
2821
SDL_HomeLEDHintChanged, ctx);
2822
} else {
2823
SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED,
2824
SDL_HomeLEDHintChanged, ctx);
2825
}
2826
2827
SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED,
2828
SDL_PlayerLEDHintChanged, ctx);
2829
2830
ctx->joystick = NULL;
2831
2832
ctx->m_bReportSensors = false;
2833
ctx->m_bEnhancedMode = false;
2834
ctx->m_bEnhancedModeAvailable = false;
2835
}
2836
2837
static void HIDAPI_DriverSwitch_FreeDevice(SDL_HIDAPI_Device *device)
2838
{
2839
}
2840
2841
SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverNintendoClassic = {
2842
SDL_HINT_JOYSTICK_HIDAPI_NINTENDO_CLASSIC,
2843
true,
2844
HIDAPI_DriverNintendoClassic_RegisterHints,
2845
HIDAPI_DriverNintendoClassic_UnregisterHints,
2846
HIDAPI_DriverNintendoClassic_IsEnabled,
2847
HIDAPI_DriverNintendoClassic_IsSupportedDevice,
2848
HIDAPI_DriverSwitch_InitDevice,
2849
HIDAPI_DriverSwitch_GetDevicePlayerIndex,
2850
HIDAPI_DriverSwitch_SetDevicePlayerIndex,
2851
HIDAPI_DriverSwitch_UpdateDevice,
2852
HIDAPI_DriverSwitch_OpenJoystick,
2853
HIDAPI_DriverSwitch_RumbleJoystick,
2854
HIDAPI_DriverSwitch_RumbleJoystickTriggers,
2855
HIDAPI_DriverSwitch_GetJoystickCapabilities,
2856
HIDAPI_DriverSwitch_SetJoystickLED,
2857
HIDAPI_DriverSwitch_SendJoystickEffect,
2858
HIDAPI_DriverSwitch_SetJoystickSensorsEnabled,
2859
HIDAPI_DriverSwitch_CloseJoystick,
2860
HIDAPI_DriverSwitch_FreeDevice,
2861
};
2862
2863
SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverJoyCons = {
2864
SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS,
2865
true,
2866
HIDAPI_DriverJoyCons_RegisterHints,
2867
HIDAPI_DriverJoyCons_UnregisterHints,
2868
HIDAPI_DriverJoyCons_IsEnabled,
2869
HIDAPI_DriverJoyCons_IsSupportedDevice,
2870
HIDAPI_DriverSwitch_InitDevice,
2871
HIDAPI_DriverSwitch_GetDevicePlayerIndex,
2872
HIDAPI_DriverSwitch_SetDevicePlayerIndex,
2873
HIDAPI_DriverSwitch_UpdateDevice,
2874
HIDAPI_DriverSwitch_OpenJoystick,
2875
HIDAPI_DriverSwitch_RumbleJoystick,
2876
HIDAPI_DriverSwitch_RumbleJoystickTriggers,
2877
HIDAPI_DriverSwitch_GetJoystickCapabilities,
2878
HIDAPI_DriverSwitch_SetJoystickLED,
2879
HIDAPI_DriverSwitch_SendJoystickEffect,
2880
HIDAPI_DriverSwitch_SetJoystickSensorsEnabled,
2881
HIDAPI_DriverSwitch_CloseJoystick,
2882
HIDAPI_DriverSwitch_FreeDevice,
2883
};
2884
2885
SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSwitch = {
2886
SDL_HINT_JOYSTICK_HIDAPI_SWITCH,
2887
true,
2888
HIDAPI_DriverSwitch_RegisterHints,
2889
HIDAPI_DriverSwitch_UnregisterHints,
2890
HIDAPI_DriverSwitch_IsEnabled,
2891
HIDAPI_DriverSwitch_IsSupportedDevice,
2892
HIDAPI_DriverSwitch_InitDevice,
2893
HIDAPI_DriverSwitch_GetDevicePlayerIndex,
2894
HIDAPI_DriverSwitch_SetDevicePlayerIndex,
2895
HIDAPI_DriverSwitch_UpdateDevice,
2896
HIDAPI_DriverSwitch_OpenJoystick,
2897
HIDAPI_DriverSwitch_RumbleJoystick,
2898
HIDAPI_DriverSwitch_RumbleJoystickTriggers,
2899
HIDAPI_DriverSwitch_GetJoystickCapabilities,
2900
HIDAPI_DriverSwitch_SetJoystickLED,
2901
HIDAPI_DriverSwitch_SendJoystickEffect,
2902
HIDAPI_DriverSwitch_SetJoystickSensorsEnabled,
2903
HIDAPI_DriverSwitch_CloseJoystick,
2904
HIDAPI_DriverSwitch_FreeDevice,
2905
};
2906
2907
#endif // SDL_JOYSTICK_HIDAPI_SWITCH
2908
2909
#endif // SDL_JOYSTICK_HIDAPI
2910
2911