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