1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2021 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.h"
26 #include "SDL_events.h"
27 #include "SDL_joystick.h"
28 #include "SDL_gamecontroller.h"
29 #include "../SDL_sysjoystick.h"
30 #include "SDL_hidapijoystick_c.h"
31 #include "SDL_hidapi_rumble.h"
32 
33 
34 #ifdef SDL_JOYSTICK_HIDAPI_LUNA
35 
36 /* Define this if you want to log all packets from the controller */
37 /*#define DEBUG_LUNA_PROTOCOL*/
38 
39 enum
40 {
41     SDL_CONTROLLER_BUTTON_LUNA_MIC = 15,
42     SDL_CONTROLLER_NUM_LUNA_BUTTONS,
43 };
44 
45 typedef struct {
46     Uint8 last_state[USB_PACKET_LENGTH];
47 } SDL_DriverLuna_Context;
48 
49 
50 static SDL_bool
HIDAPI_DriverLuna_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)51 HIDAPI_DriverLuna_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)
52 {
53     return (type == SDL_CONTROLLER_TYPE_AMAZON_LUNA) ? SDL_TRUE : SDL_FALSE;
54 }
55 
56 static const char *
HIDAPI_DriverLuna_GetDeviceName(Uint16 vendor_id,Uint16 product_id)57 HIDAPI_DriverLuna_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
58 {
59     return "Amazon Luna Controller";
60 }
61 
62 static SDL_bool
HIDAPI_DriverLuna_InitDevice(SDL_HIDAPI_Device * device)63 HIDAPI_DriverLuna_InitDevice(SDL_HIDAPI_Device *device)
64 {
65     return HIDAPI_JoystickConnected(device, NULL);
66 }
67 
68 static int
HIDAPI_DriverLuna_GetDevicePlayerIndex(SDL_HIDAPI_Device * device,SDL_JoystickID instance_id)69 HIDAPI_DriverLuna_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
70 {
71     return -1;
72 }
73 
74 static void
HIDAPI_DriverLuna_SetDevicePlayerIndex(SDL_HIDAPI_Device * device,SDL_JoystickID instance_id,int player_index)75 HIDAPI_DriverLuna_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
76 {
77 }
78 
79 static SDL_bool
HIDAPI_DriverLuna_OpenJoystick(SDL_HIDAPI_Device * device,SDL_Joystick * joystick)80 HIDAPI_DriverLuna_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
81 {
82     SDL_DriverLuna_Context *ctx;
83 
84     ctx = (SDL_DriverLuna_Context *)SDL_calloc(1, sizeof(*ctx));
85     if (!ctx) {
86         SDL_OutOfMemory();
87         return SDL_FALSE;
88     }
89 
90     device->dev = SDL_hid_open_path(device->path, 0);
91     if (!device->dev) {
92         SDL_SetError("Couldn't open %s", device->path);
93         SDL_free(ctx);
94         return SDL_FALSE;
95     }
96     device->context = ctx;
97 
98     /* Initialize the joystick capabilities */
99     joystick->nbuttons = SDL_CONTROLLER_NUM_LUNA_BUTTONS;
100     joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
101     joystick->epowerlevel = SDL_JOYSTICK_POWER_FULL;
102     joystick->serial = NULL;
103 
104     return SDL_TRUE;
105 }
106 
107 static int
HIDAPI_DriverLuna_RumbleJoystick(SDL_HIDAPI_Device * device,SDL_Joystick * joystick,Uint16 low_frequency_rumble,Uint16 high_frequency_rumble)108 HIDAPI_DriverLuna_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
109 {
110     if (device->product_id == BLUETOOTH_PRODUCT_LUNA_CONTROLLER) {
111         /* Same packet as on Xbox One controllers connected via Bluetooth */
112         Uint8 rumble_packet[] = { 0x03, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xEB };
113 
114         /* Magnitude is 1..100 so scale the 16-bit input here */
115         rumble_packet[4] = low_frequency_rumble / 655;
116         rumble_packet[5] = high_frequency_rumble / 655;
117 
118         if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
119             return SDL_SetError("Couldn't send rumble packet");
120         }
121 
122         return 0;
123     } else {
124         /* FIXME: Is there a rumble packet over USB? */
125         return SDL_Unsupported();
126     }
127 }
128 
129 static int
HIDAPI_DriverLuna_RumbleJoystickTriggers(SDL_HIDAPI_Device * device,SDL_Joystick * joystick,Uint16 left_rumble,Uint16 right_rumble)130 HIDAPI_DriverLuna_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
131 {
132     return SDL_Unsupported();
133 }
134 
135 static Uint32
HIDAPI_DriverLuna_GetJoystickCapabilities(SDL_HIDAPI_Device * device,SDL_Joystick * joystick)136 HIDAPI_DriverLuna_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
137 {
138     Uint32 result = 0;
139 
140     if (device->product_id == BLUETOOTH_PRODUCT_LUNA_CONTROLLER) {
141         result |= SDL_JOYCAP_RUMBLE;
142     }
143 
144     return result;
145 }
146 
147 static int
HIDAPI_DriverLuna_SetJoystickLED(SDL_HIDAPI_Device * device,SDL_Joystick * joystick,Uint8 red,Uint8 green,Uint8 blue)148 HIDAPI_DriverLuna_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
149 {
150     return SDL_Unsupported();
151 }
152 
153 static int
HIDAPI_DriverLuna_SendJoystickEffect(SDL_HIDAPI_Device * device,SDL_Joystick * joystick,const void * data,int size)154 HIDAPI_DriverLuna_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *data, int size)
155 {
156     return SDL_Unsupported();
157 }
158 
159 static int
HIDAPI_DriverLuna_SetJoystickSensorsEnabled(SDL_HIDAPI_Device * device,SDL_Joystick * joystick,SDL_bool enabled)160 HIDAPI_DriverLuna_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled)
161 {
162     return SDL_Unsupported();
163 }
164 
165 static void
HIDAPI_DriverLuna_HandleUSBStatePacket(SDL_Joystick * joystick,SDL_DriverLuna_Context * ctx,Uint8 * data,int size)166 HIDAPI_DriverLuna_HandleUSBStatePacket(SDL_Joystick *joystick, SDL_DriverLuna_Context *ctx, Uint8 *data, int size)
167 {
168     if (ctx->last_state[1] != data[1]) {
169         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[1] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
170         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[1] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
171         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[1] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
172         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[1] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
173         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[1] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
174         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[1] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
175         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[1] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
176         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[1] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
177     }
178     if (ctx->last_state[2] != data[2]) {
179         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data[2] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
180         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LUNA_MIC, (data[2] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
181         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[2] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
182         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[2] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
183     }
184 
185     if (ctx->last_state[3] != data[3]) {
186         SDL_bool dpad_up = SDL_FALSE;
187         SDL_bool dpad_down = SDL_FALSE;
188         SDL_bool dpad_left = SDL_FALSE;
189         SDL_bool dpad_right = SDL_FALSE;
190 
191         switch (data[3] & 0xf) {
192         case 0:
193             dpad_up = SDL_TRUE;
194             break;
195         case 1:
196             dpad_up = SDL_TRUE;
197             dpad_right = SDL_TRUE;
198             break;
199         case 2:
200             dpad_right = SDL_TRUE;
201             break;
202         case 3:
203             dpad_right = SDL_TRUE;
204             dpad_down = SDL_TRUE;
205             break;
206         case 4:
207             dpad_down = SDL_TRUE;
208             break;
209         case 5:
210             dpad_left = SDL_TRUE;
211             dpad_down = SDL_TRUE;
212             break;
213         case 6:
214             dpad_left = SDL_TRUE;
215             break;
216         case 7:
217             dpad_up = SDL_TRUE;
218             dpad_left = SDL_TRUE;
219             break;
220         default:
221             break;
222         }
223         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, dpad_down);
224         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, dpad_up);
225         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, dpad_right);
226         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, dpad_left);
227     }
228 
229 #define READ_STICK_AXIS(offset) \
230     (data[offset] == 0x7f ? 0 : \
231     (Sint16)HIDAPI_RemapVal((float)data[offset], 0x00, 0xff, SDL_MIN_SINT16, SDL_MAX_SINT16))
232     {
233         Sint16 axis = READ_STICK_AXIS(4);
234         SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
235         axis = READ_STICK_AXIS(5);
236         SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
237         axis = READ_STICK_AXIS(6);
238         SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
239         axis = READ_STICK_AXIS(7);
240         SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
241     }
242 #undef READ_STICK_AXIS
243 
244 #define READ_TRIGGER_AXIS(offset) \
245     (Sint16)HIDAPI_RemapVal((float)data[offset], 0x00, 0xff, SDL_MIN_SINT16, SDL_MAX_SINT16)
246     {
247         Sint16 axis = READ_TRIGGER_AXIS(8);
248         SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
249         axis = READ_TRIGGER_AXIS(9);
250         SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
251     }
252 #undef READ_TRIGGER_AXIS
253 
254     SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
255 }
256 
257 static void
HIDAPI_DriverLuna_HandleBluetoothStatePacket(SDL_Joystick * joystick,SDL_DriverLuna_Context * ctx,Uint8 * data,int size)258 HIDAPI_DriverLuna_HandleBluetoothStatePacket(SDL_Joystick *joystick, SDL_DriverLuna_Context *ctx, Uint8 *data, int size)
259 {
260     if (size >= 2 && data[0] == 0x02) {
261         /* Home button has dedicated report */
262         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data[1] & 0x1) ? SDL_PRESSED : SDL_RELEASED);
263         return;
264     }
265 
266     if (size >= 2 && data[0] == 0x04) {
267         /* Battery level report */
268         int level = data[1] * 100 / 0xFF;
269         if (level == 0) {
270             joystick->epowerlevel = SDL_JOYSTICK_POWER_EMPTY;
271         }
272         else if (level <= 20) {
273             joystick->epowerlevel = SDL_JOYSTICK_POWER_LOW;
274         }
275         else if (level <= 70) {
276             joystick->epowerlevel = SDL_JOYSTICK_POWER_MEDIUM;
277         }
278         else {
279             joystick->epowerlevel = SDL_JOYSTICK_POWER_FULL;
280         }
281 
282         return;
283     }
284 
285     if (size < 17 || data[0] != 0x01) {
286         /* We don't know how to handle this report */
287         return;
288     }
289 
290     if (ctx->last_state[13] != data[13]) {
291         SDL_bool dpad_up = SDL_FALSE;
292         SDL_bool dpad_down = SDL_FALSE;
293         SDL_bool dpad_left = SDL_FALSE;
294         SDL_bool dpad_right = SDL_FALSE;
295 
296         switch (data[13] & 0xf) {
297         case 1:
298             dpad_up = SDL_TRUE;
299             break;
300         case 2:
301             dpad_up = SDL_TRUE;
302             dpad_right = SDL_TRUE;
303             break;
304         case 3:
305             dpad_right = SDL_TRUE;
306             break;
307         case 4:
308             dpad_right = SDL_TRUE;
309             dpad_down = SDL_TRUE;
310             break;
311         case 5:
312             dpad_down = SDL_TRUE;
313             break;
314         case 6:
315             dpad_left = SDL_TRUE;
316             dpad_down = SDL_TRUE;
317             break;
318         case 7:
319             dpad_left = SDL_TRUE;
320             break;
321         case 8:
322             dpad_up = SDL_TRUE;
323             dpad_left = SDL_TRUE;
324             break;
325         default:
326             break;
327         }
328         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, dpad_down);
329         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, dpad_up);
330         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, dpad_right);
331         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, dpad_left);
332     }
333 
334     if (ctx->last_state[14] != data[14]) {
335         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[14] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
336         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[14] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
337         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[14] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
338         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[14] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
339         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[14] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
340         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[14] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
341     }
342     if (ctx->last_state[15] != data[15]) {
343         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[15] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
344         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[15] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
345         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[15] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
346     }
347     if (ctx->last_state[16] != data[16]) {
348         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[16] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
349         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LUNA_MIC, (data[16] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
350     }
351 
352 #define READ_STICK_AXIS(offset) \
353     (data[offset] == 0x7f ? 0 : \
354     (Sint16)HIDAPI_RemapVal((float)data[offset], 0x00, 0xff, SDL_MIN_SINT16, SDL_MAX_SINT16))
355     {
356         Sint16 axis = READ_STICK_AXIS(2);
357         SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
358         axis = READ_STICK_AXIS(4);
359         SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
360         axis = READ_STICK_AXIS(6);
361         SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
362         axis = READ_STICK_AXIS(8);
363         SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
364     }
365 #undef READ_STICK_AXIS
366 
367 #define READ_TRIGGER_AXIS(offset) \
368     (Sint16)HIDAPI_RemapVal((float)((int)(((data[offset] | (data[offset + 1] << 8)) & 0x3ff) - 0x200)), 0x00 - 0x200, 0x3ff - 0x200, SDL_MIN_SINT16, SDL_MAX_SINT16)
369     {
370         Sint16 axis = READ_TRIGGER_AXIS(9);
371         SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
372         axis = READ_TRIGGER_AXIS(11);
373         SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
374     }
375 #undef READ_TRIGGER_AXIS
376 
377     SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
378 }
379 
380 static SDL_bool
HIDAPI_DriverLuna_UpdateDevice(SDL_HIDAPI_Device * device)381 HIDAPI_DriverLuna_UpdateDevice(SDL_HIDAPI_Device *device)
382 {
383     SDL_DriverLuna_Context *ctx = (SDL_DriverLuna_Context *)device->context;
384     SDL_Joystick *joystick = NULL;
385     Uint8 data[USB_PACKET_LENGTH];
386     int size = 0;
387 
388     if (device->num_joysticks > 0) {
389         joystick = SDL_JoystickFromInstanceID(device->joysticks[0]);
390     }
391     if (!joystick) {
392         return SDL_FALSE;
393     }
394 
395     while ((size = SDL_hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) {
396 #ifdef DEBUG_LUNA_PROTOCOL
397         HIDAPI_DumpPacket("Amazon Luna packet: size = %d", data, size);
398 #endif
399         switch (size) {
400         case 10:
401             HIDAPI_DriverLuna_HandleUSBStatePacket(joystick, ctx, data, size);
402             break;
403         default:
404             HIDAPI_DriverLuna_HandleBluetoothStatePacket(joystick, ctx, data, size);
405             break;
406         }
407     }
408 
409     if (size < 0) {
410         /* Read error, device is disconnected */
411         HIDAPI_JoystickDisconnected(device, joystick->instance_id);
412     }
413     return (size >= 0);
414 }
415 
416 static void
HIDAPI_DriverLuna_CloseJoystick(SDL_HIDAPI_Device * device,SDL_Joystick * joystick)417 HIDAPI_DriverLuna_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
418 {
419     SDL_LockMutex(device->dev_lock);
420     {
421         if (device->dev) {
422             SDL_hid_close(device->dev);
423             device->dev = NULL;
424         }
425 
426         SDL_free(device->context);
427         device->context = NULL;
428     }
429     SDL_UnlockMutex(device->dev_lock);
430 }
431 
432 static void
HIDAPI_DriverLuna_FreeDevice(SDL_HIDAPI_Device * device)433 HIDAPI_DriverLuna_FreeDevice(SDL_HIDAPI_Device *device)
434 {
435 }
436 
437 SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverLuna =
438 {
439     SDL_HINT_JOYSTICK_HIDAPI_LUNA,
440     SDL_TRUE,
441     SDL_TRUE,
442     HIDAPI_DriverLuna_IsSupportedDevice,
443     HIDAPI_DriverLuna_GetDeviceName,
444     HIDAPI_DriverLuna_InitDevice,
445     HIDAPI_DriverLuna_GetDevicePlayerIndex,
446     HIDAPI_DriverLuna_SetDevicePlayerIndex,
447     HIDAPI_DriverLuna_UpdateDevice,
448     HIDAPI_DriverLuna_OpenJoystick,
449     HIDAPI_DriverLuna_RumbleJoystick,
450     HIDAPI_DriverLuna_RumbleJoystickTriggers,
451     HIDAPI_DriverLuna_GetJoystickCapabilities,
452     HIDAPI_DriverLuna_SetJoystickLED,
453     HIDAPI_DriverLuna_SendJoystickEffect,
454     HIDAPI_DriverLuna_SetJoystickSensorsEnabled,
455     HIDAPI_DriverLuna_CloseJoystick,
456     HIDAPI_DriverLuna_FreeDevice,
457 };
458 
459 #endif /* SDL_JOYSTICK_HIDAPI_LUNA */
460 
461 #endif /* SDL_JOYSTICK_HIDAPI */
462 
463 /* vi: set ts=4 sw=4 expandtab: */
464