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_sysjoystick.h"
26#include "SDL_hidapijoystick_c.h"
27#include "SDL_hidapi_rumble.h"
28
29#ifdef SDL_JOYSTICK_HIDAPI_SHIELD
30
31// Define this if you want to log all packets from the controller
32// #define DEBUG_SHIELD_PROTOCOL
33
34#define CMD_BATTERY_STATE 0x07
35#define CMD_RUMBLE 0x39
36#define CMD_CHARGE_STATE 0x3A
37
38// Milliseconds between polls of battery state
39#define BATTERY_POLL_INTERVAL_MS 60000
40
41// Milliseconds between retransmission of rumble to keep motors running
42#define RUMBLE_REFRESH_INTERVAL_MS 500
43
44// Reports that are too small are dropped over Bluetooth
45#define HID_REPORT_SIZE 33
46
47enum
48{
49 SDL_GAMEPAD_BUTTON_SHIELD_SHARE = 11,
50 SDL_GAMEPAD_BUTTON_SHIELD_V103_TOUCHPAD,
51 SDL_GAMEPAD_BUTTON_SHIELD_V103_MINUS,
52 SDL_GAMEPAD_BUTTON_SHIELD_V103_PLUS,
53 SDL_GAMEPAD_NUM_SHIELD_V103_BUTTONS,
54
55 SDL_GAMEPAD_NUM_SHIELD_V104_BUTTONS = SDL_GAMEPAD_BUTTON_SHIELD_SHARE + 1,
56};
57
58typedef enum
59{
60 k_ShieldReportIdControllerState = 0x01,
61 k_ShieldReportIdControllerTouch = 0x02,
62 k_ShieldReportIdCommandResponse = 0x03,
63 k_ShieldReportIdCommandRequest = 0x04,
64} EShieldReportId;
65
66// This same report structure is used for both requests and responses
67typedef struct
68{
69 Uint8 report_id;
70 Uint8 cmd;
71 Uint8 seq_num;
72 Uint8 payload[HID_REPORT_SIZE - 3];
73} ShieldCommandReport_t;
74SDL_COMPILE_TIME_ASSERT(ShieldCommandReport_t, sizeof(ShieldCommandReport_t) == HID_REPORT_SIZE);
75
76typedef struct
77{
78 Uint8 seq_num;
79
80 bool has_charging;
81 Uint8 charging;
82 bool has_battery_level;
83 Uint8 battery_level;
84 Uint64 last_battery_query_time;
85
86 bool rumble_report_pending;
87 bool rumble_update_pending;
88 Uint8 left_motor_amplitude;
89 Uint8 right_motor_amplitude;
90 Uint64 last_rumble_time;
91
92 Uint8 last_state[USB_PACKET_LENGTH];
93} SDL_DriverShield_Context;
94
95static void HIDAPI_DriverShield_RegisterHints(SDL_HintCallback callback, void *userdata)
96{
97 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SHIELD, callback, userdata);
98}
99
100static void HIDAPI_DriverShield_UnregisterHints(SDL_HintCallback callback, void *userdata)
101{
102 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SHIELD, callback, userdata);
103}
104
105static bool HIDAPI_DriverShield_IsEnabled(void)
106{
107 return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_SHIELD, SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI, SDL_HIDAPI_DEFAULT));
108}
109
110static bool HIDAPI_DriverShield_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)
111{
112 return SDL_IsJoystickNVIDIASHIELDController(vendor_id, product_id);
113}
114
115static bool HIDAPI_DriverShield_InitDevice(SDL_HIDAPI_Device *device)
116{
117 SDL_DriverShield_Context *ctx;
118
119 ctx = (SDL_DriverShield_Context *)SDL_calloc(1, sizeof(*ctx));
120 if (!ctx) {
121 return false;
122 }
123 device->context = ctx;
124
125 HIDAPI_SetDeviceName(device, "NVIDIA SHIELD Controller");
126
127 return HIDAPI_JoystickConnected(device, NULL);
128}
129
130static int HIDAPI_DriverShield_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
131{
132 return -1;
133}
134
135static void HIDAPI_DriverShield_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
136{
137}
138
139static bool HIDAPI_DriverShield_SendCommand(SDL_HIDAPI_Device *device, Uint8 cmd, const void *data, int size)
140{
141 SDL_DriverShield_Context *ctx = (SDL_DriverShield_Context *)device->context;
142 ShieldCommandReport_t cmd_pkt;
143
144 if (size > sizeof(cmd_pkt.payload)) {
145 return SDL_SetError("Command data exceeds HID report size");
146 }
147
148 if (!SDL_HIDAPI_LockRumble()) {
149 return false;
150 }
151
152 cmd_pkt.report_id = k_ShieldReportIdCommandRequest;
153 cmd_pkt.cmd = cmd;
154 cmd_pkt.seq_num = ctx->seq_num++;
155 if (data) {
156 SDL_memcpy(cmd_pkt.payload, data, size);
157 }
158
159 // Zero unused data in the payload
160 if (size != sizeof(cmd_pkt.payload)) {
161 SDL_memset(&cmd_pkt.payload[size], 0, sizeof(cmd_pkt.payload) - size);
162 }
163
164 if (SDL_HIDAPI_SendRumbleAndUnlock(device, (Uint8 *)&cmd_pkt, sizeof(cmd_pkt)) != sizeof(cmd_pkt)) {
165 return SDL_SetError("Couldn't send command packet");
166 }
167
168 return true;
169}
170
171static bool HIDAPI_DriverShield_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
172{
173 SDL_DriverShield_Context *ctx = (SDL_DriverShield_Context *)device->context;
174
175 SDL_AssertJoysticksLocked();
176
177 ctx->rumble_report_pending = false;
178 ctx->rumble_update_pending = false;
179 ctx->left_motor_amplitude = 0;
180 ctx->right_motor_amplitude = 0;
181 ctx->last_rumble_time = 0;
182 SDL_zeroa(ctx->last_state);
183
184 // Initialize the joystick capabilities
185 if (device->product_id == USB_PRODUCT_NVIDIA_SHIELD_CONTROLLER_V103) {
186 joystick->nbuttons = SDL_GAMEPAD_NUM_SHIELD_V103_BUTTONS;
187 joystick->naxes = SDL_GAMEPAD_AXIS_COUNT;
188 joystick->nhats = 1;
189
190 SDL_PrivateJoystickAddTouchpad(joystick, 1);
191 } else {
192 joystick->nbuttons = SDL_GAMEPAD_NUM_SHIELD_V104_BUTTONS;
193 joystick->naxes = SDL_GAMEPAD_AXIS_COUNT;
194 joystick->nhats = 1;
195 }
196
197 // Request battery and charging info
198 ctx->last_battery_query_time = SDL_GetTicks();
199 HIDAPI_DriverShield_SendCommand(device, CMD_CHARGE_STATE, NULL, 0);
200 HIDAPI_DriverShield_SendCommand(device, CMD_BATTERY_STATE, NULL, 0);
201
202 return true;
203}
204
205static bool HIDAPI_DriverShield_SendNextRumble(SDL_HIDAPI_Device *device)
206{
207 SDL_DriverShield_Context *ctx = (SDL_DriverShield_Context *)device->context;
208 Uint8 rumble_data[3];
209
210 if (!ctx->rumble_update_pending) {
211 return true;
212 }
213
214 rumble_data[0] = 0x01; // enable
215 rumble_data[1] = ctx->left_motor_amplitude;
216 rumble_data[2] = ctx->right_motor_amplitude;
217
218 ctx->rumble_update_pending = false;
219 ctx->last_rumble_time = SDL_GetTicks();
220
221 return HIDAPI_DriverShield_SendCommand(device, CMD_RUMBLE, rumble_data, sizeof(rumble_data));
222}
223
224static bool HIDAPI_DriverShield_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
225{
226 if (device->product_id == USB_PRODUCT_NVIDIA_SHIELD_CONTROLLER_V103) {
227 Uint8 rumble_packet[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
228
229 rumble_packet[2] = (low_frequency_rumble >> 8);
230 rumble_packet[4] = (high_frequency_rumble >> 8);
231
232 if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
233 return SDL_SetError("Couldn't send rumble packet");
234 }
235 return true;
236
237 } else {
238 SDL_DriverShield_Context *ctx = (SDL_DriverShield_Context *)device->context;
239
240 // The rumble motors are quite intense, so tone down the intensity like the official driver does
241 ctx->left_motor_amplitude = low_frequency_rumble >> 11;
242 ctx->right_motor_amplitude = high_frequency_rumble >> 11;
243 ctx->rumble_update_pending = true;
244
245 if (ctx->rumble_report_pending) {
246 // We will service this after the hardware acknowledges the previous request
247 return true;
248 }
249
250 return HIDAPI_DriverShield_SendNextRumble(device);
251 }
252}
253
254static bool HIDAPI_DriverShield_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
255{
256 return SDL_Unsupported();
257}
258
259static Uint32 HIDAPI_DriverShield_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
260{
261 return SDL_JOYSTICK_CAP_RUMBLE;
262}
263
264static bool HIDAPI_DriverShield_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
265{
266 return SDL_Unsupported();
267}
268
269static bool HIDAPI_DriverShield_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *data, int size)
270{
271 const Uint8 *data_bytes = (const Uint8 *)data;
272
273 if (size > 1) {
274 // Single command byte followed by a variable length payload
275 return HIDAPI_DriverShield_SendCommand(device, data_bytes[0], &data_bytes[1], size - 1);
276 } else if (size == 1) {
277 // Single command byte with no payload
278 return HIDAPI_DriverShield_SendCommand(device, data_bytes[0], NULL, 0);
279 } else {
280 return SDL_SetError("Effect data must at least contain a command byte");
281 }
282}
283
284static bool HIDAPI_DriverShield_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, bool enabled)
285{
286 return SDL_Unsupported();
287}
288
289static void HIDAPI_DriverShield_HandleStatePacketV103(SDL_Joystick *joystick, SDL_DriverShield_Context *ctx, Uint8 *data, int size)
290{
291 Uint64 timestamp = SDL_GetTicksNS();
292
293 if (ctx->last_state[3] != data[3]) {
294 Uint8 hat;
295
296 switch (data[3]) {
297 case 0:
298 hat = SDL_HAT_UP;
299 break;
300 case 1:
301 hat = SDL_HAT_RIGHTUP;
302 break;
303 case 2:
304 hat = SDL_HAT_RIGHT;
305 break;
306 case 3:
307 hat = SDL_HAT_RIGHTDOWN;
308 break;
309 case 4:
310 hat = SDL_HAT_DOWN;
311 break;
312 case 5:
313 hat = SDL_HAT_LEFTDOWN;
314 break;
315 case 6:
316 hat = SDL_HAT_LEFT;
317 break;
318 case 7:
319 hat = SDL_HAT_LEFTUP;
320 break;
321 default:
322 hat = SDL_HAT_CENTERED;
323 break;
324 }
325 SDL_SendJoystickHat(timestamp, joystick, 0, hat);
326 }
327
328 if (ctx->last_state[1] != data[1]) {
329 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SOUTH, ((data[1] & 0x01) != 0));
330 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_EAST, ((data[1] & 0x02) != 0));
331 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_WEST, ((data[1] & 0x04) != 0));
332 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_NORTH, ((data[1] & 0x08) != 0));
333 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data[1] & 0x10) != 0));
334 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data[1] & 0x20) != 0));
335 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data[1] & 0x40) != 0));
336 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data[1] & 0x80) != 0));
337 }
338
339 if (ctx->last_state[2] != data[2]) {
340 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data[2] & 0x02) != 0));
341 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SHIELD_V103_PLUS, ((data[2] & 0x08) != 0));
342 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SHIELD_V103_MINUS, ((data[2] & 0x10) != 0));
343 //SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data[2] & 0x20) != 0));
344 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data[2] & 0x40) != 0));
345 //SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SHIELD_SHARE, ((data[2] & 0x80) != 0));
346 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data[2] & 0x80) != 0));
347 }
348
349 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_Swap16LE(*(Sint16 *)&data[4]) - 0x8000);
350 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_Swap16LE(*(Sint16 *)&data[6]) - 0x8000);
351
352 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, SDL_Swap16LE(*(Sint16 *)&data[8]) - 0x8000);
353 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, SDL_Swap16LE(*(Sint16 *)&data[10]) - 0x8000);
354
355 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, SDL_Swap16LE(*(Sint16 *)&data[12]) - 0x8000);
356 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, SDL_Swap16LE(*(Sint16 *)&data[14]) - 0x8000);
357
358 SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
359}
360
361#undef clamp
362#define clamp(val, min, max) (((val) > (max)) ? (max) : (((val) < (min)) ? (min) : (val)))
363
364static void HIDAPI_DriverShield_HandleTouchPacketV103(SDL_Joystick *joystick, SDL_DriverShield_Context *ctx, const Uint8 *data, int size)
365{
366 bool touchpad_down;
367 float touchpad_x, touchpad_y;
368 Uint64 timestamp = SDL_GetTicksNS();
369
370 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SHIELD_V103_TOUCHPAD, ((data[1] & 0x01) != 0));
371
372 // It's a triangular pad, but just use the center as the usable touch area
373 touchpad_down = ((data[1] & 0x80) == 0);
374 touchpad_x = clamp((float)(data[2] - 0x70) / 0x50, 0.0f, 1.0f);
375 touchpad_y = clamp((float)(data[4] - 0x40) / 0x15, 0.0f, 1.0f);
376 SDL_SendJoystickTouchpad(timestamp, joystick, 0, 0, touchpad_down, touchpad_x, touchpad_y, touchpad_down ? 1.0f : 0.0f);
377}
378
379static void HIDAPI_DriverShield_HandleStatePacketV104(SDL_Joystick *joystick, SDL_DriverShield_Context *ctx, Uint8 *data, int size)
380{
381 Uint64 timestamp = SDL_GetTicksNS();
382
383 if (size < 23) {
384 return;
385 }
386
387 if (ctx->last_state[2] != data[2]) {
388 Uint8 hat;
389
390 switch (data[2]) {
391 case 0:
392 hat = SDL_HAT_UP;
393 break;
394 case 1:
395 hat = SDL_HAT_RIGHTUP;
396 break;
397 case 2:
398 hat = SDL_HAT_RIGHT;
399 break;
400 case 3:
401 hat = SDL_HAT_RIGHTDOWN;
402 break;
403 case 4:
404 hat = SDL_HAT_DOWN;
405 break;
406 case 5:
407 hat = SDL_HAT_LEFTDOWN;
408 break;
409 case 6:
410 hat = SDL_HAT_LEFT;
411 break;
412 case 7:
413 hat = SDL_HAT_LEFTUP;
414 break;
415 default:
416 hat = SDL_HAT_CENTERED;
417 break;
418 }
419 SDL_SendJoystickHat(timestamp, joystick, 0, hat);
420 }
421
422 if (ctx->last_state[3] != data[3]) {
423 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SOUTH, ((data[3] & 0x01) != 0));
424 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_EAST, ((data[3] & 0x02) != 0));
425 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_WEST, ((data[3] & 0x04) != 0));
426 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_NORTH, ((data[3] & 0x08) != 0));
427 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data[3] & 0x10) != 0));
428 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data[3] & 0x20) != 0));
429 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data[3] & 0x40) != 0));
430 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data[3] & 0x80) != 0));
431 }
432
433 if (ctx->last_state[4] != data[4]) {
434 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data[4] & 0x01) != 0));
435 }
436
437 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_Swap16LE(*(Sint16 *)&data[9]) - 0x8000);
438 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_Swap16LE(*(Sint16 *)&data[11]) - 0x8000);
439
440 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, SDL_Swap16LE(*(Sint16 *)&data[13]) - 0x8000);
441 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, SDL_Swap16LE(*(Sint16 *)&data[15]) - 0x8000);
442
443 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, SDL_Swap16LE(*(Sint16 *)&data[19]) - 0x8000);
444 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, SDL_Swap16LE(*(Sint16 *)&data[21]) - 0x8000);
445
446 if (ctx->last_state[17] != data[17]) {
447 //SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SHIELD_SHARE, ((data[17] & 0x01) != 0));
448 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data[17] & 0x02) != 0));
449 //SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data[17] & 0x04) != 0));
450 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data[17] & 0x01) != 0));
451 }
452
453 SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
454}
455
456static void HIDAPI_DriverShield_UpdatePowerInfo(SDL_Joystick *joystick, SDL_DriverShield_Context *ctx)
457{
458 if (!ctx->has_charging || !ctx->has_battery_level) {
459 return;
460 }
461
462 SDL_PowerState state = ctx->charging ? SDL_POWERSTATE_CHARGING : SDL_POWERSTATE_ON_BATTERY;
463 int percent = ctx->battery_level * 20;
464 SDL_SendJoystickPowerInfo(joystick, state, percent);
465}
466
467static bool HIDAPI_DriverShield_UpdateDevice(SDL_HIDAPI_Device *device)
468{
469 SDL_DriverShield_Context *ctx = (SDL_DriverShield_Context *)device->context;
470 SDL_Joystick *joystick = NULL;
471 Uint8 data[USB_PACKET_LENGTH];
472 int size = 0;
473 ShieldCommandReport_t *cmd_resp_report;
474
475 if (device->num_joysticks > 0) {
476 joystick = SDL_GetJoystickFromID(device->joysticks[0]);
477 } else {
478 return false;
479 }
480
481 while ((size = SDL_hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) {
482#ifdef DEBUG_SHIELD_PROTOCOL
483 HIDAPI_DumpPacket("NVIDIA SHIELD packet: size = %d", data, size);
484#endif
485
486 // Byte 0 is HID report ID
487 switch (data[0]) {
488 case k_ShieldReportIdControllerState:
489 if (!joystick) {
490 break;
491 }
492 if (size == 16) {
493 HIDAPI_DriverShield_HandleStatePacketV103(joystick, ctx, data, size);
494 } else {
495 HIDAPI_DriverShield_HandleStatePacketV104(joystick, ctx, data, size);
496 }
497 break;
498 case k_ShieldReportIdControllerTouch:
499 if (!joystick) {
500 break;
501 }
502 HIDAPI_DriverShield_HandleTouchPacketV103(joystick, ctx, data, size);
503 break;
504 case k_ShieldReportIdCommandResponse:
505 cmd_resp_report = (ShieldCommandReport_t *)data;
506 switch (cmd_resp_report->cmd) {
507 case CMD_RUMBLE:
508 ctx->rumble_report_pending = false;
509 HIDAPI_DriverShield_SendNextRumble(device);
510 break;
511 case CMD_CHARGE_STATE:
512 ctx->has_charging = true;
513 ctx->charging = cmd_resp_report->payload[0];
514 HIDAPI_DriverShield_UpdatePowerInfo(joystick, ctx);
515 break;
516 case CMD_BATTERY_STATE:
517 ctx->has_battery_level = true;
518 ctx->battery_level = cmd_resp_report->payload[2];
519 HIDAPI_DriverShield_UpdatePowerInfo(joystick, ctx);
520 break;
521 }
522 break;
523 }
524 }
525
526 // Ask for battery state again if we're due for an update
527 if (joystick && SDL_GetTicks() >= (ctx->last_battery_query_time + BATTERY_POLL_INTERVAL_MS)) {
528 ctx->last_battery_query_time = SDL_GetTicks();
529 HIDAPI_DriverShield_SendCommand(device, CMD_BATTERY_STATE, NULL, 0);
530 }
531
532 // Retransmit rumble packets if they've lasted longer than the hardware supports
533 if ((ctx->left_motor_amplitude != 0 || ctx->right_motor_amplitude != 0) &&
534 SDL_GetTicks() >= (ctx->last_rumble_time + RUMBLE_REFRESH_INTERVAL_MS)) {
535 ctx->rumble_update_pending = true;
536 HIDAPI_DriverShield_SendNextRumble(device);
537 }
538
539 if (size < 0) {
540 // Read error, device is disconnected
541 HIDAPI_JoystickDisconnected(device, device->joysticks[0]);
542 }
543 return (size >= 0);
544}
545
546static void HIDAPI_DriverShield_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
547{
548}
549
550static void HIDAPI_DriverShield_FreeDevice(SDL_HIDAPI_Device *device)
551{
552}
553
554SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverShield = {
555 SDL_HINT_JOYSTICK_HIDAPI_SHIELD,
556 true,
557 HIDAPI_DriverShield_RegisterHints,
558 HIDAPI_DriverShield_UnregisterHints,
559 HIDAPI_DriverShield_IsEnabled,
560 HIDAPI_DriverShield_IsSupportedDevice,
561 HIDAPI_DriverShield_InitDevice,
562 HIDAPI_DriverShield_GetDevicePlayerIndex,
563 HIDAPI_DriverShield_SetDevicePlayerIndex,
564 HIDAPI_DriverShield_UpdateDevice,
565 HIDAPI_DriverShield_OpenJoystick,
566 HIDAPI_DriverShield_RumbleJoystick,
567 HIDAPI_DriverShield_RumbleJoystickTriggers,
568 HIDAPI_DriverShield_GetJoystickCapabilities,
569 HIDAPI_DriverShield_SetJoystickLED,
570 HIDAPI_DriverShield_SendJoystickEffect,
571 HIDAPI_DriverShield_SetJoystickSensorsEnabled,
572 HIDAPI_DriverShield_CloseJoystick,
573 HIDAPI_DriverShield_FreeDevice,
574};
575
576#endif // SDL_JOYSTICK_HIDAPI_SHIELD
577
578#endif // SDL_JOYSTICK_HIDAPI
579