Atlas - SDL_hidapi_gamecube.c
Home / ext / SDL / src / joystick / hidapi Lines: 1 | Size: 23217 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]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#include "SDL_internal.h" 22 23#ifdef SDL_JOYSTICK_HIDAPI 24 25#include "../../SDL_hints_c.h" 26#include "../../misc/SDL_libusb.h" 27#include "../SDL_sysjoystick.h" 28#include "SDL_hidapijoystick_c.h" 29#include "SDL_hidapi_rumble.h" 30#include "../../hidapi/SDL_hidapi_c.h" 31 32#ifdef SDL_JOYSTICK_HIDAPI_GAMECUBE 33 34// Define this if you want to log all packets from the controller 35#if 0 36#define DEBUG_GAMECUBE_PROTOCOL 37#endif 38 39#define MAX_CONTROLLERS 4 40 41typedef struct 42{ 43 bool pc_mode; 44 SDL_JoystickID joysticks[MAX_CONTROLLERS]; 45 Uint8 wireless[MAX_CONTROLLERS]; 46 Uint8 min_axis[MAX_CONTROLLERS * SDL_GAMEPAD_AXIS_COUNT]; 47 Uint8 max_axis[MAX_CONTROLLERS * SDL_GAMEPAD_AXIS_COUNT]; 48 Uint8 rumbleAllowed[MAX_CONTROLLERS]; 49 Uint8 rumble[1 + MAX_CONTROLLERS]; 50 // Without this variable, hid_write starts to lag a TON 51 bool rumbleUpdate; 52 bool useRumbleBrake; 53} SDL_DriverGameCube_Context; 54 55static void HIDAPI_DriverGameCube_RegisterHints(SDL_HintCallback callback, void *userdata) 56{ 57 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE, callback, userdata); 58} 59 60static void HIDAPI_DriverGameCube_UnregisterHints(SDL_HintCallback callback, void *userdata) 61{ 62 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE, callback, userdata); 63} 64 65static bool HIDAPI_DriverGameCube_IsEnabled(void) 66{ 67 return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE, 68 SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI, 69 SDL_HIDAPI_DEFAULT)); 70} 71 72static bool HIDAPI_DriverGameCube_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) 73{ 74 if (vendor_id == USB_VENDOR_NINTENDO && product_id == USB_PRODUCT_NINTENDO_GAMECUBE_ADAPTER) { 75 // Nintendo Co., Ltd. Wii U GameCube Controller Adapter 76 return true; 77 } 78 if (vendor_id == USB_VENDOR_DRAGONRISE && 79 (product_id == USB_PRODUCT_EVORETRO_GAMECUBE_ADAPTER1 || 80 product_id == USB_PRODUCT_EVORETRO_GAMECUBE_ADAPTER2 || 81 product_id == USB_PRODUCT_EVORETRO_GAMECUBE_ADAPTER3)) { 82 // EVORETRO GameCube Controller Adapter 83 return true; 84 } 85 return false; 86} 87 88static void ResetAxisRange(SDL_DriverGameCube_Context *ctx, int joystick_index) 89{ 90 SDL_memset(&ctx->min_axis[joystick_index * SDL_GAMEPAD_AXIS_COUNT], 128 - 88, SDL_GAMEPAD_AXIS_COUNT); 91 SDL_memset(&ctx->max_axis[joystick_index * SDL_GAMEPAD_AXIS_COUNT], 128 + 88, SDL_GAMEPAD_AXIS_COUNT); 92 93 // Trigger axes may have a higher resting value 94 ctx->min_axis[joystick_index * SDL_GAMEPAD_AXIS_COUNT + SDL_GAMEPAD_AXIS_LEFT_TRIGGER] = 40; 95 ctx->min_axis[joystick_index * SDL_GAMEPAD_AXIS_COUNT + SDL_GAMEPAD_AXIS_RIGHT_TRIGGER] = 40; 96} 97 98static void SDLCALL SDL_JoystickGameCubeRumbleBrakeHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 99{ 100 if (hint) { 101 SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)userdata; 102 ctx->useRumbleBrake = SDL_GetStringBoolean(hint, false); 103 } 104} 105 106static bool HIDAPI_DriverGameCube_EnableAdapter(SDL_HIDAPI_Device *device) 107{ 108#ifdef HAVE_LIBUSB 109 // Need to close the device while sending USB commands to it 110 SDL_hid_close(device->dev); 111 112 // This is needed to enable input for Nyko and EVORETRO GameCube adapters 113 SDL_LibUSBContext *libusb_ctx; 114 if (SDL_InitLibUSB(&libusb_ctx)) { 115 libusb_context *context = NULL; 116 libusb_device **devs = NULL; 117 libusb_device_handle *handle = NULL; 118 struct libusb_device_descriptor desc; 119 ssize_t i, num_devs; 120 bool kernel_detached = false; 121 122 if (libusb_ctx->init(&context) == 0) { 123 num_devs = libusb_ctx->get_device_list(context, &devs); 124 for (i = 0; i < num_devs; ++i) { 125 if (libusb_ctx->get_device_descriptor(devs[i], &desc) != 0) { 126 continue; 127 } 128 129 if (desc.idVendor != USB_VENDOR_NINTENDO || 130 desc.idProduct != USB_PRODUCT_NINTENDO_GAMECUBE_ADAPTER) { 131 continue; 132 } 133 134 if (libusb_ctx->open(devs[i], &handle) != 0) { 135 continue; 136 } 137 138 if (libusb_ctx->kernel_driver_active(handle, 0)) { 139 if (libusb_ctx->detach_kernel_driver(handle, 0) == 0) { 140 kernel_detached = true; 141 } 142 } 143 144 if (libusb_ctx->claim_interface(handle, 0) == 0) { 145 libusb_ctx->control_transfer(handle, 0x21, 11, 0x0001, 0, NULL, 0, 1000); 146 libusb_ctx->release_interface(handle, 0); 147 } 148 149 if (kernel_detached) { 150 libusb_ctx->attach_kernel_driver(handle, 0); 151 } 152 153 libusb_ctx->close(handle); 154 } 155 156 libusb_ctx->free_device_list(devs, 1); 157 158 libusb_ctx->exit(context); 159 } 160 SDL_QuitLibUSB(); 161 } 162 163 // Reopen the device now that we're done 164 device->dev = SDL_hid_open_path(device->path); 165 if (!device->dev) { 166 return false; 167 } 168#endif // HAVE_LIBUSB 169 170 Uint8 initMagic = 0x13; 171 if (SDL_hid_write(device->dev, &initMagic, sizeof(initMagic)) != sizeof(initMagic)) { 172 SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, 173 "HIDAPI_DriverGameCube_InitDevice(): Couldn't initialize WUP-028"); 174 return false; 175 } 176 177 // Wait for the adapter to initialize 178 SDL_Delay(10); 179 180 return true; 181} 182 183static bool HIDAPI_DriverGameCube_InitDevice(SDL_HIDAPI_Device *device) 184{ 185 SDL_DriverGameCube_Context *ctx; 186 Uint8 packet[37]; 187 Uint8 *curSlot; 188 Uint8 i; 189 int size; 190 Uint8 rumbleMagic = 0x11; 191 192 ctx = (SDL_DriverGameCube_Context *)SDL_calloc(1, sizeof(*ctx)); 193 if (!ctx) { 194 return false; 195 } 196 device->context = ctx; 197 198 ctx->rumble[0] = rumbleMagic; 199 200 if (device->vendor_id != USB_VENDOR_NINTENDO) { 201 ctx->pc_mode = true; 202 } 203 204 if (ctx->pc_mode) { 205 ResetAxisRange(ctx, 0); 206 HIDAPI_JoystickConnected(device, &ctx->joysticks[0]); 207 } else { 208 if (!HIDAPI_DriverGameCube_EnableAdapter(device)) { 209 return false; 210 } 211 212 // Add all the applicable joysticks 213 while ((size = SDL_hid_read_timeout(device->dev, packet, sizeof(packet), 0)) > 0) { 214#ifdef DEBUG_GAMECUBE_PROTOCOL 215 HIDAPI_DumpPacket("Nintendo GameCube packet: size = %d", packet, size); 216#endif 217 if (size < 37 || packet[0] != 0x21) { 218 continue; // Nothing to do yet...? 219 } 220 221 // Go through all 4 slots 222 curSlot = packet + 1; 223 for (i = 0; i < MAX_CONTROLLERS; i += 1, curSlot += 9) { 224 ctx->wireless[i] = (curSlot[0] & 0x20) != 0; 225 226 // Only allow rumble if the adapter's second USB cable is connected 227 ctx->rumbleAllowed[i] = (curSlot[0] & 0x04) && !ctx->wireless[i]; 228 229 if (curSlot[0] & 0x30) { // 0x10 - Wired, 0x20 - Wireless 230 if (ctx->joysticks[i] == 0) { 231 ResetAxisRange(ctx, i); 232 HIDAPI_JoystickConnected(device, &ctx->joysticks[i]); 233 } 234 } else { 235 if (ctx->joysticks[i] != 0) { 236 HIDAPI_JoystickDisconnected(device, ctx->joysticks[i]); 237 ctx->joysticks[i] = 0; 238 } 239 continue; 240 } 241 } 242 } 243 } 244 245 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE_RUMBLE_BRAKE, 246 SDL_JoystickGameCubeRumbleBrakeHintChanged, ctx); 247 248 HIDAPI_SetDeviceName(device, "Nintendo GameCube Controller"); 249 250 return true; 251} 252 253static int HIDAPI_DriverGameCube_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id) 254{ 255 SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)device->context; 256 Uint8 i; 257 258 for (i = 0; i < 4; ++i) { 259 if (instance_id == ctx->joysticks[i]) { 260 return i; 261 } 262 } 263 return -1; 264} 265 266static void HIDAPI_DriverGameCube_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index) 267{ 268} 269 270static void HIDAPI_DriverGameCube_HandleJoystickPacket(SDL_HIDAPI_Device *device, SDL_DriverGameCube_Context *ctx, const Uint8 *packet, bool invert_c_stick) 271{ 272 SDL_Joystick *joystick; 273 const Uint8 i = 0; // We have a separate context for each connected controller in PC mode, just use the first index 274 Uint8 v; 275 Sint16 axis_value; 276 Uint64 timestamp = SDL_GetTicksNS(); 277 278 joystick = SDL_GetJoystickFromID(ctx->joysticks[i]); 279 if (!joystick) { 280 // Hasn't been opened yet, skip 281 return; 282 } 283 284#define READ_BUTTON(off, flag, button) \ 285 SDL_SendJoystickButton( \ 286 timestamp, \ 287 joystick, \ 288 button, \ 289 ((packet[off] & flag) != 0)); 290 READ_BUTTON(0, 0x02, 0) // A 291 READ_BUTTON(0, 0x04, 1) // B 292 READ_BUTTON(0, 0x08, 3) // Y 293 READ_BUTTON(0, 0x01, 2) // X 294 READ_BUTTON(1, 0x80, 4) // DPAD_LEFT 295 READ_BUTTON(1, 0x20, 5) // DPAD_RIGHT 296 READ_BUTTON(1, 0x40, 6) // DPAD_DOWN 297 READ_BUTTON(1, 0x10, 7) // DPAD_UP 298 READ_BUTTON(1, 0x02, 8) // START 299 READ_BUTTON(0, 0x80, 9) // RIGHTSHOULDER 300 /* These two buttons are for the bottoms of the analog triggers. 301 * More than likely, you're going to want to read the axes instead! 302 * -flibit 303 */ 304 READ_BUTTON(0, 0x20, 10) // TRIGGERRIGHT 305 READ_BUTTON(0, 0x10, 11) // TRIGGERLEFT 306#undef READ_BUTTON 307 308#define READ_AXIS(off, axis, invert) \ 309 v = (invert) ? (0xff - packet[off]) : packet[off]; \ 310 if (v < ctx->min_axis[i * SDL_GAMEPAD_AXIS_COUNT + axis]) \ 311 ctx->min_axis[i * SDL_GAMEPAD_AXIS_COUNT + axis] = v; \ 312 if (v > ctx->max_axis[i * SDL_GAMEPAD_AXIS_COUNT + axis]) \ 313 ctx->max_axis[i * SDL_GAMEPAD_AXIS_COUNT + axis] = v; \ 314 axis_value = (Sint16)HIDAPI_RemapVal(v, ctx->min_axis[i * SDL_GAMEPAD_AXIS_COUNT + axis], ctx->max_axis[i * SDL_GAMEPAD_AXIS_COUNT + axis], SDL_MIN_SINT16, SDL_MAX_SINT16); \ 315 SDL_SendJoystickAxis( \ 316 timestamp, \ 317 joystick, \ 318 axis, axis_value); 319 READ_AXIS(2, SDL_GAMEPAD_AXIS_LEFTX, 0) 320 READ_AXIS(3, SDL_GAMEPAD_AXIS_LEFTY, 1) 321 READ_AXIS(5, SDL_GAMEPAD_AXIS_RIGHTX, invert_c_stick ? 1 : 0) 322 READ_AXIS(4, SDL_GAMEPAD_AXIS_RIGHTY, invert_c_stick ? 0 : 1) 323 READ_AXIS(6, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, 0) 324 READ_AXIS(7, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, 0) 325#undef READ_AXIS 326} 327 328static void HIDAPI_DriverGameCube_HandleNintendoPacket(SDL_HIDAPI_Device *device, SDL_DriverGameCube_Context *ctx, Uint8 *packet, int size) 329{ 330 SDL_Joystick *joystick; 331 Uint8 *curSlot; 332 Uint8 i; 333 Sint16 axis_value; 334 Uint64 timestamp = SDL_GetTicksNS(); 335 336 if (size < 37 || packet[0] != 0x21) { 337 return; // Nothing to do right now...? 338 } 339 340 // Go through all 4 slots 341 curSlot = packet + 1; 342 for (i = 0; i < MAX_CONTROLLERS; i += 1, curSlot += 9) { 343 ctx->wireless[i] = (curSlot[0] & 0x20) != 0; 344 345 // Only allow rumble if the adapter's second USB cable is connected 346 ctx->rumbleAllowed[i] = (curSlot[0] & 0x04) && !ctx->wireless[i]; 347 348 if (curSlot[0] & 0x30) { // 0x10 - Wired, 0x20 - Wireless 349 if (ctx->joysticks[i] == 0) { 350 ResetAxisRange(ctx, i); 351 HIDAPI_JoystickConnected(device, &ctx->joysticks[i]); 352 } 353 joystick = SDL_GetJoystickFromID(ctx->joysticks[i]); 354 355 // Hasn't been opened yet, skip 356 if (!joystick) { 357 continue; 358 } 359 } else { 360 if (ctx->joysticks[i] != 0) { 361 HIDAPI_JoystickDisconnected(device, ctx->joysticks[i]); 362 ctx->joysticks[i] = 0; 363 } 364 continue; 365 } 366 367#define READ_BUTTON(off, flag, button) \ 368 SDL_SendJoystickButton( \ 369 timestamp, \ 370 joystick, \ 371 button, \ 372 ((curSlot[off] & flag) != 0)); 373 READ_BUTTON(1, 0x01, 0) // A 374 READ_BUTTON(1, 0x02, 1) // B 375 READ_BUTTON(1, 0x04, 2) // X 376 READ_BUTTON(1, 0x08, 3) // Y 377 READ_BUTTON(1, 0x10, 4) // DPAD_LEFT 378 READ_BUTTON(1, 0x20, 5) // DPAD_RIGHT 379 READ_BUTTON(1, 0x40, 6) // DPAD_DOWN 380 READ_BUTTON(1, 0x80, 7) // DPAD_UP 381 READ_BUTTON(2, 0x01, 8) // START 382 READ_BUTTON(2, 0x02, 9) // RIGHTSHOULDER 383 /* These two buttons are for the bottoms of the analog triggers. 384 * More than likely, you're going to want to read the axes instead! 385 * -flibit 386 */ 387 READ_BUTTON(2, 0x04, 10) // TRIGGERRIGHT 388 READ_BUTTON(2, 0x08, 11) // TRIGGERLEFT 389#undef READ_BUTTON 390 391#define READ_AXIS(off, axis) \ 392 if (curSlot[off] < ctx->min_axis[i * SDL_GAMEPAD_AXIS_COUNT + axis]) \ 393 ctx->min_axis[i * SDL_GAMEPAD_AXIS_COUNT + axis] = curSlot[off]; \ 394 if (curSlot[off] > ctx->max_axis[i * SDL_GAMEPAD_AXIS_COUNT + axis]) \ 395 ctx->max_axis[i * SDL_GAMEPAD_AXIS_COUNT + axis] = curSlot[off]; \ 396 axis_value = (Sint16)HIDAPI_RemapVal(curSlot[off], ctx->min_axis[i * SDL_GAMEPAD_AXIS_COUNT + axis], ctx->max_axis[i * SDL_GAMEPAD_AXIS_COUNT + axis], SDL_MIN_SINT16, SDL_MAX_SINT16); \ 397 SDL_SendJoystickAxis( \ 398 timestamp, \ 399 joystick, \ 400 axis, axis_value); 401 READ_AXIS(3, SDL_GAMEPAD_AXIS_LEFTX) 402 READ_AXIS(4, SDL_GAMEPAD_AXIS_LEFTY) 403 READ_AXIS(5, SDL_GAMEPAD_AXIS_RIGHTX) 404 READ_AXIS(6, SDL_GAMEPAD_AXIS_RIGHTY) 405 READ_AXIS(7, SDL_GAMEPAD_AXIS_LEFT_TRIGGER) 406 READ_AXIS(8, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) 407#undef READ_AXIS 408 } 409} 410 411static bool HIDAPI_DriverGameCube_UpdateDevice(SDL_HIDAPI_Device *device) 412{ 413 SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)device->context; 414 Uint8 packet[USB_PACKET_LENGTH]; 415 int size; 416 417 // Read input packet 418 while ((size = SDL_hid_read_timeout(device->dev, packet, sizeof(packet), 0)) > 0) { 419#ifdef DEBUG_GAMECUBE_PROTOCOL 420 HIDAPI_DumpPacket("Nintendo GameCube packet: size = %d", packet, size); 421#endif 422 if (ctx->pc_mode) { 423 if (size == 10) { 424 // This is the older firmware 425 // The first byte is the index of the connected controller 426 // The C stick has an inverted value range compared to the left stick 427 HIDAPI_DriverGameCube_HandleJoystickPacket(device, ctx, &packet[1], true); 428 } else if (size == 9) { 429 // This is the newer firmware (version 0x7) 430 // The C stick has the same value range compared to the left stick 431 HIDAPI_DriverGameCube_HandleJoystickPacket(device, ctx, packet, false); 432 } else { 433 // How do we handle this packet? 434 } 435 } else { 436 HIDAPI_DriverGameCube_HandleNintendoPacket(device, ctx, packet, size); 437 } 438 } 439 440 // Write rumble packet 441 if (ctx->rumbleUpdate) { 442 SDL_HIDAPI_SendRumble(device, ctx->rumble, sizeof(ctx->rumble)); 443 ctx->rumbleUpdate = false; 444 } 445 446 // If we got here, nothing bad happened! 447 return true; 448} 449 450static bool HIDAPI_DriverGameCube_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 451{ 452 SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)device->context; 453 Uint8 i; 454 455 SDL_AssertJoysticksLocked(); 456 457 for (i = 0; i < MAX_CONTROLLERS; i += 1) { 458 if (joystick->instance_id == ctx->joysticks[i]) { 459 joystick->nbuttons = 12; 460 joystick->naxes = SDL_GAMEPAD_AXIS_COUNT; 461 if (ctx->wireless[i]) { 462 joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRELESS; 463 } else { 464 joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRED; 465 } 466 return true; 467 } 468 } 469 return false; // Should never get here! 470} 471 472static bool HIDAPI_DriverGameCube_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) 473{ 474 SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)device->context; 475 Uint8 i, val; 476 477 SDL_AssertJoysticksLocked(); 478 479 if (ctx->pc_mode) { 480 return SDL_Unsupported(); 481 } 482 483 for (i = 0; i < MAX_CONTROLLERS; i += 1) { 484 if (joystick->instance_id == ctx->joysticks[i]) { 485 if (ctx->wireless[i]) { 486 return SDL_SetError("Nintendo GameCube WaveBird controllers do not support rumble"); 487 } 488 if (!ctx->rumbleAllowed[i]) { 489 return SDL_SetError("Second USB cable for WUP-028 not connected"); 490 } 491 if (ctx->useRumbleBrake) { 492 if (low_frequency_rumble == 0 && high_frequency_rumble > 0) { 493 val = 0; // if only low is 0 we want to do a regular stop 494 } else if (low_frequency_rumble == 0 && high_frequency_rumble == 0) { 495 val = 2; // if both frequencies are 0 we want to do a hard stop 496 } else { 497 val = 1; // normal rumble 498 } 499 } else { 500 val = (low_frequency_rumble > 0 || high_frequency_rumble > 0); 501 } 502 if (val != ctx->rumble[i + 1]) { 503 ctx->rumble[i + 1] = val; 504 ctx->rumbleUpdate = true; 505 } 506 return true; 507 } 508 } 509 510 // Should never get here! 511 return SDL_SetError("Couldn't find joystick"); 512} 513 514static bool HIDAPI_DriverGameCube_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble) 515{ 516 return SDL_Unsupported(); 517} 518 519static Uint32 HIDAPI_DriverGameCube_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 520{ 521 SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)device->context; 522 Uint32 result = 0; 523 524 SDL_AssertJoysticksLocked(); 525 526 if (!ctx->pc_mode) { 527 Uint8 i; 528 529 for (i = 0; i < MAX_CONTROLLERS; i += 1) { 530 if (joystick->instance_id == ctx->joysticks[i]) { 531 if (!ctx->wireless[i] && ctx->rumbleAllowed[i]) { 532 result |= SDL_JOYSTICK_CAP_RUMBLE; 533 break; 534 } 535 } 536 } 537 } 538 539 return result; 540} 541 542static bool HIDAPI_DriverGameCube_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue) 543{ 544 return SDL_Unsupported(); 545} 546 547static bool HIDAPI_DriverGameCube_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *data, int size) 548{ 549 return SDL_Unsupported(); 550} 551 552static bool HIDAPI_DriverGameCube_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, bool enabled) 553{ 554 return SDL_Unsupported(); 555} 556 557static void HIDAPI_DriverGameCube_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 558{ 559 SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)device->context; 560 561 // Stop rumble activity 562 if (ctx->rumbleUpdate) { 563 SDL_HIDAPI_SendRumble(device, ctx->rumble, sizeof(ctx->rumble)); 564 ctx->rumbleUpdate = false; 565 } 566} 567 568static void HIDAPI_DriverGameCube_FreeDevice(SDL_HIDAPI_Device *device) 569{ 570 SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)device->context; 571 572 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE_RUMBLE_BRAKE, 573 SDL_JoystickGameCubeRumbleBrakeHintChanged, ctx); 574} 575 576SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverGameCube = { 577 SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE, 578 true, 579 HIDAPI_DriverGameCube_RegisterHints, 580 HIDAPI_DriverGameCube_UnregisterHints, 581 HIDAPI_DriverGameCube_IsEnabled, 582 HIDAPI_DriverGameCube_IsSupportedDevice, 583 HIDAPI_DriverGameCube_InitDevice, 584 HIDAPI_DriverGameCube_GetDevicePlayerIndex, 585 HIDAPI_DriverGameCube_SetDevicePlayerIndex, 586 HIDAPI_DriverGameCube_UpdateDevice, 587 HIDAPI_DriverGameCube_OpenJoystick, 588 HIDAPI_DriverGameCube_RumbleJoystick, 589 HIDAPI_DriverGameCube_RumbleJoystickTriggers, 590 HIDAPI_DriverGameCube_GetJoystickCapabilities, 591 HIDAPI_DriverGameCube_SetJoystickLED, 592 HIDAPI_DriverGameCube_SendJoystickEffect, 593 HIDAPI_DriverGameCube_SetJoystickSensorsEnabled, 594 HIDAPI_DriverGameCube_CloseJoystick, 595 HIDAPI_DriverGameCube_FreeDevice, 596}; 597 598#endif // SDL_JOYSTICK_HIDAPI_GAMECUBE 599 600#endif // SDL_JOYSTICK_HIDAPI 601[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.