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