Skip to content

Latest commit

 

History

History
1174 lines (999 loc) · 44.2 KB

SDL_hidapi_switch.c

File metadata and controls

1174 lines (999 loc) · 44.2 KB
 
1
2
/*
Simple DirectMedia Layer
Jan 17, 2020
Jan 17, 2020
3
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
Aug 9, 2018
Aug 9, 2018
21
22
23
/* This driver supports the Nintendo Switch Pro controller.
Code and logic contributed by Valve Corporation under the SDL zlib license.
*/
24
25
26
27
28
29
30
31
32
33
#include "../../SDL_internal.h"
#ifdef SDL_JOYSTICK_HIDAPI
#include "SDL_hints.h"
#include "SDL_log.h"
#include "SDL_events.h"
#include "SDL_timer.h"
#include "SDL_joystick.h"
#include "SDL_gamecontroller.h"
Feb 7, 2020
Feb 7, 2020
34
#include "../../SDL_hints_c.h"
35
36
#include "../SDL_sysjoystick.h"
#include "SDL_hidapijoystick_c.h"
Feb 7, 2020
Feb 7, 2020
37
#include "SDL_hidapi_rumble.h"
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#ifdef SDL_JOYSTICK_HIDAPI_SWITCH
typedef enum {
k_eSwitchInputReportIDs_SubcommandReply = 0x21,
k_eSwitchInputReportIDs_FullControllerState = 0x30,
k_eSwitchInputReportIDs_SimpleControllerState = 0x3F,
k_eSwitchInputReportIDs_CommandAck = 0x81,
} ESwitchInputReportIDs;
typedef enum {
k_eSwitchOutputReportIDs_RumbleAndSubcommand = 0x01,
k_eSwitchOutputReportIDs_Rumble = 0x10,
k_eSwitchOutputReportIDs_Proprietary = 0x80,
} ESwitchOutputReportIDs;
typedef enum {
k_eSwitchSubcommandIDs_BluetoothManualPair = 0x01,
k_eSwitchSubcommandIDs_RequestDeviceInfo = 0x02,
k_eSwitchSubcommandIDs_SetInputReportMode = 0x03,
k_eSwitchSubcommandIDs_SetHCIState = 0x06,
k_eSwitchSubcommandIDs_SPIFlashRead = 0x10,
k_eSwitchSubcommandIDs_SetPlayerLights = 0x30,
k_eSwitchSubcommandIDs_SetHomeLight = 0x38,
k_eSwitchSubcommandIDs_EnableIMU = 0x40,
k_eSwitchSubcommandIDs_SetIMUSensitivity = 0x41,
k_eSwitchSubcommandIDs_EnableVibration = 0x48,
} ESwitchSubcommandIDs;
typedef enum {
k_eSwitchProprietaryCommandIDs_Handshake = 0x02,
k_eSwitchProprietaryCommandIDs_HighSpeed = 0x03,
k_eSwitchProprietaryCommandIDs_ForceUSB = 0x04,
k_eSwitchProprietaryCommandIDs_ClearUSB = 0x05,
k_eSwitchProprietaryCommandIDs_ResetMCU = 0x06,
} ESwitchProprietaryCommandIDs;
typedef enum {
k_eSwitchDeviceInfoControllerType_JoyConLeft = 0x1,
k_eSwitchDeviceInfoControllerType_JoyConRight = 0x2,
k_eSwitchDeviceInfoControllerType_ProController = 0x3,
} ESwitchDeviceInfoControllerType;
#define k_unSwitchOutputPacketDataLength 49
#define k_unSwitchMaxOutputPacketLength 64
#define k_unSwitchBluetoothPacketLength k_unSwitchOutputPacketDataLength
#define k_unSwitchUSBPacketLength k_unSwitchMaxOutputPacketLength
#define k_unSPIStickCalibrationStartOffset 0x603D
#define k_unSPIStickCalibrationEndOffset 0x604E
#define k_unSPIStickCalibrationLength (k_unSPIStickCalibrationEndOffset - k_unSPIStickCalibrationStartOffset + 1)
#pragma pack(1)
Oct 17, 2019
Oct 17, 2019
92
93
94
95
96
97
98
99
typedef struct
{
Uint8 rgucButtons[2];
Uint8 ucStickHat;
Uint8 rgucJoystickLeft[2];
Uint8 rgucJoystickRight[2];
} SwitchInputOnlyControllerStatePacket_t;
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
typedef struct
{
Uint8 rgucButtons[2];
Uint8 ucStickHat;
Sint16 sJoystickLeft[2];
Sint16 sJoystickRight[2];
} SwitchSimpleStatePacket_t;
typedef struct
{
Uint8 ucCounter;
Uint8 ucBatteryAndConnection;
Uint8 rgucButtons[3];
Uint8 rgucJoystickLeft[3];
Uint8 rgucJoystickRight[3];
Uint8 ucVibrationCode;
} SwitchControllerStatePacket_t;
typedef struct
{
SwitchControllerStatePacket_t controllerState;
struct {
Sint16 sAccelX;
Sint16 sAccelY;
Sint16 sAccelZ;
Sint16 sGyroX;
Sint16 sGyroY;
Sint16 sGyroZ;
} imuState[3];
} SwitchStatePacket_t;
typedef struct
{
Uint32 unAddress;
Uint8 ucLength;
} SwitchSPIOpData_t;
typedef struct
{
SwitchControllerStatePacket_t m_controllerState;
Uint8 ucSubcommandAck;
Uint8 ucSubcommandID;
#define k_unSubcommandDataBytes 35
union {
Oct 17, 2019
Oct 17, 2019
148
Uint8 rgucSubcommandData[k_unSubcommandDataBytes];
149
150
151
struct {
SwitchSPIOpData_t opData;
Oct 17, 2019
Oct 17, 2019
152
Uint8 rgucReadData[k_unSubcommandDataBytes - sizeof(SwitchSPIOpData_t)];
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
} spiReadData;
struct {
Uint8 rgucFirmwareVersion[2];
Uint8 ucDeviceType;
Uint8 ucFiller1;
Uint8 rgucMACAddress[6];
Uint8 ucFiller2;
Uint8 ucColorLocation;
} deviceInfo;
};
} SwitchSubcommandInputPacket_t;
typedef struct
{
Uint8 rgucData[4];
} SwitchRumbleData_t;
typedef struct
{
Uint8 ucPacketType;
Uint8 ucPacketNumber;
SwitchRumbleData_t rumbleData[2];
} SwitchCommonOutputPacket_t;
typedef struct
{
SwitchCommonOutputPacket_t commonData;
Uint8 ucSubcommandID;
Oct 17, 2019
Oct 17, 2019
183
Uint8 rgucSubcommandData[k_unSwitchOutputPacketDataLength - sizeof(SwitchCommonOutputPacket_t) - 1];
184
185
186
187
188
189
190
} SwitchSubcommandOutputPacket_t;
typedef struct
{
Uint8 ucPacketType;
Uint8 ucProprietaryID;
Oct 17, 2019
Oct 17, 2019
191
Uint8 rgucProprietaryData[k_unSwitchOutputPacketDataLength - 1 - 1];
192
193
194
195
} SwitchProprietaryOutputPacket_t;
#pragma pack()
typedef struct {
Feb 7, 2020
Feb 7, 2020
196
SDL_HIDAPI_Device *device;
Oct 18, 2019
Oct 18, 2019
197
SDL_bool m_bInputOnly;
Oct 22, 2019
Oct 22, 2019
198
SDL_bool m_bHasHomeLED;
Oct 18, 2019
Oct 18, 2019
199
SDL_bool m_bUsingBluetooth;
Mar 11, 2020
Mar 11, 2020
200
SDL_bool m_bIsGameCube;
Oct 18, 2019
Oct 18, 2019
201
SDL_bool m_bUseButtonLabels;
202
203
204
Uint8 m_nCommandNumber;
SwitchCommonOutputPacket_t m_RumblePacket;
Uint8 m_rgucReadBuffer[k_unSwitchMaxOutputPacketLength];
Feb 7, 2020
Feb 7, 2020
205
206
SDL_bool m_bRumbleActive;
Uint32 m_unRumbleRefresh;
Oct 17, 2019
Oct 17, 2019
207
208
SwitchInputOnlyControllerStatePacket_t m_lastInputOnlyState;
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
SwitchSimpleStatePacket_t m_lastSimpleState;
SwitchStatePacket_t m_lastFullState;
struct StickCalibrationData {
struct {
Sint16 sCenter;
Sint16 sMin;
Sint16 sMax;
} axis[2];
} m_StickCalData[2];
struct StickExtents {
struct {
Sint16 sMin;
Sint16 sMax;
} axis[2];
} m_StickExtents[2];
} SDL_DriverSwitch_Context;
Dec 19, 2019
Dec 19, 2019
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
static SDL_bool IsGameCubeFormFactor(int vendor_id, int product_id)
{
static Uint32 gamecube_formfactor[] = {
MAKE_VIDPID(0x0e6f, 0x0185), /* PDP Wired Fight Pad Pro for Nintendo Switch */
MAKE_VIDPID(0x20d6, 0xa711), /* Core (Plus) Wired Controller */
};
Uint32 id = MAKE_VIDPID(vendor_id, product_id);
int i;
for (i = 0; i < SDL_arraysize(gamecube_formfactor); ++i) {
if (id == gamecube_formfactor[i]) {
return SDL_TRUE;
}
}
return SDL_FALSE;
}
Jan 19, 2020
Jan 19, 2020
247
HIDAPI_DriverSwitch_IsSupportedDevice(const char *name, SDL_GameControllerType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol)
Oct 22, 2019
Oct 22, 2019
248
{
Feb 27, 2020
Feb 27, 2020
249
250
251
252
253
254
255
256
257
/* The HORI Wireless Switch Pad enumerates as a HID device when connected via USB
with the same VID/PID as when connected over Bluetooth but doesn't actually
support communication over USB. The most reliable way to block this without allowing the
controller to continually attempt to reconnect is to filter it out by manufactuer/product string.
Note that the controller does have a different product string when connected over Bluetooth.
*/
if (SDL_strcmp( name, "HORI Wireless Switch Pad" ) == 0) {
return SDL_FALSE;
}
Jan 19, 2020
Jan 19, 2020
258
return (type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO);
259
260
261
262
263
264
}
static const char *
HIDAPI_DriverSwitch_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
{
/* Give a user friendly name for this controller */
Oct 22, 2019
Oct 22, 2019
265
return "Nintendo Switch Pro Controller";
266
267
268
269
}
static int ReadInput(SDL_DriverSwitch_Context *ctx)
{
Feb 7, 2020
Feb 7, 2020
270
271
272
273
274
275
/* Make sure we don't try to read at the same time a write is happening */
if (SDL_AtomicGet(&ctx->device->rumble_pending) > 0) {
return 0;
}
return hid_read_timeout(ctx->device->dev, ctx->m_rgucReadBuffer, sizeof(ctx->m_rgucReadBuffer), 0);
Feb 7, 2020
Feb 7, 2020
278
static int WriteOutput(SDL_DriverSwitch_Context *ctx, const Uint8 *data, int size)
Feb 7, 2020
Feb 7, 2020
280
281
282
283
284
/* Use the rumble thread for general asynchronous writes */
if (SDL_HIDAPI_LockRumble() < 0) {
return -1;
}
return SDL_HIDAPI_SendRumbleAndUnlock(ctx->device, data, size);
285
286
287
288
289
290
291
292
293
294
295
296
}
static SwitchSubcommandInputPacket_t *ReadSubcommandReply(SDL_DriverSwitch_Context *ctx, ESwitchSubcommandIDs expectedID)
{
/* Average response time for messages is ~30ms */
Uint32 TimeoutMs = 100;
Uint32 startTicks = SDL_GetTicks();
int nRead = 0;
while ((nRead = ReadInput(ctx)) != -1) {
if (nRead > 0) {
if (ctx->m_rgucReadBuffer[0] == k_eSwitchInputReportIDs_SubcommandReply) {
Oct 17, 2019
Oct 17, 2019
297
SwitchSubcommandInputPacket_t *reply = (SwitchSubcommandInputPacket_t *)&ctx->m_rgucReadBuffer[1];
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
if (reply->ucSubcommandID == expectedID && (reply->ucSubcommandAck & 0x80)) {
return reply;
}
}
} else {
SDL_Delay(1);
}
if (SDL_TICKS_PASSED(SDL_GetTicks(), startTicks + TimeoutMs)) {
break;
}
}
return NULL;
}
static SDL_bool ReadProprietaryReply(SDL_DriverSwitch_Context *ctx, ESwitchProprietaryCommandIDs expectedID)
{
/* Average response time for messages is ~30ms */
Uint32 TimeoutMs = 100;
Uint32 startTicks = SDL_GetTicks();
int nRead = 0;
while ((nRead = ReadInput(ctx)) != -1) {
if (nRead > 0) {
Oct 17, 2019
Oct 17, 2019
322
if (ctx->m_rgucReadBuffer[0] == k_eSwitchInputReportIDs_CommandAck && ctx->m_rgucReadBuffer[1] == expectedID) {
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
return SDL_TRUE;
}
} else {
SDL_Delay(1);
}
if (SDL_TICKS_PASSED(SDL_GetTicks(), startTicks + TimeoutMs)) {
break;
}
}
return SDL_FALSE;
}
static void ConstructSubcommand(SDL_DriverSwitch_Context *ctx, ESwitchSubcommandIDs ucCommandID, Uint8 *pBuf, Uint8 ucLen, SwitchSubcommandOutputPacket_t *outPacket)
{
SDL_memset(outPacket, 0, sizeof(*outPacket));
outPacket->commonData.ucPacketType = k_eSwitchOutputReportIDs_RumbleAndSubcommand;
outPacket->commonData.ucPacketNumber = ctx->m_nCommandNumber;
SDL_memcpy(&outPacket->commonData.rumbleData, &ctx->m_RumblePacket.rumbleData, sizeof(ctx->m_RumblePacket.rumbleData));
outPacket->ucSubcommandID = ucCommandID;
SDL_memcpy(outPacket->rgucSubcommandData, pBuf, ucLen);
ctx->m_nCommandNumber = (ctx->m_nCommandNumber + 1) & 0xF;
}
static SDL_bool WritePacket(SDL_DriverSwitch_Context *ctx, void *pBuf, Uint8 ucLen)
{
Uint8 rgucBuf[k_unSwitchMaxOutputPacketLength];
Oct 18, 2019
Oct 18, 2019
354
const size_t unWriteSize = ctx->m_bUsingBluetooth ? k_unSwitchBluetoothPacketLength : k_unSwitchUSBPacketLength;
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
if (ucLen > k_unSwitchOutputPacketDataLength) {
return SDL_FALSE;
}
if (ucLen < unWriteSize) {
SDL_memcpy(rgucBuf, pBuf, ucLen);
SDL_memset(rgucBuf+ucLen, 0, unWriteSize-ucLen);
pBuf = rgucBuf;
ucLen = (Uint8)unWriteSize;
}
return (WriteOutput(ctx, (Uint8 *)pBuf, ucLen) >= 0);
}
static SDL_bool WriteSubcommand(SDL_DriverSwitch_Context *ctx, ESwitchSubcommandIDs ucCommandID, Uint8 *pBuf, Uint8 ucLen, SwitchSubcommandInputPacket_t **ppReply)
{
int nRetries = 5;
SwitchSubcommandInputPacket_t *reply = NULL;
while (!reply && nRetries--) {
SwitchSubcommandOutputPacket_t commandPacket;
ConstructSubcommand(ctx, ucCommandID, pBuf, ucLen, &commandPacket);
if (!WritePacket(ctx, &commandPacket, sizeof(commandPacket))) {
continue;
}
reply = ReadSubcommandReply(ctx, ucCommandID);
}
if (ppReply) {
*ppReply = reply;
}
return reply != NULL;
}
static SDL_bool WriteProprietary(SDL_DriverSwitch_Context *ctx, ESwitchProprietaryCommandIDs ucCommand, Uint8 *pBuf, Uint8 ucLen, SDL_bool waitForReply)
{
int nRetries = 5;
while (nRetries--) {
SwitchProprietaryOutputPacket_t packet;
if ((!pBuf && ucLen > 0) || ucLen > sizeof(packet.rgucProprietaryData)) {
return SDL_FALSE;
}
packet.ucPacketType = k_eSwitchOutputReportIDs_Proprietary;
packet.ucProprietaryID = ucCommand;
Feb 7, 2020
Feb 7, 2020
404
405
406
if (pBuf) {
SDL_memcpy(packet.rgucProprietaryData, pBuf, ucLen);
}
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
if (!WritePacket(ctx, &packet, sizeof(packet))) {
continue;
}
if (!waitForReply || ReadProprietaryReply(ctx, ucCommand)) {
return SDL_TRUE;
}
}
return SDL_FALSE;
}
static void SetNeutralRumble(SwitchRumbleData_t *pRumble)
{
pRumble->rgucData[0] = 0x00;
pRumble->rgucData[1] = 0x01;
pRumble->rgucData[2] = 0x40;
pRumble->rgucData[3] = 0x40;
}
static void EncodeRumble(SwitchRumbleData_t *pRumble, Uint16 usHighFreq, Uint8 ucHighFreqAmp, Uint8 ucLowFreq, Uint16 usLowFreqAmp)
{
if (ucHighFreqAmp > 0 || usLowFreqAmp > 0) {
// High-band frequency and low-band amplitude are actually nine-bits each so they
// take a bit from the high-band amplitude and low-band frequency bytes respectively
pRumble->rgucData[0] = usHighFreq & 0xFF;
pRumble->rgucData[1] = ucHighFreqAmp | ((usHighFreq >> 8) & 0x01);
pRumble->rgucData[2] = ucLowFreq | ((usLowFreqAmp >> 8) & 0x80);
pRumble->rgucData[3] = usLowFreqAmp & 0xFF;
#ifdef DEBUG_RUMBLE
SDL_Log("Freq: %.2X %.2X %.2X, Amp: %.2X %.2X %.2X\n",
usHighFreq & 0xFF, ((usHighFreq >> 8) & 0x01), ucLowFreq,
ucHighFreqAmp, ((usLowFreqAmp >> 8) & 0x80), usLowFreqAmp & 0xFF);
#endif
} else {
SetNeutralRumble(pRumble);
}
}
static SDL_bool WriteRumble(SDL_DriverSwitch_Context *ctx)
{
/* Write into m_RumblePacket rather than a temporary buffer to allow the current rumble state
* to be retained for subsequent rumble or subcommand packets sent to the controller
*/
ctx->m_RumblePacket.ucPacketType = k_eSwitchOutputReportIDs_Rumble;
ctx->m_RumblePacket.ucPacketNumber = ctx->m_nCommandNumber;
ctx->m_nCommandNumber = (ctx->m_nCommandNumber + 1) & 0xF;
Feb 7, 2020
Feb 7, 2020
457
458
/* Refresh the rumble state periodically */
if (ctx->m_bRumbleActive) {
Mar 5, 2020
Mar 5, 2020
459
ctx->m_unRumbleRefresh = SDL_GetTicks() + 30;
Feb 7, 2020
Feb 7, 2020
460
461
462
463
464
465
466
if (!ctx->m_unRumbleRefresh) {
ctx->m_unRumbleRefresh = 1;
}
} else {
ctx->m_unRumbleRefresh = 0;
}
467
468
469
470
471
472
473
474
475
476
477
478
479
480
return WritePacket(ctx, (Uint8 *)&ctx->m_RumblePacket, sizeof(ctx->m_RumblePacket));
}
static SDL_bool BTrySetupUSB(SDL_DriverSwitch_Context *ctx)
{
/* We have to send a connection handshake to the controller when communicating over USB
* before we're able to send it other commands. Luckily this command is not supported
* over Bluetooth, so we can use the controller's lack of response as a way to
* determine if the connection is over USB or Bluetooth
*/
if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_Handshake, NULL, 0, SDL_TRUE)) {
return SDL_FALSE;
}
if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_HighSpeed, NULL, 0, SDL_TRUE)) {
Mar 4, 2020
Mar 4, 2020
481
/* The 8BitDo M30 and SF30 Pro don't respond to this command, but otherwise work correctly */
Jan 8, 2020
Jan 8, 2020
482
/*return SDL_FALSE;*/
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
}
if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_Handshake, NULL, 0, SDL_TRUE)) {
return SDL_FALSE;
}
return SDL_TRUE;
}
static SDL_bool SetVibrationEnabled(SDL_DriverSwitch_Context *ctx, Uint8 enabled)
{
return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_EnableVibration, &enabled, sizeof(enabled), NULL);
}
static SDL_bool SetInputMode(SDL_DriverSwitch_Context *ctx, Uint8 input_mode)
{
return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SetInputReportMode, &input_mode, 1, NULL);
}
static SDL_bool SetHomeLED(SDL_DriverSwitch_Context *ctx, Uint8 brightness)
{
Uint8 ucLedIntensity = 0;
Uint8 rgucBuffer[4];
if (brightness > 0) {
if (brightness < 65) {
ucLedIntensity = (brightness + 5) / 10;
} else {
ucLedIntensity = (Uint8)SDL_ceilf(0xF * SDL_powf((float)brightness / 100.f, 2.13f));
}
}
rgucBuffer[0] = (0x0 << 4) | 0x1; /* 0 mini cycles (besides first), cycle duration 8ms */
rgucBuffer[1] = ((ucLedIntensity & 0xF) << 4) | 0x0; /* LED start intensity (0x0-0xF), 0 cycles (LED stays on at start intensity after first cycle) */
rgucBuffer[2] = ((ucLedIntensity & 0xF) << 4) | 0x0; /* First cycle LED intensity, 0x0 intensity for second cycle */
rgucBuffer[3] = (0x0 << 4) | 0x0; /* 8ms fade transition to first cycle, 8ms first cycle LED duration */
return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SetHomeLight, rgucBuffer, sizeof(rgucBuffer), NULL);
}
static SDL_bool SetSlotLED(SDL_DriverSwitch_Context *ctx, Uint8 slot)
{
Uint8 led_data = (1 << slot);
return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SetPlayerLights, &led_data, sizeof(led_data), NULL);
}
static SDL_bool LoadStickCalibration(SDL_DriverSwitch_Context *ctx)
{
Uint8 *pStickCal;
size_t stick, axis;
SwitchSubcommandInputPacket_t *reply = NULL;
/* Read Calibration Info */
SwitchSPIOpData_t readParams;
readParams.unAddress = k_unSPIStickCalibrationStartOffset;
readParams.ucLength = k_unSPIStickCalibrationLength;
if (!WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SPIFlashRead, (uint8_t *)&readParams, sizeof(readParams), &reply)) {
return SDL_FALSE;
}
/* Stick calibration values are 12-bits each and are packed by bit
* For whatever reason the fields are in a different order for each stick
* Left: X-Max, Y-Max, X-Center, Y-Center, X-Min, Y-Min
* Right: X-Center, Y-Center, X-Min, Y-Min, X-Max, Y-Max
*/
pStickCal = reply->spiReadData.rgucReadData;
/* Left stick */
ctx->m_StickCalData[0].axis[0].sMax = ((pStickCal[1] << 8) & 0xF00) | pStickCal[0]; /* X Axis max above center */
ctx->m_StickCalData[0].axis[1].sMax = (pStickCal[2] << 4) | (pStickCal[1] >> 4); /* Y Axis max above center */
ctx->m_StickCalData[0].axis[0].sCenter = ((pStickCal[4] << 8) & 0xF00) | pStickCal[3]; /* X Axis center */
ctx->m_StickCalData[0].axis[1].sCenter = (pStickCal[5] << 4) | (pStickCal[4] >> 4); /* Y Axis center */
ctx->m_StickCalData[0].axis[0].sMin = ((pStickCal[7] << 8) & 0xF00) | pStickCal[6]; /* X Axis min below center */
ctx->m_StickCalData[0].axis[1].sMin = (pStickCal[8] << 4) | (pStickCal[7] >> 4); /* Y Axis min below center */
/* Right stick */
ctx->m_StickCalData[1].axis[0].sCenter = ((pStickCal[10] << 8) & 0xF00) | pStickCal[9]; /* X Axis center */
ctx->m_StickCalData[1].axis[1].sCenter = (pStickCal[11] << 4) | (pStickCal[10] >> 4); /* Y Axis center */
ctx->m_StickCalData[1].axis[0].sMin = ((pStickCal[13] << 8) & 0xF00) | pStickCal[12]; /* X Axis min below center */
ctx->m_StickCalData[1].axis[1].sMin = (pStickCal[14] << 4) | (pStickCal[13] >> 4); /* Y Axis min below center */
ctx->m_StickCalData[1].axis[0].sMax = ((pStickCal[16] << 8) & 0xF00) | pStickCal[15]; /* X Axis max above center */
ctx->m_StickCalData[1].axis[1].sMax = (pStickCal[17] << 4) | (pStickCal[16] >> 4); /* Y Axis max above center */
/* Filter out any values that were uninitialized (0xFFF) in the SPI read */
for (stick = 0; stick < 2; ++stick) {
for (axis = 0; axis < 2; ++axis) {
if (ctx->m_StickCalData[stick].axis[axis].sCenter == 0xFFF) {
ctx->m_StickCalData[stick].axis[axis].sCenter = 0;
}
if (ctx->m_StickCalData[stick].axis[axis].sMax == 0xFFF) {
ctx->m_StickCalData[stick].axis[axis].sMax = 0;
}
if (ctx->m_StickCalData[stick].axis[axis].sMin == 0xFFF) {
ctx->m_StickCalData[stick].axis[axis].sMin = 0;
}
}
}
Oct 18, 2019
Oct 18, 2019
580
if (ctx->m_bUsingBluetooth) {
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
for (stick = 0; stick < 2; ++stick) {
for(axis = 0; axis < 2; ++axis) {
ctx->m_StickExtents[stick].axis[axis].sMin = (Sint16)(SDL_MIN_SINT16 * 0.5f);
ctx->m_StickExtents[stick].axis[axis].sMax = (Sint16)(SDL_MAX_SINT16 * 0.5f);
}
}
} else {
for (stick = 0; stick < 2; ++stick) {
for(axis = 0; axis < 2; ++axis) {
ctx->m_StickExtents[stick].axis[axis].sMin = -(Sint16)(ctx->m_StickCalData[stick].axis[axis].sMin * 0.7f);
ctx->m_StickExtents[stick].axis[axis].sMax = (Sint16)(ctx->m_StickCalData[stick].axis[axis].sMax * 0.7f);
}
}
}
return SDL_TRUE;
}
static float fsel(float fComparand, float fValGE, float fLT)
{
return fComparand >= 0 ? fValGE : fLT;
}
static float RemapVal(float val, float A, float B, float C, float D)
{
if (A == B) {
return fsel(val - B , D , C);
}
return C + (D - C) * (val - A) / (B - A);
}
static Sint16 ApplyStickCalibrationCentered(SDL_DriverSwitch_Context *ctx, int nStick, int nAxis, Sint16 sRawValue, Sint16 sCenter)
{
sRawValue -= sCenter;
if (sRawValue > ctx->m_StickExtents[nStick].axis[nAxis].sMax) {
ctx->m_StickExtents[nStick].axis[nAxis].sMax = sRawValue;
}
if (sRawValue < ctx->m_StickExtents[nStick].axis[nAxis].sMin) {
ctx->m_StickExtents[nStick].axis[nAxis].sMin = sRawValue;
}
if (sRawValue > 0) {
return (Sint16)(RemapVal(sRawValue, 0, ctx->m_StickExtents[nStick].axis[nAxis].sMax, 0, SDL_MAX_SINT16));
} else {
return (Sint16)(RemapVal(sRawValue, ctx->m_StickExtents[nStick].axis[nAxis].sMin, 0, SDL_MIN_SINT16, 0));
}
}
static Sint16 ApplyStickCalibration(SDL_DriverSwitch_Context *ctx, int nStick, int nAxis, Sint16 sRawValue)
{
return ApplyStickCalibrationCentered(ctx, nStick, nAxis, sRawValue, ctx->m_StickCalData[nStick].axis[nAxis].sCenter);
}
Oct 18, 2019
Oct 18, 2019
634
635
636
static void SDLCALL SDL_GameControllerButtonReportingHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
{
SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)userdata;
Nov 14, 2019
Nov 14, 2019
637
ctx->m_bUseButtonLabels = SDL_GetStringBoolean(hint, SDL_TRUE);
Oct 18, 2019
Oct 18, 2019
638
639
640
641
}
static Uint8 RemapButton(SDL_DriverSwitch_Context *ctx, Uint8 button)
{
Mar 11, 2020
Mar 11, 2020
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
if (!ctx->m_bUseButtonLabels) {
/* Use button positions */
if (ctx->m_bIsGameCube) {
switch (button) {
case SDL_CONTROLLER_BUTTON_B:
return SDL_CONTROLLER_BUTTON_X;
case SDL_CONTROLLER_BUTTON_X:
return SDL_CONTROLLER_BUTTON_B;
default:
break;
}
} else {
switch (button) {
case SDL_CONTROLLER_BUTTON_A:
return SDL_CONTROLLER_BUTTON_B;
case SDL_CONTROLLER_BUTTON_B:
return SDL_CONTROLLER_BUTTON_A;
case SDL_CONTROLLER_BUTTON_X:
return SDL_CONTROLLER_BUTTON_Y;
case SDL_CONTROLLER_BUTTON_Y:
return SDL_CONTROLLER_BUTTON_X;
default:
break;
}
Oct 18, 2019
Oct 18, 2019
666
667
668
669
}
}
return button;
}
Dec 19, 2019
Dec 19, 2019
670
671
672
673
static SDL_bool
HIDAPI_DriverSwitch_InitDevice(SDL_HIDAPI_Device *device)
{
Mar 16, 2020
Mar 16, 2020
674
return HIDAPI_JoystickConnected(device, NULL, SDL_FALSE);
Dec 19, 2019
Dec 19, 2019
675
676
}
Dec 21, 2019
Dec 21, 2019
677
678
679
680
681
682
683
684
685
686
687
static int
HIDAPI_DriverSwitch_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
{
return -1;
}
static void
HIDAPI_DriverSwitch_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
{
}
Dec 19, 2019
Dec 19, 2019
689
HIDAPI_DriverSwitch_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
690
691
692
693
694
695
696
{
SDL_DriverSwitch_Context *ctx;
Uint8 input_mode;
ctx = (SDL_DriverSwitch_Context *)SDL_calloc(1, sizeof(*ctx));
if (!ctx) {
SDL_OutOfMemory();
Dec 19, 2019
Dec 19, 2019
697
goto error;
Feb 7, 2020
Feb 7, 2020
699
ctx->device = device;
Dec 19, 2019
Dec 19, 2019
700
device->context = ctx;
Feb 7, 2020
Feb 7, 2020
702
device->dev = hid_open_path(device->path, 0);
Dec 19, 2019
Dec 19, 2019
703
704
705
706
if (!device->dev) {
SDL_SetError("Couldn't open %s", device->path);
goto error;
}
Oct 17, 2019
Oct 17, 2019
708
/* Find out whether or not we can send output reports */
Dec 19, 2019
Dec 19, 2019
709
ctx->m_bInputOnly = SDL_IsJoystickNintendoSwitchProInputOnly(device->vendor_id, device->product_id);
Oct 18, 2019
Oct 18, 2019
710
if (!ctx->m_bInputOnly) {
Oct 22, 2019
Oct 22, 2019
711
/* The Power A Nintendo Switch Pro controllers don't have a Home LED */
Dec 19, 2019
Dec 19, 2019
712
ctx->m_bHasHomeLED = (device->vendor_id != 0 && device->product_id != 0) ? SDL_TRUE : SDL_FALSE;
Oct 22, 2019
Oct 22, 2019
713
Oct 17, 2019
Oct 17, 2019
714
715
716
/* Initialize rumble data */
SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[0]);
SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[1]);
Oct 17, 2019
Oct 17, 2019
718
719
/* Try setting up USB mode, and if that fails we're using Bluetooth */
if (!BTrySetupUSB(ctx)) {
Oct 18, 2019
Oct 18, 2019
720
ctx->m_bUsingBluetooth = SDL_TRUE;
Oct 17, 2019
Oct 17, 2019
721
}
Oct 17, 2019
Oct 17, 2019
723
724
if (!LoadStickCalibration(ctx)) {
SDL_SetError("Couldn't load stick calibration");
Dec 19, 2019
Dec 19, 2019
725
goto error;
Oct 17, 2019
Oct 17, 2019
726
}
Oct 17, 2019
Oct 17, 2019
728
729
if (!SetVibrationEnabled(ctx, 1)) {
SDL_SetError("Couldn't enable vibration");
Dec 19, 2019
Dec 19, 2019
730
goto error;
Oct 17, 2019
Oct 17, 2019
731
}
Oct 17, 2019
Oct 17, 2019
733
/* Set the desired input mode */
Oct 18, 2019
Oct 18, 2019
734
if (ctx->m_bUsingBluetooth) {
Oct 17, 2019
Oct 17, 2019
735
736
737
738
739
740
input_mode = k_eSwitchInputReportIDs_SimpleControllerState;
} else {
input_mode = k_eSwitchInputReportIDs_FullControllerState;
}
if (!SetInputMode(ctx, input_mode)) {
SDL_SetError("Couldn't set input mode");
Dec 19, 2019
Dec 19, 2019
741
goto error;
Oct 17, 2019
Oct 17, 2019
744
/* Start sending USB reports */
Oct 18, 2019
Oct 18, 2019
745
if (!ctx->m_bUsingBluetooth) {
Oct 17, 2019
Oct 17, 2019
746
747
748
/* ForceUSB doesn't generate an ACK, so don't wait for a reply */
if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_ForceUSB, NULL, 0, SDL_FALSE)) {
SDL_SetError("Couldn't start USB reports");
Dec 19, 2019
Dec 19, 2019
749
goto error;
Oct 17, 2019
Oct 17, 2019
750
751
752
753
}
}
/* Set the LED state */
Oct 22, 2019
Oct 22, 2019
754
755
756
if (ctx->m_bHasHomeLED) {
SetHomeLED(ctx, 100);
}
Oct 17, 2019
Oct 17, 2019
757
758
SetSlotLED(ctx, (joystick->instance_id % 4));
}
Dec 19, 2019
Dec 19, 2019
760
if (IsGameCubeFormFactor(device->vendor_id, device->product_id)) {
Oct 18, 2019
Oct 18, 2019
761
/* This is a controller shaped like a GameCube controller, with a large central A button */
Mar 11, 2020
Mar 11, 2020
762
ctx->m_bIsGameCube = SDL_TRUE;
Oct 18, 2019
Oct 18, 2019
763
764
}
Mar 11, 2020
Mar 11, 2020
765
766
767
SDL_AddHintCallback(SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS,
SDL_GameControllerButtonReportingHintChanged, ctx);
768
769
770
771
772
773
/* Initialize the joystick capabilities */
joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX;
joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
return SDL_TRUE;
Dec 19, 2019
Dec 19, 2019
774
775
776
777
778
779
780
781
782
783
784
error:
if (device->dev) {
hid_close(device->dev);
device->dev = NULL;
}
if (device->context) {
SDL_free(device->context);
device->context = NULL;
}
return SDL_FALSE;
Feb 4, 2020
Feb 4, 2020
788
HIDAPI_DriverSwitch_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
Dec 19, 2019
Dec 19, 2019
790
SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
/* Experimentally determined rumble values. These will only matter on some controllers as tested ones
* seem to disregard these and just use any non-zero rumble values as a binary flag for constant rumble
*
* More information about these values can be found here:
* https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md
*/
const Uint16 k_usHighFreq = 0x0074;
const Uint8 k_ucHighFreqAmp = 0xBE;
const Uint8 k_ucLowFreq = 0x3D;
const Uint16 k_usLowFreqAmp = 0x806F;
if (low_frequency_rumble) {
EncodeRumble(&ctx->m_RumblePacket.rumbleData[0], k_usHighFreq, k_ucHighFreqAmp, k_ucLowFreq, k_usLowFreqAmp);
} else {
SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[0]);
}
if (high_frequency_rumble) {
EncodeRumble(&ctx->m_RumblePacket.rumbleData[1], k_usHighFreq, k_ucHighFreqAmp, k_ucLowFreq, k_usLowFreqAmp);
} else {
SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[1]);
}
Feb 7, 2020
Feb 7, 2020
815
816
ctx->m_bRumbleActive = (low_frequency_rumble || high_frequency_rumble) ? SDL_TRUE : SDL_FALSE;
817
818
819
820
821
822
823
if (!WriteRumble(ctx)) {
SDL_SetError("Couldn't send rumble packet");
return -1;
}
return 0;
}
Oct 17, 2019
Oct 17, 2019
824
825
826
827
828
829
static void HandleInputOnlyControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchInputOnlyControllerStatePacket_t *packet)
{
Sint16 axis;
if (packet->rgucButtons[0] != ctx->m_lastInputOnlyState.rgucButtons[0]) {
Uint8 data = packet->rgucButtons[0];
Mar 11, 2020
Mar 11, 2020
830
831
832
833
SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_A), (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_B), (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_X), (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_Y), (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
Oct 17, 2019
Oct 17, 2019
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data & 0x20) ? SDL_PRESSED : SDL_RELEASED);
axis = (data & 0x40) ? 32767 : -32768;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
axis = (data & 0x80) ? 32767 : -32768;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
}
if (packet->rgucButtons[1] != ctx->m_lastInputOnlyState.rgucButtons[1]) {
Uint8 data = packet->rgucButtons[1];
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
}
if (packet->ucStickHat != ctx->m_lastInputOnlyState.ucStickHat) {
SDL_bool dpad_up = SDL_FALSE;
SDL_bool dpad_down = SDL_FALSE;
SDL_bool dpad_left = SDL_FALSE;
SDL_bool dpad_right = SDL_FALSE;
switch (packet->ucStickHat) {
case 0:
dpad_up = SDL_TRUE;
break;
case 1:
dpad_up = SDL_TRUE;
dpad_right = SDL_TRUE;
break;
case 2:
dpad_right = SDL_TRUE;
break;
case 3:
dpad_right = SDL_TRUE;
dpad_down = SDL_TRUE;
break;
case 4:
dpad_down = SDL_TRUE;
break;
case 5:
dpad_left = SDL_TRUE;
dpad_down = SDL_TRUE;
break;
case 6:
dpad_left = SDL_TRUE;
break;
case 7:
dpad_up = SDL_TRUE;
dpad_left = SDL_TRUE;
break;
default:
break;
}
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, dpad_down);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, dpad_up);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, dpad_right);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, dpad_left);
}
if (packet->rgucJoystickLeft[0] != ctx->m_lastInputOnlyState.rgucJoystickLeft[0]) {
axis = (Sint16)(RemapVal(packet->rgucJoystickLeft[0], SDL_MIN_UINT8, SDL_MAX_UINT8, SDL_MIN_SINT16, SDL_MAX_SINT16));
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
}
if (packet->rgucJoystickLeft[1] != ctx->m_lastInputOnlyState.rgucJoystickLeft[1]) {
axis = (Sint16)(RemapVal(packet->rgucJoystickLeft[1], SDL_MIN_UINT8, SDL_MAX_UINT8, SDL_MIN_SINT16, SDL_MAX_SINT16));
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
}
if (packet->rgucJoystickRight[0] != ctx->m_lastInputOnlyState.rgucJoystickRight[0]) {
axis = (Sint16)(RemapVal(packet->rgucJoystickRight[0], SDL_MIN_UINT8, SDL_MAX_UINT8, SDL_MIN_SINT16, SDL_MAX_SINT16));
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
}
if (packet->rgucJoystickRight[1] != ctx->m_lastInputOnlyState.rgucJoystickRight[1]) {
axis = (Sint16)(RemapVal(packet->rgucJoystickRight[1], SDL_MIN_UINT8, SDL_MAX_UINT8, SDL_MIN_SINT16, SDL_MAX_SINT16));
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
}
ctx->m_lastInputOnlyState = *packet;
}
920
921
922
923
924
925
926
927
static void HandleSimpleControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchSimpleStatePacket_t *packet)
{
/* 0x8000 is the neutral value for all joystick axes */
const Uint16 usJoystickCenter = 0x8000;
Sint16 axis;
if (packet->rgucButtons[0] != ctx->m_lastSimpleState.rgucButtons[0]) {
Uint8 data = packet->rgucButtons[0];
Oct 18, 2019
Oct 18, 2019
928
929
930
931
SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_A), (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_B), (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_X), (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_Y), (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data & 0x20) ? SDL_PRESSED : SDL_RELEASED);
axis = (data & 0x40) ? 32767 : -32768;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
axis = (data & 0x80) ? 32767 : -32768;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
}
if (packet->rgucButtons[1] != ctx->m_lastSimpleState.rgucButtons[1]) {
Uint8 data = packet->rgucButtons[1];
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
}
if (packet->ucStickHat != ctx->m_lastSimpleState.ucStickHat) {
SDL_bool dpad_up = SDL_FALSE;
SDL_bool dpad_down = SDL_FALSE;
SDL_bool dpad_left = SDL_FALSE;
SDL_bool dpad_right = SDL_FALSE;
switch (packet->ucStickHat) {
case 0:
dpad_up = SDL_TRUE;
break;
case 1:
dpad_up = SDL_TRUE;
dpad_right = SDL_TRUE;
break;
case 2:
dpad_right = SDL_TRUE;
break;
case 3:
dpad_right = SDL_TRUE;
dpad_down = SDL_TRUE;
break;
case 4:
dpad_down = SDL_TRUE;
break;
case 5:
dpad_left = SDL_TRUE;
dpad_down = SDL_TRUE;
break;
case 6:
dpad_left = SDL_TRUE;
break;
case 7:
dpad_up = SDL_TRUE;
dpad_left = SDL_TRUE;
break;
default:
break;
}
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, dpad_down);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, dpad_up);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, dpad_right);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, dpad_left);
}
axis = ApplyStickCalibrationCentered(ctx, 0, 0, packet->sJoystickLeft[0], (Sint16)usJoystickCenter);
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
axis = ApplyStickCalibrationCentered(ctx, 0, 1, packet->sJoystickLeft[1], (Sint16)usJoystickCenter);
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);