소스 검색

Added support for the player LED on Nintendo Switch 2 controllers

Sam Lantinga 2 주 전
부모
커밋
1f007ad5cd
1개의 변경된 파일70개의 추가작업 그리고 11개의 파일을 삭제
  1. 70 11
      src/joystick/hidapi/SDL_hidapi_switch2.c

+ 70 - 11
src/joystick/hidapi/SDL_hidapi_switch2.c

@@ -85,6 +85,9 @@ typedef struct
 
 typedef struct
 {
+    SDL_HIDAPI_Device *device;
+    SDL_Joystick *joystick;
+
     SDL_LibUSBContext *libusb;
     libusb_device_handle *device_handle;
     bool interface_claimed;
@@ -97,6 +100,9 @@ typedef struct
     Uint8 left_trigger_max;
     Uint8 right_trigger_max;
 
+    bool player_lights;
+    int player_index;
+
     bool vertical_mode;
     Uint8 last_state[USB_PACKET_LENGTH];
 } SDL_DriverSwitch2_Context;
@@ -198,6 +204,37 @@ static void MapTriggerAxis(Uint64 timestamp, SDL_Joystick *joystick, Uint8 axis,
     SDL_SendJoystickAxis(timestamp, joystick, axis, mapped_value);
 }
 
+static bool UpdateSlotLED(SDL_DriverSwitch2_Context *ctx)
+{
+    unsigned char SET_LED_DATA[] = {
+        0x09, 0x91, 0x00, 0x07, 0x00, 0x08, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    };
+    unsigned char calibration_data[0x50] = {0};
+
+    if (ctx->player_lights && ctx->player_index >= 0) {
+        SET_LED_DATA[8] = (1 << (ctx->player_index % 4));
+    }
+    int res = SendBulkData(ctx, SET_LED_DATA, sizeof(SET_LED_DATA));
+    if (res < 0) {
+        return SDL_SetError("Couldn't set LED data: %d\n", res);
+    }
+    return (RecvBulkData(ctx, calibration_data, 0x40) > 0);
+}
+
+static void SDLCALL SDL_PlayerLEDHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
+{
+    SDL_DriverSwitch2_Context *ctx = (SDL_DriverSwitch2_Context *)userdata;
+    bool player_lights = SDL_GetStringBoolean(hint, true);
+
+    if (player_lights != ctx->player_lights) {
+        ctx->player_lights = player_lights;
+
+        UpdateSlotLED(ctx);
+        HIDAPI_UpdateDeviceProperties(ctx->device);
+    }
+}
+
 static void HIDAPI_DriverSwitch2_RegisterHints(SDL_HintCallback callback, void *userdata)
 {
     SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SWITCH2, callback, userdata);
@@ -300,10 +337,6 @@ static bool HIDAPI_DriverSwitch2_InitUSB(SDL_HIDAPI_Device *device)
         0x03, 0x91, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x00,
         0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
     };
-    const unsigned char SET_LED_DATA[] = {
-        0x09, 0x91, 0x00, 0x07, 0x00, 0x08, 0x00, 0x00,
-        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-    };
     unsigned char flash_read_command[] = {
         0x02, 0x91, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00,
         0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x01, 0x00
@@ -316,12 +349,6 @@ static bool HIDAPI_DriverSwitch2_InitUSB(SDL_HIDAPI_Device *device)
     }
     RecvBulkData(ctx, calibration_data, 0x40);
 
-    res = SendBulkData(ctx, SET_LED_DATA, sizeof(SET_LED_DATA));
-    if (res < 0) {
-        return SDL_SetError("Couldn't set LED data: %d\n", res);
-    }
-    RecvBulkData(ctx, calibration_data, 0x40);
-
     flash_read_command[12] = 0x80;
     res = SendBulkData(ctx, flash_read_command, sizeof(flash_read_command));
     if (res < 0) {
@@ -377,6 +404,7 @@ static bool HIDAPI_DriverSwitch2_InitDevice(SDL_HIDAPI_Device *device)
     if (!ctx) {
         return false;
     }
+    ctx->device = device;
     device->context = ctx;
 
     if (device->is_bluetooth) {
@@ -410,12 +438,31 @@ static int HIDAPI_DriverSwitch2_GetDevicePlayerIndex(SDL_HIDAPI_Device *device,
 
 static void HIDAPI_DriverSwitch2_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
 {
+    SDL_DriverSwitch2_Context *ctx = (SDL_DriverSwitch2_Context *)device->context;
+
+    if (!ctx->joystick) {
+        return;
+    }
+
+    ctx->player_index = player_index;
+
+    UpdateSlotLED(ctx);
 }
 
 static bool HIDAPI_DriverSwitch2_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
 {
     SDL_DriverSwitch2_Context *ctx = (SDL_DriverSwitch2_Context *)device->context;
 
+    ctx->joystick = joystick;
+
+    // Initialize player index (needed for setting LEDs)
+    ctx->player_index = SDL_GetJoystickPlayerIndex(joystick);
+    ctx->player_lights = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED, true);
+    UpdateSlotLED(ctx);
+
+    SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED,
+                        SDL_PlayerLEDHintChanged, ctx);
+
     // Initialize the joystick capabilities
     switch (device->product_id) {
     case USB_PRODUCT_NINTENDO_SWITCH2_GAMECUBE_CONTROLLER:
@@ -453,7 +500,13 @@ static bool HIDAPI_DriverSwitch2_RumbleJoystickTriggers(SDL_HIDAPI_Device *devic
 
 static Uint32 HIDAPI_DriverSwitch2_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
 {
-    return 0;
+    SDL_DriverSwitch2_Context *ctx = (SDL_DriverSwitch2_Context *)device->context;
+    Uint32 result = 0;
+
+    if (ctx->player_lights) {
+        result |= SDL_JOYSTICK_CAP_PLAYER_LED;
+    }
+    return result;
 }
 
 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)
 
 static void HIDAPI_DriverSwitch2_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
 {
+    SDL_DriverSwitch2_Context *ctx = (SDL_DriverSwitch2_Context *)device->context;
+
+    SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED,
+                           SDL_PlayerLEDHintChanged, ctx);
+
+    ctx->joystick = NULL;
 }
 
 static void HIDAPI_DriverSwitch2_FreeDevice(SDL_HIDAPI_Device *device)