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 | |
47 | enum |
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 | |
58 | typedef 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 |
67 | typedef struct |
68 | { |
69 | Uint8 report_id; |
70 | Uint8 cmd; |
71 | Uint8 seq_num; |
72 | Uint8 payload[HID_REPORT_SIZE - 3]; |
73 | } ShieldCommandReport_t; |
74 | SDL_COMPILE_TIME_ASSERT(ShieldCommandReport_t, sizeof(ShieldCommandReport_t) == HID_REPORT_SIZE); |
75 | |
76 | typedef 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 | |
95 | static void HIDAPI_DriverShield_RegisterHints(SDL_HintCallback callback, void *userdata) |
96 | { |
97 | SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SHIELD, callback, userdata); |
98 | } |
99 | |
100 | static void HIDAPI_DriverShield_UnregisterHints(SDL_HintCallback callback, void *userdata) |
101 | { |
102 | SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SHIELD, callback, userdata); |
103 | } |
104 | |
105 | static 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 | |
110 | static 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 | |
115 | static 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 | |
130 | static int HIDAPI_DriverShield_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id) |
131 | { |
132 | return -1; |
133 | } |
134 | |
135 | static void HIDAPI_DriverShield_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index) |
136 | { |
137 | } |
138 | |
139 | static 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 | |
171 | static 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 | |
205 | static 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 | |
224 | static 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 | |
254 | static bool HIDAPI_DriverShield_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble) |
255 | { |
256 | return SDL_Unsupported(); |
257 | } |
258 | |
259 | static Uint32 HIDAPI_DriverShield_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) |
260 | { |
261 | return SDL_JOYSTICK_CAP_RUMBLE; |
262 | } |
263 | |
264 | static bool HIDAPI_DriverShield_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue) |
265 | { |
266 | return SDL_Unsupported(); |
267 | } |
268 | |
269 | static 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 | |
284 | static bool HIDAPI_DriverShield_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, bool enabled) |
285 | { |
286 | return SDL_Unsupported(); |
287 | } |
288 | |
289 | static 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 | |
364 | static 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 | |
379 | static 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 | |
456 | static 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 | |
467 | static 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 | |
546 | static void HIDAPI_DriverShield_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) |
547 | { |
548 | } |
549 | |
550 | static void HIDAPI_DriverShield_FreeDevice(SDL_HIDAPI_Device *device) |
551 | { |
552 | } |
553 | |
554 | SDL_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 | |