Skip to content

Commit 8f95c51

Browse files
committed
Added support for the player LED on Nintendo Switch 2 controllers
1 parent bd49de1 commit 8f95c51

File tree

1 file changed

+70
-11
lines changed

1 file changed

+70
-11
lines changed

src/joystick/hidapi/SDL_hidapi_switch2.c

Lines changed: 70 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ typedef struct
8585

8686
typedef struct
8787
{
88+
SDL_HIDAPI_Device *device;
89+
SDL_Joystick *joystick;
90+
8891
SDL_LibUSBContext *libusb;
8992
libusb_device_handle *device_handle;
9093
bool interface_claimed;
@@ -97,6 +100,9 @@ typedef struct
97100
Uint8 left_trigger_max;
98101
Uint8 right_trigger_max;
99102

103+
bool player_lights;
104+
int player_index;
105+
100106
bool vertical_mode;
101107
Uint8 last_state[USB_PACKET_LENGTH];
102108
} SDL_DriverSwitch2_Context;
@@ -198,6 +204,37 @@ static void MapTriggerAxis(Uint64 timestamp, SDL_Joystick *joystick, Uint8 axis,
198204
SDL_SendJoystickAxis(timestamp, joystick, axis, mapped_value);
199205
}
200206

207+
static bool UpdateSlotLED(SDL_DriverSwitch2_Context *ctx)
208+
{
209+
unsigned char SET_LED_DATA[] = {
210+
0x09, 0x91, 0x00, 0x07, 0x00, 0x08, 0x00, 0x00,
211+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
212+
};
213+
unsigned char calibration_data[0x50] = {0};
214+
215+
if (ctx->player_lights && ctx->player_index >= 0) {
216+
SET_LED_DATA[8] = (1 << (ctx->player_index % 4));
217+
}
218+
int res = SendBulkData(ctx, SET_LED_DATA, sizeof(SET_LED_DATA));
219+
if (res < 0) {
220+
return SDL_SetError("Couldn't set LED data: %d\n", res);
221+
}
222+
return (RecvBulkData(ctx, calibration_data, 0x40) > 0);
223+
}
224+
225+
static void SDLCALL SDL_PlayerLEDHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
226+
{
227+
SDL_DriverSwitch2_Context *ctx = (SDL_DriverSwitch2_Context *)userdata;
228+
bool player_lights = SDL_GetStringBoolean(hint, true);
229+
230+
if (player_lights != ctx->player_lights) {
231+
ctx->player_lights = player_lights;
232+
233+
UpdateSlotLED(ctx);
234+
HIDAPI_UpdateDeviceProperties(ctx->device);
235+
}
236+
}
237+
201238
static void HIDAPI_DriverSwitch2_RegisterHints(SDL_HintCallback callback, void *userdata)
202239
{
203240
SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SWITCH2, callback, userdata);
@@ -300,10 +337,6 @@ static bool HIDAPI_DriverSwitch2_InitUSB(SDL_HIDAPI_Device *device)
300337
0x03, 0x91, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x00,
301338
0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
302339
};
303-
const unsigned char SET_LED_DATA[] = {
304-
0x09, 0x91, 0x00, 0x07, 0x00, 0x08, 0x00, 0x00,
305-
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
306-
};
307340
unsigned char flash_read_command[] = {
308341
0x02, 0x91, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00,
309342
0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x01, 0x00
@@ -316,12 +349,6 @@ static bool HIDAPI_DriverSwitch2_InitUSB(SDL_HIDAPI_Device *device)
316349
}
317350
RecvBulkData(ctx, calibration_data, 0x40);
318351

319-
res = SendBulkData(ctx, SET_LED_DATA, sizeof(SET_LED_DATA));
320-
if (res < 0) {
321-
return SDL_SetError("Couldn't set LED data: %d\n", res);
322-
}
323-
RecvBulkData(ctx, calibration_data, 0x40);
324-
325352
flash_read_command[12] = 0x80;
326353
res = SendBulkData(ctx, flash_read_command, sizeof(flash_read_command));
327354
if (res < 0) {
@@ -377,6 +404,7 @@ static bool HIDAPI_DriverSwitch2_InitDevice(SDL_HIDAPI_Device *device)
377404
if (!ctx) {
378405
return false;
379406
}
407+
ctx->device = device;
380408
device->context = ctx;
381409

382410
if (device->is_bluetooth) {
@@ -410,12 +438,31 @@ static int HIDAPI_DriverSwitch2_GetDevicePlayerIndex(SDL_HIDAPI_Device *device,
410438

411439
static void HIDAPI_DriverSwitch2_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
412440
{
441+
SDL_DriverSwitch2_Context *ctx = (SDL_DriverSwitch2_Context *)device->context;
442+
443+
if (!ctx->joystick) {
444+
return;
445+
}
446+
447+
ctx->player_index = player_index;
448+
449+
UpdateSlotLED(ctx);
413450
}
414451

415452
static bool HIDAPI_DriverSwitch2_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
416453
{
417454
SDL_DriverSwitch2_Context *ctx = (SDL_DriverSwitch2_Context *)device->context;
418455

456+
ctx->joystick = joystick;
457+
458+
// Initialize player index (needed for setting LEDs)
459+
ctx->player_index = SDL_GetJoystickPlayerIndex(joystick);
460+
ctx->player_lights = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED, true);
461+
UpdateSlotLED(ctx);
462+
463+
SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED,
464+
SDL_PlayerLEDHintChanged, ctx);
465+
419466
// Initialize the joystick capabilities
420467
switch (device->product_id) {
421468
case USB_PRODUCT_NINTENDO_SWITCH2_GAMECUBE_CONTROLLER:
@@ -453,7 +500,13 @@ static bool HIDAPI_DriverSwitch2_RumbleJoystickTriggers(SDL_HIDAPI_Device *devic
453500

454501
static Uint32 HIDAPI_DriverSwitch2_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
455502
{
456-
return 0;
503+
SDL_DriverSwitch2_Context *ctx = (SDL_DriverSwitch2_Context *)device->context;
504+
Uint32 result = 0;
505+
506+
if (ctx->player_lights) {
507+
result |= SDL_JOYSTICK_CAP_PLAYER_LED;
508+
}
509+
return result;
457510
}
458511

459512
static bool HIDAPI_DriverSwitch2_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
@@ -886,6 +939,12 @@ static bool HIDAPI_DriverSwitch2_UpdateDevice(SDL_HIDAPI_Device *device)
886939

887940
static void HIDAPI_DriverSwitch2_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
888941
{
942+
SDL_DriverSwitch2_Context *ctx = (SDL_DriverSwitch2_Context *)device->context;
943+
944+
SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED,
945+
SDL_PlayerLEDHintChanged, ctx);
946+
947+
ctx->joystick = NULL;
889948
}
890949

891950
static void HIDAPI_DriverSwitch2_FreeDevice(SDL_HIDAPI_Device *device)

0 commit comments

Comments
 (0)