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 | // This is the gamepad API for Simple DirectMedia Layer |
24 | |
25 | #include "SDL_sysjoystick.h" |
26 | #include "SDL_joystick_c.h" |
27 | #include "SDL_steam_virtual_gamepad.h" |
28 | #include "SDL_gamepad_c.h" |
29 | #include "SDL_gamepad_db.h" |
30 | #include "controller_type.h" |
31 | #include "usb_ids.h" |
32 | #include "hidapi/SDL_hidapi_nintendo.h" |
33 | #include "../events/SDL_events_c.h" |
34 | |
35 | |
36 | #ifdef SDL_PLATFORM_ANDROID |
37 | #endif |
38 | |
39 | // Many gamepads turn the center button into an instantaneous button press |
40 | #define SDL_MINIMUM_GUIDE_BUTTON_DELAY_MS 250 |
41 | |
42 | #define SDL_GAMEPAD_CRC_FIELD "crc:" |
43 | #define SDL_GAMEPAD_CRC_FIELD_SIZE 4 // hard-coded for speed |
44 | #define SDL_GAMEPAD_TYPE_FIELD "type:" |
45 | #define SDL_GAMEPAD_TYPE_FIELD_SIZE SDL_strlen(SDL_GAMEPAD_TYPE_FIELD) |
46 | #define SDL_GAMEPAD_FACE_FIELD "face:" |
47 | #define SDL_GAMEPAD_FACE_FIELD_SIZE 5 // hard-coded for speed |
48 | #define SDL_GAMEPAD_PLATFORM_FIELD "platform:" |
49 | #define SDL_GAMEPAD_PLATFORM_FIELD_SIZE SDL_strlen(SDL_GAMEPAD_PLATFORM_FIELD) |
50 | #define SDL_GAMEPAD_HINT_FIELD "hint:" |
51 | #define SDL_GAMEPAD_HINT_FIELD_SIZE SDL_strlen(SDL_GAMEPAD_HINT_FIELD) |
52 | #define SDL_GAMEPAD_SDKGE_FIELD "sdk>=:" |
53 | #define SDL_GAMEPAD_SDKGE_FIELD_SIZE SDL_strlen(SDL_GAMEPAD_SDKGE_FIELD) |
54 | #define SDL_GAMEPAD_SDKLE_FIELD "sdk<=:" |
55 | #define SDL_GAMEPAD_SDKLE_FIELD_SIZE SDL_strlen(SDL_GAMEPAD_SDKLE_FIELD) |
56 | |
57 | static bool SDL_gamepads_initialized; |
58 | static SDL_Gamepad *SDL_gamepads SDL_GUARDED_BY(SDL_joystick_lock) = NULL; |
59 | |
60 | // The face button style of a gamepad |
61 | typedef enum |
62 | { |
63 | SDL_GAMEPAD_FACE_STYLE_UNKNOWN, |
64 | SDL_GAMEPAD_FACE_STYLE_ABXY, |
65 | SDL_GAMEPAD_FACE_STYLE_BAYX, |
66 | SDL_GAMEPAD_FACE_STYLE_SONY, |
67 | } SDL_GamepadFaceStyle; |
68 | |
69 | // our hard coded list of mapping support |
70 | typedef enum |
71 | { |
72 | SDL_GAMEPAD_MAPPING_PRIORITY_DEFAULT, |
73 | SDL_GAMEPAD_MAPPING_PRIORITY_API, |
74 | SDL_GAMEPAD_MAPPING_PRIORITY_USER, |
75 | } SDL_GamepadMappingPriority; |
76 | |
77 | #define _guarded SDL_GUARDED_BY(SDL_joystick_lock) |
78 | |
79 | typedef struct GamepadMapping_t |
80 | { |
81 | SDL_GUID guid _guarded; |
82 | char *name _guarded; |
83 | char *mapping _guarded; |
84 | SDL_GamepadMappingPriority priority _guarded; |
85 | struct GamepadMapping_t *next _guarded; |
86 | } GamepadMapping_t; |
87 | |
88 | typedef struct |
89 | { |
90 | int refcount _guarded; |
91 | SDL_JoystickID *joysticks _guarded; |
92 | GamepadMapping_t **joystick_mappings _guarded; |
93 | |
94 | int num_changed_mappings _guarded; |
95 | GamepadMapping_t **changed_mappings _guarded; |
96 | |
97 | } MappingChangeTracker; |
98 | |
99 | #undef _guarded |
100 | |
101 | static SDL_GUID s_zeroGUID; |
102 | static GamepadMapping_t *s_pSupportedGamepads SDL_GUARDED_BY(SDL_joystick_lock) = NULL; |
103 | static GamepadMapping_t *s_pDefaultMapping SDL_GUARDED_BY(SDL_joystick_lock) = NULL; |
104 | static GamepadMapping_t *s_pXInputMapping SDL_GUARDED_BY(SDL_joystick_lock) = NULL; |
105 | static MappingChangeTracker *s_mappingChangeTracker SDL_GUARDED_BY(SDL_joystick_lock) = NULL; |
106 | static SDL_HashTable *s_gamepadInstanceIDs SDL_GUARDED_BY(SDL_joystick_lock) = NULL; |
107 | |
108 | #define _guarded SDL_GUARDED_BY(SDL_joystick_lock) |
109 | |
110 | // The SDL gamepad structure |
111 | struct SDL_Gamepad |
112 | { |
113 | SDL_Joystick *joystick _guarded; // underlying joystick device |
114 | int ref_count _guarded; |
115 | |
116 | const char *name _guarded; |
117 | SDL_GamepadType type _guarded; |
118 | SDL_GamepadFaceStyle face_style _guarded; |
119 | GamepadMapping_t *mapping _guarded; |
120 | int num_bindings _guarded; |
121 | SDL_GamepadBinding *bindings _guarded; |
122 | SDL_GamepadBinding **last_match_axis _guarded; |
123 | Uint8 *last_hat_mask _guarded; |
124 | Uint64 guide_button_down _guarded; |
125 | |
126 | struct SDL_Gamepad *next _guarded; // pointer to next gamepad we have allocated |
127 | }; |
128 | |
129 | #undef _guarded |
130 | |
131 | #define CHECK_GAMEPAD_MAGIC(gamepad, result) \ |
132 | if (!SDL_ObjectValid(gamepad, SDL_OBJECT_TYPE_GAMEPAD) || \ |
133 | !SDL_IsJoystickValid(gamepad->joystick)) { \ |
134 | SDL_InvalidParamError("gamepad"); \ |
135 | SDL_UnlockJoysticks(); \ |
136 | return result; \ |
137 | } |
138 | |
139 | static SDL_vidpid_list SDL_allowed_gamepads = { |
140 | SDL_HINT_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT, 0, 0, NULL, |
141 | NULL, 0, 0, NULL, |
142 | 0, NULL, |
143 | false |
144 | }; |
145 | static SDL_vidpid_list SDL_ignored_gamepads = { |
146 | SDL_HINT_GAMECONTROLLER_IGNORE_DEVICES, 0, 0, NULL, |
147 | NULL, 0, 0, NULL, |
148 | 0, NULL, |
149 | false |
150 | }; |
151 | |
152 | static GamepadMapping_t *SDL_PrivateAddMappingForGUID(SDL_GUID jGUID, const char *mappingString, bool *existing, SDL_GamepadMappingPriority priority); |
153 | static void SDL_PrivateLoadButtonMapping(SDL_Gamepad *gamepad, GamepadMapping_t *pGamepadMapping); |
154 | static GamepadMapping_t *SDL_PrivateGetGamepadMapping(SDL_JoystickID instance_id, bool create_mapping); |
155 | static void SDL_SendGamepadAxis(Uint64 timestamp, SDL_Gamepad *gamepad, SDL_GamepadAxis axis, Sint16 value); |
156 | static void SDL_SendGamepadButton(Uint64 timestamp, SDL_Gamepad *gamepad, SDL_GamepadButton button, bool down); |
157 | |
158 | static bool HasSameOutput(SDL_GamepadBinding *a, SDL_GamepadBinding *b) |
159 | { |
160 | if (a->output_type != b->output_type) { |
161 | return false; |
162 | } |
163 | |
164 | if (a->output_type == SDL_GAMEPAD_BINDTYPE_AXIS) { |
165 | return a->output.axis.axis == b->output.axis.axis; |
166 | } else { |
167 | return a->output.button == b->output.button; |
168 | } |
169 | } |
170 | |
171 | static void ResetOutput(Uint64 timestamp, SDL_Gamepad *gamepad, SDL_GamepadBinding *bind) |
172 | { |
173 | if (bind->output_type == SDL_GAMEPAD_BINDTYPE_AXIS) { |
174 | SDL_SendGamepadAxis(timestamp, gamepad, bind->output.axis.axis, 0); |
175 | } else { |
176 | SDL_SendGamepadButton(timestamp, gamepad, bind->output.button, false); |
177 | } |
178 | } |
179 | |
180 | static void HandleJoystickAxis(Uint64 timestamp, SDL_Gamepad *gamepad, int axis, int value) |
181 | { |
182 | int i; |
183 | SDL_GamepadBinding *last_match; |
184 | SDL_GamepadBinding *match = NULL; |
185 | |
186 | SDL_AssertJoysticksLocked(); |
187 | |
188 | last_match = gamepad->last_match_axis[axis]; |
189 | for (i = 0; i < gamepad->num_bindings; ++i) { |
190 | SDL_GamepadBinding *binding = &gamepad->bindings[i]; |
191 | if (binding->input_type == SDL_GAMEPAD_BINDTYPE_AXIS && |
192 | axis == binding->input.axis.axis) { |
193 | if (binding->input.axis.axis_min < binding->input.axis.axis_max) { |
194 | if (value >= binding->input.axis.axis_min && |
195 | value <= binding->input.axis.axis_max) { |
196 | match = binding; |
197 | break; |
198 | } |
199 | } else { |
200 | if (value >= binding->input.axis.axis_max && |
201 | value <= binding->input.axis.axis_min) { |
202 | match = binding; |
203 | break; |
204 | } |
205 | } |
206 | } |
207 | } |
208 | |
209 | if (last_match && (!match || !HasSameOutput(last_match, match))) { |
210 | // Clear the last input that this axis generated |
211 | ResetOutput(timestamp, gamepad, last_match); |
212 | } |
213 | |
214 | if (match) { |
215 | if (match->output_type == SDL_GAMEPAD_BINDTYPE_AXIS) { |
216 | if (match->input.axis.axis_min != match->output.axis.axis_min || match->input.axis.axis_max != match->output.axis.axis_max) { |
217 | float normalized_value = (float)(value - match->input.axis.axis_min) / (match->input.axis.axis_max - match->input.axis.axis_min); |
218 | value = match->output.axis.axis_min + (int)(normalized_value * (match->output.axis.axis_max - match->output.axis.axis_min)); |
219 | } |
220 | SDL_SendGamepadAxis(timestamp, gamepad, match->output.axis.axis, (Sint16)value); |
221 | } else { |
222 | bool down; |
223 | int threshold = match->input.axis.axis_min + (match->input.axis.axis_max - match->input.axis.axis_min) / 2; |
224 | if (match->input.axis.axis_max < match->input.axis.axis_min) { |
225 | down = (value <= threshold); |
226 | } else { |
227 | down = (value >= threshold); |
228 | } |
229 | SDL_SendGamepadButton(timestamp, gamepad, match->output.button, down); |
230 | } |
231 | } |
232 | gamepad->last_match_axis[axis] = match; |
233 | } |
234 | |
235 | static void HandleJoystickButton(Uint64 timestamp, SDL_Gamepad *gamepad, int button, bool down) |
236 | { |
237 | int i; |
238 | |
239 | SDL_AssertJoysticksLocked(); |
240 | |
241 | for (i = 0; i < gamepad->num_bindings; ++i) { |
242 | SDL_GamepadBinding *binding = &gamepad->bindings[i]; |
243 | if (binding->input_type == SDL_GAMEPAD_BINDTYPE_BUTTON && |
244 | button == binding->input.button) { |
245 | if (binding->output_type == SDL_GAMEPAD_BINDTYPE_AXIS) { |
246 | int value = down ? binding->output.axis.axis_max : binding->output.axis.axis_min; |
247 | SDL_SendGamepadAxis(timestamp, gamepad, binding->output.axis.axis, (Sint16)value); |
248 | } else { |
249 | SDL_SendGamepadButton(timestamp, gamepad, binding->output.button, down); |
250 | } |
251 | break; |
252 | } |
253 | } |
254 | } |
255 | |
256 | static void HandleJoystickHat(Uint64 timestamp, SDL_Gamepad *gamepad, int hat, Uint8 value) |
257 | { |
258 | int i; |
259 | Uint8 last_mask, changed_mask; |
260 | |
261 | SDL_AssertJoysticksLocked(); |
262 | |
263 | last_mask = gamepad->last_hat_mask[hat]; |
264 | changed_mask = (last_mask ^ value); |
265 | for (i = 0; i < gamepad->num_bindings; ++i) { |
266 | SDL_GamepadBinding *binding = &gamepad->bindings[i]; |
267 | if (binding->input_type == SDL_GAMEPAD_BINDTYPE_HAT && hat == binding->input.hat.hat) { |
268 | if ((changed_mask & binding->input.hat.hat_mask) != 0) { |
269 | if (value & binding->input.hat.hat_mask) { |
270 | if (binding->output_type == SDL_GAMEPAD_BINDTYPE_AXIS) { |
271 | SDL_SendGamepadAxis(timestamp, gamepad, binding->output.axis.axis, (Sint16)binding->output.axis.axis_max); |
272 | } else { |
273 | SDL_SendGamepadButton(timestamp, gamepad, binding->output.button, true); |
274 | } |
275 | } else { |
276 | ResetOutput(timestamp, gamepad, binding); |
277 | } |
278 | } |
279 | } |
280 | } |
281 | gamepad->last_hat_mask[hat] = value; |
282 | } |
283 | |
284 | /* The joystick layer will _also_ send events to recenter before disconnect, |
285 | but it has to make (sometimes incorrect) guesses at what being "centered" |
286 | is. The gamepad layer, however, can set a definite logical idle |
287 | position, so set them all here. If we happened to already be at the |
288 | center thanks to the joystick layer or idle hands, this won't generate |
289 | duplicate events. */ |
290 | static void RecenterGamepad(SDL_Gamepad *gamepad) |
291 | { |
292 | int i; |
293 | Uint64 timestamp = SDL_GetTicksNS(); |
294 | |
295 | for (i = 0; i < SDL_GAMEPAD_BUTTON_COUNT; ++i) { |
296 | SDL_GamepadButton button = (SDL_GamepadButton)i; |
297 | if (SDL_GetGamepadButton(gamepad, button)) { |
298 | SDL_SendGamepadButton(timestamp, gamepad, button, false); |
299 | } |
300 | } |
301 | |
302 | for (i = 0; i < SDL_GAMEPAD_AXIS_COUNT; ++i) { |
303 | SDL_GamepadAxis axis = (SDL_GamepadAxis)i; |
304 | if (SDL_GetGamepadAxis(gamepad, axis) != 0) { |
305 | SDL_SendGamepadAxis(timestamp, gamepad, axis, 0); |
306 | } |
307 | } |
308 | } |
309 | |
310 | void SDL_PrivateGamepadAdded(SDL_JoystickID instance_id) |
311 | { |
312 | SDL_Event event; |
313 | |
314 | if (!SDL_gamepads_initialized) { |
315 | return; |
316 | } |
317 | |
318 | event.type = SDL_EVENT_GAMEPAD_ADDED; |
319 | event.common.timestamp = 0; |
320 | event.gdevice.which = instance_id; |
321 | SDL_PushEvent(&event); |
322 | } |
323 | |
324 | void SDL_PrivateGamepadRemoved(SDL_JoystickID instance_id) |
325 | { |
326 | SDL_Event event; |
327 | SDL_Gamepad *gamepad; |
328 | |
329 | SDL_AssertJoysticksLocked(); |
330 | |
331 | if (!SDL_gamepads_initialized) { |
332 | return; |
333 | } |
334 | |
335 | for (gamepad = SDL_gamepads; gamepad; gamepad = gamepad->next) { |
336 | if (gamepad->joystick->instance_id == instance_id) { |
337 | RecenterGamepad(gamepad); |
338 | break; |
339 | } |
340 | } |
341 | |
342 | event.type = SDL_EVENT_GAMEPAD_REMOVED; |
343 | event.common.timestamp = 0; |
344 | event.gdevice.which = instance_id; |
345 | SDL_PushEvent(&event); |
346 | } |
347 | |
348 | static void SDL_PrivateGamepadRemapped(SDL_JoystickID instance_id) |
349 | { |
350 | SDL_Event event; |
351 | |
352 | if (!SDL_gamepads_initialized || SDL_IsJoystickBeingAdded()) { |
353 | return; |
354 | } |
355 | |
356 | event.type = SDL_EVENT_GAMEPAD_REMAPPED; |
357 | event.common.timestamp = 0; |
358 | event.gdevice.which = instance_id; |
359 | SDL_PushEvent(&event); |
360 | } |
361 | |
362 | /* |
363 | * Event filter to fire gamepad events from joystick ones |
364 | */ |
365 | static bool SDLCALL SDL_GamepadEventWatcher(void *userdata, SDL_Event *event) |
366 | { |
367 | SDL_Gamepad *gamepad; |
368 | |
369 | switch (event->type) { |
370 | case SDL_EVENT_JOYSTICK_AXIS_MOTION: |
371 | { |
372 | SDL_AssertJoysticksLocked(); |
373 | |
374 | for (gamepad = SDL_gamepads; gamepad; gamepad = gamepad->next) { |
375 | if (gamepad->joystick->instance_id == event->jaxis.which) { |
376 | HandleJoystickAxis(event->common.timestamp, gamepad, event->jaxis.axis, event->jaxis.value); |
377 | break; |
378 | } |
379 | } |
380 | } break; |
381 | case SDL_EVENT_JOYSTICK_BUTTON_DOWN: |
382 | case SDL_EVENT_JOYSTICK_BUTTON_UP: |
383 | { |
384 | SDL_AssertJoysticksLocked(); |
385 | |
386 | for (gamepad = SDL_gamepads; gamepad; gamepad = gamepad->next) { |
387 | if (gamepad->joystick->instance_id == event->jbutton.which) { |
388 | HandleJoystickButton(event->common.timestamp, gamepad, event->jbutton.button, event->jbutton.down); |
389 | break; |
390 | } |
391 | } |
392 | } break; |
393 | case SDL_EVENT_JOYSTICK_HAT_MOTION: |
394 | { |
395 | SDL_AssertJoysticksLocked(); |
396 | |
397 | for (gamepad = SDL_gamepads; gamepad; gamepad = gamepad->next) { |
398 | if (gamepad->joystick->instance_id == event->jhat.which) { |
399 | HandleJoystickHat(event->common.timestamp, gamepad, event->jhat.hat, event->jhat.value); |
400 | break; |
401 | } |
402 | } |
403 | } break; |
404 | case SDL_EVENT_JOYSTICK_UPDATE_COMPLETE: |
405 | { |
406 | SDL_AssertJoysticksLocked(); |
407 | |
408 | for (gamepad = SDL_gamepads; gamepad; gamepad = gamepad->next) { |
409 | if (gamepad->joystick->instance_id == event->jdevice.which) { |
410 | SDL_Event deviceevent; |
411 | |
412 | deviceevent.type = SDL_EVENT_GAMEPAD_UPDATE_COMPLETE; |
413 | deviceevent.common.timestamp = event->jdevice.timestamp; |
414 | deviceevent.gdevice.which = event->jdevice.which; |
415 | SDL_PushEvent(&deviceevent); |
416 | break; |
417 | } |
418 | } |
419 | } break; |
420 | default: |
421 | break; |
422 | } |
423 | |
424 | return true; |
425 | } |
426 | |
427 | /* SDL defines sensor orientation relative to the device natural |
428 | orientation, so when it's changed orientation to be used as a |
429 | gamepad, change the sensor orientation to match. |
430 | */ |
431 | static void AdjustSensorOrientation(const SDL_Joystick *joystick, const float *src, float *dst) |
432 | { |
433 | unsigned int i, j; |
434 | |
435 | SDL_AssertJoysticksLocked(); |
436 | |
437 | for (i = 0; i < 3; ++i) { |
438 | dst[i] = 0.0f; |
439 | for (j = 0; j < 3; ++j) { |
440 | dst[i] += joystick->sensor_transform[i][j] * src[j]; |
441 | } |
442 | } |
443 | } |
444 | |
445 | /* |
446 | * Event filter to fire gamepad sensor events from system sensor events |
447 | * |
448 | * We don't use SDL_GamepadEventWatcher() for this because we want to |
449 | * deliver gamepad sensor events when system sensor events are disabled, |
450 | * and we also need to avoid a potential deadlock where joystick event |
451 | * delivery locks the joysticks and then the event queue, but sensor |
452 | * event delivery would lock the event queue and then from within the |
453 | * event watcher function lock the joysticks. |
454 | */ |
455 | void SDL_GamepadSensorWatcher(Uint64 timestamp, SDL_SensorID sensor, Uint64 sensor_timestamp, float *data, int num_values) |
456 | { |
457 | SDL_Gamepad *gamepad; |
458 | |
459 | SDL_LockJoysticks(); |
460 | for (gamepad = SDL_gamepads; gamepad; gamepad = gamepad->next) { |
461 | if (gamepad->joystick->accel && gamepad->joystick->accel_sensor == sensor) { |
462 | float gamepad_data[3]; |
463 | AdjustSensorOrientation(gamepad->joystick, data, gamepad_data); |
464 | SDL_SendJoystickSensor(timestamp, gamepad->joystick, SDL_SENSOR_ACCEL, sensor_timestamp, gamepad_data, SDL_arraysize(gamepad_data)); |
465 | } |
466 | if (gamepad->joystick->gyro && gamepad->joystick->gyro_sensor == sensor) { |
467 | float gamepad_data[3]; |
468 | AdjustSensorOrientation(gamepad->joystick, data, gamepad_data); |
469 | SDL_SendJoystickSensor(timestamp, gamepad->joystick, SDL_SENSOR_GYRO, sensor_timestamp, gamepad_data, SDL_arraysize(gamepad_data)); |
470 | } |
471 | } |
472 | SDL_UnlockJoysticks(); |
473 | } |
474 | |
475 | static void PushMappingChangeTracking(void) |
476 | { |
477 | MappingChangeTracker *tracker; |
478 | int i, num_joysticks; |
479 | |
480 | SDL_AssertJoysticksLocked(); |
481 | |
482 | if (s_mappingChangeTracker) { |
483 | ++s_mappingChangeTracker->refcount; |
484 | return; |
485 | } |
486 | s_mappingChangeTracker = (MappingChangeTracker *)SDL_calloc(1, sizeof(*tracker)); |
487 | s_mappingChangeTracker->refcount = 1; |
488 | |
489 | // Save the list of joysticks and associated mappings |
490 | tracker = s_mappingChangeTracker; |
491 | tracker->joysticks = SDL_GetJoysticks(&num_joysticks); |
492 | if (!tracker->joysticks) { |
493 | return; |
494 | } |
495 | if (num_joysticks == 0) { |
496 | return; |
497 | } |
498 | tracker->joystick_mappings = (GamepadMapping_t **)SDL_malloc(num_joysticks * sizeof(*tracker->joystick_mappings)); |
499 | if (!tracker->joystick_mappings) { |
500 | return; |
501 | } |
502 | for (i = 0; i < num_joysticks; ++i) { |
503 | tracker->joystick_mappings[i] = SDL_PrivateGetGamepadMapping(tracker->joysticks[i], false); |
504 | } |
505 | } |
506 | |
507 | static void AddMappingChangeTracking(GamepadMapping_t *mapping) |
508 | { |
509 | MappingChangeTracker *tracker; |
510 | int num_mappings; |
511 | GamepadMapping_t **new_mappings; |
512 | |
513 | SDL_AssertJoysticksLocked(); |
514 | |
515 | SDL_assert(s_mappingChangeTracker != NULL); |
516 | tracker = s_mappingChangeTracker; |
517 | num_mappings = tracker->num_changed_mappings; |
518 | new_mappings = (GamepadMapping_t **)SDL_realloc(tracker->changed_mappings, (num_mappings + 1) * sizeof(*new_mappings)); |
519 | if (new_mappings) { |
520 | tracker->changed_mappings = new_mappings; |
521 | tracker->changed_mappings[num_mappings] = mapping; |
522 | tracker->num_changed_mappings = (num_mappings + 1); |
523 | } |
524 | } |
525 | |
526 | static bool HasMappingChangeTracking(MappingChangeTracker *tracker, GamepadMapping_t *mapping) |
527 | { |
528 | int i; |
529 | |
530 | SDL_AssertJoysticksLocked(); |
531 | |
532 | for (i = 0; i < tracker->num_changed_mappings; ++i) { |
533 | if (tracker->changed_mappings[i] == mapping) { |
534 | return true; |
535 | } |
536 | } |
537 | return false; |
538 | } |
539 | |
540 | static void PopMappingChangeTracking(void) |
541 | { |
542 | int i; |
543 | MappingChangeTracker *tracker; |
544 | |
545 | SDL_AssertJoysticksLocked(); |
546 | |
547 | SDL_assert(s_mappingChangeTracker != NULL); |
548 | tracker = s_mappingChangeTracker; |
549 | --tracker->refcount; |
550 | if (tracker->refcount > 0) { |
551 | return; |
552 | } |
553 | s_mappingChangeTracker = NULL; |
554 | |
555 | // Now check to see what gamepads changed because of the mapping changes |
556 | if (tracker->joysticks && tracker->joystick_mappings) { |
557 | for (i = 0; tracker->joysticks[i]; ++i) { |
558 | // Looking up the new mapping might create one and associate it with the gamepad (and generate events) |
559 | SDL_JoystickID joystick = tracker->joysticks[i]; |
560 | SDL_Gamepad *gamepad = SDL_GetGamepadFromID(joystick); |
561 | GamepadMapping_t *new_mapping = SDL_PrivateGetGamepadMapping(joystick, false); |
562 | GamepadMapping_t *old_mapping = gamepad ? gamepad->mapping : tracker->joystick_mappings[i]; |
563 | |
564 | if (new_mapping && !old_mapping) { |
565 | SDL_InsertIntoHashTable(s_gamepadInstanceIDs, (void *)(uintptr_t)joystick, (const void *)true, true); |
566 | SDL_PrivateGamepadAdded(joystick); |
567 | } else if (old_mapping && !new_mapping) { |
568 | SDL_InsertIntoHashTable(s_gamepadInstanceIDs, (void *)(uintptr_t)joystick, (const void *)false, true); |
569 | SDL_PrivateGamepadRemoved(joystick); |
570 | } else if (old_mapping != new_mapping || HasMappingChangeTracking(tracker, new_mapping)) { |
571 | if (gamepad) { |
572 | SDL_PrivateLoadButtonMapping(gamepad, new_mapping); |
573 | } |
574 | SDL_PrivateGamepadRemapped(joystick); |
575 | } |
576 | } |
577 | } |
578 | |
579 | SDL_free(tracker->joysticks); |
580 | SDL_free(tracker->joystick_mappings); |
581 | SDL_free(tracker->changed_mappings); |
582 | SDL_free(tracker); |
583 | } |
584 | |
585 | #ifdef SDL_PLATFORM_ANDROID |
586 | /* |
587 | * Helper function to guess at a mapping based on the elements reported for this gamepad |
588 | */ |
589 | static GamepadMapping_t *SDL_CreateMappingForAndroidGamepad(SDL_GUID guid) |
590 | { |
591 | const int face_button_mask = ((1 << SDL_GAMEPAD_BUTTON_SOUTH) | |
592 | (1 << SDL_GAMEPAD_BUTTON_EAST) | |
593 | (1 << SDL_GAMEPAD_BUTTON_WEST) | |
594 | (1 << SDL_GAMEPAD_BUTTON_NORTH)); |
595 | bool existing; |
596 | char mapping_string[1024]; |
597 | int button_mask; |
598 | int axis_mask; |
599 | |
600 | button_mask = SDL_Swap16LE(*(Uint16 *)(&guid.data[sizeof(guid.data) - 4])); |
601 | axis_mask = SDL_Swap16LE(*(Uint16 *)(&guid.data[sizeof(guid.data) - 2])); |
602 | if (!button_mask && !axis_mask) { |
603 | // Accelerometer, shouldn't have a gamepad mapping |
604 | return NULL; |
605 | } |
606 | if (!(button_mask & face_button_mask)) { |
607 | // We don't know what buttons or axes are supported, don't make up a mapping |
608 | return NULL; |
609 | } |
610 | |
611 | SDL_strlcpy(mapping_string, "none,*," , sizeof(mapping_string)); |
612 | |
613 | if (button_mask & (1 << SDL_GAMEPAD_BUTTON_SOUTH)) { |
614 | SDL_strlcat(mapping_string, "a:b0," , sizeof(mapping_string)); |
615 | } |
616 | if (button_mask & (1 << SDL_GAMEPAD_BUTTON_EAST)) { |
617 | SDL_strlcat(mapping_string, "b:b1," , sizeof(mapping_string)); |
618 | } else if (button_mask & (1 << SDL_GAMEPAD_BUTTON_BACK)) { |
619 | // Use the back button as "B" for easy UI navigation with TV remotes |
620 | SDL_strlcat(mapping_string, "b:b4," , sizeof(mapping_string)); |
621 | button_mask &= ~(1 << SDL_GAMEPAD_BUTTON_BACK); |
622 | } |
623 | if (button_mask & (1 << SDL_GAMEPAD_BUTTON_WEST)) { |
624 | SDL_strlcat(mapping_string, "x:b2," , sizeof(mapping_string)); |
625 | } |
626 | if (button_mask & (1 << SDL_GAMEPAD_BUTTON_NORTH)) { |
627 | SDL_strlcat(mapping_string, "y:b3," , sizeof(mapping_string)); |
628 | } |
629 | if (button_mask & (1 << SDL_GAMEPAD_BUTTON_BACK)) { |
630 | SDL_strlcat(mapping_string, "back:b4," , sizeof(mapping_string)); |
631 | } |
632 | if (button_mask & (1 << SDL_GAMEPAD_BUTTON_GUIDE)) { |
633 | // The guide button generally isn't functional (or acts as a home button) on most Android gamepads before Android 11 |
634 | if (SDL_GetAndroidSDKVersion() >= 30 /* Android 11 */) { |
635 | SDL_strlcat(mapping_string, "guide:b5," , sizeof(mapping_string)); |
636 | } |
637 | } |
638 | if (button_mask & (1 << SDL_GAMEPAD_BUTTON_START)) { |
639 | SDL_strlcat(mapping_string, "start:b6," , sizeof(mapping_string)); |
640 | } |
641 | if (button_mask & (1 << SDL_GAMEPAD_BUTTON_LEFT_STICK)) { |
642 | SDL_strlcat(mapping_string, "leftstick:b7," , sizeof(mapping_string)); |
643 | } |
644 | if (button_mask & (1 << SDL_GAMEPAD_BUTTON_RIGHT_STICK)) { |
645 | SDL_strlcat(mapping_string, "rightstick:b8," , sizeof(mapping_string)); |
646 | } |
647 | if (button_mask & (1 << SDL_GAMEPAD_BUTTON_LEFT_SHOULDER)) { |
648 | SDL_strlcat(mapping_string, "leftshoulder:b9," , sizeof(mapping_string)); |
649 | } |
650 | if (button_mask & (1 << SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER)) { |
651 | SDL_strlcat(mapping_string, "rightshoulder:b10," , sizeof(mapping_string)); |
652 | } |
653 | if (button_mask & (1 << SDL_GAMEPAD_BUTTON_DPAD_UP)) { |
654 | SDL_strlcat(mapping_string, "dpup:b11," , sizeof(mapping_string)); |
655 | } |
656 | if (button_mask & (1 << SDL_GAMEPAD_BUTTON_DPAD_DOWN)) { |
657 | SDL_strlcat(mapping_string, "dpdown:b12," , sizeof(mapping_string)); |
658 | } |
659 | if (button_mask & (1 << SDL_GAMEPAD_BUTTON_DPAD_LEFT)) { |
660 | SDL_strlcat(mapping_string, "dpleft:b13," , sizeof(mapping_string)); |
661 | } |
662 | if (button_mask & (1 << SDL_GAMEPAD_BUTTON_DPAD_RIGHT)) { |
663 | SDL_strlcat(mapping_string, "dpright:b14," , sizeof(mapping_string)); |
664 | } |
665 | if (axis_mask & (1 << SDL_GAMEPAD_AXIS_LEFTX)) { |
666 | SDL_strlcat(mapping_string, "leftx:a0," , sizeof(mapping_string)); |
667 | } |
668 | if (axis_mask & (1 << SDL_GAMEPAD_AXIS_LEFTY)) { |
669 | SDL_strlcat(mapping_string, "lefty:a1," , sizeof(mapping_string)); |
670 | } |
671 | if (axis_mask & (1 << SDL_GAMEPAD_AXIS_RIGHTX)) { |
672 | SDL_strlcat(mapping_string, "rightx:a2," , sizeof(mapping_string)); |
673 | } |
674 | if (axis_mask & (1 << SDL_GAMEPAD_AXIS_RIGHTY)) { |
675 | SDL_strlcat(mapping_string, "righty:a3," , sizeof(mapping_string)); |
676 | } |
677 | if (axis_mask & (1 << SDL_GAMEPAD_AXIS_LEFT_TRIGGER)) { |
678 | SDL_strlcat(mapping_string, "lefttrigger:a4," , sizeof(mapping_string)); |
679 | } |
680 | if (axis_mask & (1 << SDL_GAMEPAD_AXIS_RIGHT_TRIGGER)) { |
681 | SDL_strlcat(mapping_string, "righttrigger:a5," , sizeof(mapping_string)); |
682 | } |
683 | |
684 | return SDL_PrivateAddMappingForGUID(guid, mapping_string, &existing, SDL_GAMEPAD_MAPPING_PRIORITY_DEFAULT); |
685 | } |
686 | #endif // SDL_PLATFORM_ANDROID |
687 | |
688 | /* |
689 | * Helper function to guess at a mapping for HIDAPI gamepads |
690 | */ |
691 | static GamepadMapping_t *SDL_CreateMappingForHIDAPIGamepad(SDL_GUID guid) |
692 | { |
693 | bool existing; |
694 | char mapping_string[1024]; |
695 | Uint16 vendor; |
696 | Uint16 product; |
697 | |
698 | SDL_strlcpy(mapping_string, "none,*," , sizeof(mapping_string)); |
699 | |
700 | SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL); |
701 | |
702 | if (SDL_IsJoystickWheel(vendor, product)) { |
703 | // We don't want to pick up Logitech FFB wheels here |
704 | // Some versions of WINE will also not treat devices that show up as gamepads as wheels |
705 | return NULL; |
706 | } |
707 | |
708 | if ((vendor == USB_VENDOR_NINTENDO && product == USB_PRODUCT_NINTENDO_GAMECUBE_ADAPTER) || |
709 | (vendor == USB_VENDOR_DRAGONRISE && |
710 | (product == USB_PRODUCT_EVORETRO_GAMECUBE_ADAPTER1 || |
711 | product == USB_PRODUCT_EVORETRO_GAMECUBE_ADAPTER2))) { |
712 | // GameCube driver has 12 buttons and 6 axes |
713 | SDL_strlcat(mapping_string, "a:b0,b:b1,dpdown:b6,dpleft:b4,dpright:b5,dpup:b7,lefttrigger:a4,leftx:a0,lefty:a1~,rightshoulder:b9,righttrigger:a5,rightx:a2,righty:a3~,start:b8,x:b2,y:b3," , sizeof(mapping_string)); |
714 | } else if (vendor == USB_VENDOR_NINTENDO && |
715 | (guid.data[15] == k_eSwitchDeviceInfoControllerType_HVCLeft || |
716 | guid.data[15] == k_eSwitchDeviceInfoControllerType_HVCRight || |
717 | guid.data[15] == k_eSwitchDeviceInfoControllerType_NESLeft || |
718 | guid.data[15] == k_eSwitchDeviceInfoControllerType_NESRight || |
719 | guid.data[15] == k_eSwitchDeviceInfoControllerType_SNES || |
720 | guid.data[15] == k_eSwitchDeviceInfoControllerType_N64 || |
721 | guid.data[15] == k_eSwitchDeviceInfoControllerType_SEGA_Genesis || |
722 | guid.data[15] == k_eWiiExtensionControllerType_None || |
723 | guid.data[15] == k_eWiiExtensionControllerType_Nunchuk || |
724 | guid.data[15] == k_eSwitchDeviceInfoControllerType_JoyConLeft || |
725 | guid.data[15] == k_eSwitchDeviceInfoControllerType_JoyConRight)) { |
726 | switch (guid.data[15]) { |
727 | case k_eSwitchDeviceInfoControllerType_HVCLeft: |
728 | SDL_strlcat(mapping_string, "a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,rightshoulder:b10,start:b6," , sizeof(mapping_string)); |
729 | break; |
730 | case k_eSwitchDeviceInfoControllerType_HVCRight: |
731 | SDL_strlcat(mapping_string, "a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,rightshoulder:b10," , sizeof(mapping_string)); |
732 | break; |
733 | case k_eSwitchDeviceInfoControllerType_NESLeft: |
734 | case k_eSwitchDeviceInfoControllerType_NESRight: |
735 | SDL_strlcat(mapping_string, "a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,rightshoulder:b10,start:b6," , sizeof(mapping_string)); |
736 | break; |
737 | case k_eSwitchDeviceInfoControllerType_SNES: |
738 | SDL_strlcat(mapping_string, "a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,lefttrigger:a4,rightshoulder:b10,righttrigger:a5,start:b6,x:b2,y:b3," , sizeof(mapping_string)); |
739 | break; |
740 | case k_eSwitchDeviceInfoControllerType_N64: |
741 | SDL_strlcat(mapping_string, "a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,start:b6,x:b2,y:b3,misc1:b11," , sizeof(mapping_string)); |
742 | break; |
743 | case k_eSwitchDeviceInfoControllerType_SEGA_Genesis: |
744 | SDL_strlcat(mapping_string, "a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,rightshoulder:b10,righttrigger:a5,start:b6,x:b2,y:b3,misc1:b11," , sizeof(mapping_string)); |
745 | break; |
746 | case k_eWiiExtensionControllerType_None: |
747 | SDL_strlcat(mapping_string, "a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,start:b6,x:b2,y:b3," , sizeof(mapping_string)); |
748 | break; |
749 | case k_eWiiExtensionControllerType_Nunchuk: |
750 | { |
751 | // FIXME: Should we map this to the left or right side? |
752 | const bool map_nunchuck_left_side = true; |
753 | |
754 | if (map_nunchuck_left_side) { |
755 | SDL_strlcat(mapping_string, "a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:a4,leftx:a0,lefty:a1,start:b6,x:b2,y:b3," , sizeof(mapping_string)); |
756 | } else { |
757 | SDL_strlcat(mapping_string, "a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,rightshoulder:b9,righttrigger:a4,rightx:a0,righty:a1,start:b6,x:b2,y:b3," , sizeof(mapping_string)); |
758 | } |
759 | } break; |
760 | default: |
761 | if (SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS, false)) { |
762 | // Vertical mode |
763 | if (guid.data[15] == k_eSwitchDeviceInfoControllerType_JoyConLeft) { |
764 | SDL_strlcat(mapping_string, "back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,misc1:b11,paddle2:b13,paddle4:b15," , sizeof(mapping_string)); |
765 | } else { |
766 | SDL_strlcat(mapping_string, "a:b0,b:b1,guide:b5,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,paddle1:b12,paddle3:b14," , sizeof(mapping_string)); |
767 | } |
768 | } else { |
769 | // Mini gamepad mode |
770 | if (guid.data[15] == k_eSwitchDeviceInfoControllerType_JoyConLeft) { |
771 | SDL_strlcat(mapping_string, "a:b0,b:b1,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b2,y:b3,paddle2:b13,paddle4:b15," , sizeof(mapping_string)); |
772 | } else { |
773 | SDL_strlcat(mapping_string, "a:b0,b:b1,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b2,y:b3,paddle1:b12,paddle3:b14," , sizeof(mapping_string)); |
774 | } |
775 | } |
776 | break; |
777 | } |
778 | } else { |
779 | // All other gamepads have the standard set of 19 buttons and 6 axes |
780 | SDL_strlcat(mapping_string, "a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3," , sizeof(mapping_string)); |
781 | |
782 | if (SDL_IsJoystickXboxSeriesX(vendor, product)) { |
783 | // XBox Series X Controllers have a share button under the guide button |
784 | SDL_strlcat(mapping_string, "misc1:b11," , sizeof(mapping_string)); |
785 | } else if (SDL_IsJoystickXboxOneElite(vendor, product)) { |
786 | // XBox One Elite Controllers have 4 back paddle buttons |
787 | SDL_strlcat(mapping_string, "paddle1:b11,paddle2:b13,paddle3:b12,paddle4:b14," , sizeof(mapping_string)); |
788 | } else if (SDL_IsJoystickSteamController(vendor, product)) { |
789 | // Steam controllers have 2 back paddle buttons |
790 | SDL_strlcat(mapping_string, "paddle1:b12,paddle2:b11," , sizeof(mapping_string)); |
791 | } else if (SDL_IsJoystickNintendoSwitchPro(vendor, product) || |
792 | SDL_IsJoystickNintendoSwitchProInputOnly(vendor, product)) { |
793 | // Nintendo Switch Pro controllers have a screenshot button |
794 | SDL_strlcat(mapping_string, "misc1:b11," , sizeof(mapping_string)); |
795 | } else if (SDL_IsJoystickNintendoSwitchJoyConPair(vendor, product)) { |
796 | // The Nintendo Switch Joy-Con combined controllers has a share button and paddles |
797 | SDL_strlcat(mapping_string, "misc1:b11,paddle1:b12,paddle2:b13,paddle3:b14,paddle4:b15," , sizeof(mapping_string)); |
798 | } else if (SDL_IsJoystickAmazonLunaController(vendor, product)) { |
799 | // Amazon Luna Controller has a mic button under the guide button |
800 | SDL_strlcat(mapping_string, "misc1:b11," , sizeof(mapping_string)); |
801 | } else if (SDL_IsJoystickGoogleStadiaController(vendor, product)) { |
802 | // The Google Stadia controller has a share button and a Google Assistant button |
803 | SDL_strlcat(mapping_string, "misc1:b11,misc2:b12" , sizeof(mapping_string)); |
804 | } else if (SDL_IsJoystickNVIDIASHIELDController(vendor, product)) { |
805 | // The NVIDIA SHIELD controller has a share button between back and start buttons |
806 | SDL_strlcat(mapping_string, "misc1:b11," , sizeof(mapping_string)); |
807 | |
808 | if (product == USB_PRODUCT_NVIDIA_SHIELD_CONTROLLER_V103) { |
809 | // The original SHIELD controller has a touchpad and plus/minus buttons as well |
810 | SDL_strlcat(mapping_string, "touchpad:b12,misc2:b13,misc3:b14" , sizeof(mapping_string)); |
811 | } |
812 | } else if (SDL_IsJoystickHoriSteamController(vendor, product)) { |
813 | /* The Wireless HORIPad for Steam has QAM, Steam, Capsense L/R Sticks, 2 rear buttons, and 2 misc buttons */ |
814 | SDL_strlcat(mapping_string, "paddle1:b13,paddle2:b12,paddle3:b15,paddle4:b14,misc2:b11,misc3:b16,misc4:b17" , sizeof(mapping_string)); |
815 | } else { |
816 | switch (SDL_GetGamepadTypeFromGUID(guid, NULL)) { |
817 | case SDL_GAMEPAD_TYPE_PS4: |
818 | // PS4 controllers have an additional touchpad button |
819 | SDL_strlcat(mapping_string, "touchpad:b11," , sizeof(mapping_string)); |
820 | break; |
821 | case SDL_GAMEPAD_TYPE_PS5: |
822 | // PS5 controllers have a microphone button and an additional touchpad button |
823 | SDL_strlcat(mapping_string, "touchpad:b11,misc1:b12," , sizeof(mapping_string)); |
824 | // DualSense Edge controllers have paddles |
825 | if (SDL_IsJoystickDualSenseEdge(vendor, product)) { |
826 | SDL_strlcat(mapping_string, "paddle1:b16,paddle2:b15,paddle3:b14,paddle4:b13," , sizeof(mapping_string)); |
827 | } |
828 | break; |
829 | default: |
830 | if (vendor == 0 && product == 0) { |
831 | // This is a Bluetooth Nintendo Switch Pro controller |
832 | SDL_strlcat(mapping_string, "misc1:b11," , sizeof(mapping_string)); |
833 | } |
834 | break; |
835 | } |
836 | } |
837 | } |
838 | |
839 | return SDL_PrivateAddMappingForGUID(guid, mapping_string, &existing, SDL_GAMEPAD_MAPPING_PRIORITY_DEFAULT); |
840 | } |
841 | |
842 | /* |
843 | * Helper function to guess at a mapping for RAWINPUT gamepads |
844 | */ |
845 | static GamepadMapping_t *SDL_CreateMappingForRAWINPUTGamepad(SDL_GUID guid) |
846 | { |
847 | bool existing; |
848 | char mapping_string[1024]; |
849 | |
850 | SDL_strlcpy(mapping_string, "none,*," , sizeof(mapping_string)); |
851 | SDL_strlcat(mapping_string, "a:b0,b:b1,x:b2,y:b3,back:b6,guide:b10,start:b7,leftstick:b8,rightstick:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5," , sizeof(mapping_string)); |
852 | |
853 | return SDL_PrivateAddMappingForGUID(guid, mapping_string, &existing, SDL_GAMEPAD_MAPPING_PRIORITY_DEFAULT); |
854 | } |
855 | |
856 | /* |
857 | * Helper function to guess at a mapping for WGI gamepads |
858 | */ |
859 | static GamepadMapping_t *SDL_CreateMappingForWGIGamepad(SDL_GUID guid) |
860 | { |
861 | bool existing; |
862 | char mapping_string[1024]; |
863 | |
864 | if (guid.data[15] != SDL_JOYSTICK_TYPE_GAMEPAD) { |
865 | return NULL; |
866 | } |
867 | |
868 | SDL_strlcpy(mapping_string, "none,*," , sizeof(mapping_string)); |
869 | SDL_strlcat(mapping_string, "a:b0,b:b1,x:b2,y:b3,back:b6,start:b7,leftstick:b8,rightstick:b9,leftshoulder:b4,rightshoulder:b5,dpup:b10,dpdown:b12,dpleft:b13,dpright:b11,leftx:a1,lefty:a0~,rightx:a3,righty:a2~,lefttrigger:a4,righttrigger:a5," , sizeof(mapping_string)); |
870 | |
871 | return SDL_PrivateAddMappingForGUID(guid, mapping_string, &existing, SDL_GAMEPAD_MAPPING_PRIORITY_DEFAULT); |
872 | } |
873 | |
874 | /* |
875 | * Helper function to scan the mappings database for a gamepad with the specified GUID |
876 | */ |
877 | static GamepadMapping_t *SDL_PrivateMatchGamepadMappingForGUID(SDL_GUID guid, bool match_version, bool exact_match_crc) |
878 | { |
879 | GamepadMapping_t *mapping, *best_match = NULL; |
880 | Uint16 crc = 0; |
881 | |
882 | SDL_AssertJoysticksLocked(); |
883 | |
884 | SDL_GetJoystickGUIDInfo(guid, NULL, NULL, NULL, &crc); |
885 | |
886 | // Clear the CRC from the GUID for matching, the mappings never include it in the GUID |
887 | SDL_SetJoystickGUIDCRC(&guid, 0); |
888 | |
889 | if (!match_version) { |
890 | SDL_SetJoystickGUIDVersion(&guid, 0); |
891 | } |
892 | |
893 | for (mapping = s_pSupportedGamepads; mapping; mapping = mapping->next) { |
894 | SDL_GUID mapping_guid; |
895 | |
896 | if (SDL_memcmp(&mapping->guid, &s_zeroGUID, sizeof(mapping->guid)) == 0) { |
897 | continue; |
898 | } |
899 | |
900 | SDL_memcpy(&mapping_guid, &mapping->guid, sizeof(mapping_guid)); |
901 | if (!match_version) { |
902 | SDL_SetJoystickGUIDVersion(&mapping_guid, 0); |
903 | } |
904 | |
905 | if (SDL_memcmp(&guid, &mapping_guid, sizeof(guid)) == 0) { |
906 | const char *crc_string = SDL_strstr(mapping->mapping, SDL_GAMEPAD_CRC_FIELD); |
907 | if (crc_string) { |
908 | Uint16 mapping_crc = (Uint16)SDL_strtol(crc_string + SDL_GAMEPAD_CRC_FIELD_SIZE, NULL, 16); |
909 | if (mapping_crc != crc) { |
910 | // This mapping specified a CRC and they don't match |
911 | continue; |
912 | } |
913 | |
914 | // An exact match, including CRC |
915 | return mapping; |
916 | } else if (crc && exact_match_crc) { |
917 | return NULL; |
918 | } |
919 | |
920 | if (!best_match) { |
921 | best_match = mapping; |
922 | } |
923 | } |
924 | } |
925 | return best_match; |
926 | } |
927 | |
928 | /* |
929 | * Helper function to scan the mappings database for a gamepad with the specified GUID |
930 | */ |
931 | static GamepadMapping_t *SDL_PrivateGetGamepadMappingForGUID(SDL_GUID guid, bool adding_mapping) |
932 | { |
933 | GamepadMapping_t *mapping; |
934 | |
935 | mapping = SDL_PrivateMatchGamepadMappingForGUID(guid, true, adding_mapping); |
936 | if (mapping) { |
937 | return mapping; |
938 | } |
939 | |
940 | if (adding_mapping) { |
941 | // We didn't find an existing mapping |
942 | return NULL; |
943 | } |
944 | |
945 | // Try harder to get the best match, or create a mapping |
946 | |
947 | if (SDL_JoystickGUIDUsesVersion(guid)) { |
948 | // Try again, ignoring the version |
949 | mapping = SDL_PrivateMatchGamepadMappingForGUID(guid, false, false); |
950 | if (mapping) { |
951 | return mapping; |
952 | } |
953 | } |
954 | |
955 | #ifdef SDL_JOYSTICK_XINPUT |
956 | if (SDL_IsJoystickXInput(guid)) { |
957 | // This is an XInput device |
958 | return s_pXInputMapping; |
959 | } |
960 | #endif |
961 | if (SDL_IsJoystickHIDAPI(guid)) { |
962 | mapping = SDL_CreateMappingForHIDAPIGamepad(guid); |
963 | } else if (SDL_IsJoystickRAWINPUT(guid)) { |
964 | mapping = SDL_CreateMappingForRAWINPUTGamepad(guid); |
965 | } else if (SDL_IsJoystickWGI(guid)) { |
966 | mapping = SDL_CreateMappingForWGIGamepad(guid); |
967 | } else if (SDL_IsJoystickVIRTUAL(guid)) { |
968 | // We'll pick up a robust mapping in VIRTUAL_JoystickGetGamepadMapping |
969 | #ifdef SDL_PLATFORM_ANDROID |
970 | } else { |
971 | mapping = SDL_CreateMappingForAndroidGamepad(guid); |
972 | #endif |
973 | } |
974 | return mapping; |
975 | } |
976 | |
977 | static const char *map_StringForGamepadType[] = { |
978 | "unknown" , |
979 | "standard" , |
980 | "xbox360" , |
981 | "xboxone" , |
982 | "ps3" , |
983 | "ps4" , |
984 | "ps5" , |
985 | "switchpro" , |
986 | "joyconleft" , |
987 | "joyconright" , |
988 | "joyconpair" |
989 | }; |
990 | SDL_COMPILE_TIME_ASSERT(map_StringForGamepadType, SDL_arraysize(map_StringForGamepadType) == SDL_GAMEPAD_TYPE_COUNT); |
991 | |
992 | /* |
993 | * convert a string to its enum equivalent |
994 | */ |
995 | SDL_GamepadType SDL_GetGamepadTypeFromString(const char *str) |
996 | { |
997 | int i; |
998 | |
999 | if (!str || str[0] == '\0') { |
1000 | return SDL_GAMEPAD_TYPE_UNKNOWN; |
1001 | } |
1002 | |
1003 | if (*str == '+' || *str == '-') { |
1004 | ++str; |
1005 | } |
1006 | |
1007 | for (i = 0; i < SDL_arraysize(map_StringForGamepadType); ++i) { |
1008 | if (SDL_strcasecmp(str, map_StringForGamepadType[i]) == 0) { |
1009 | return (SDL_GamepadType)i; |
1010 | } |
1011 | } |
1012 | return SDL_GAMEPAD_TYPE_UNKNOWN; |
1013 | } |
1014 | |
1015 | /* |
1016 | * convert an enum to its string equivalent |
1017 | */ |
1018 | const char *SDL_GetGamepadStringForType(SDL_GamepadType type) |
1019 | { |
1020 | if (type >= SDL_GAMEPAD_TYPE_STANDARD && type < SDL_GAMEPAD_TYPE_COUNT) { |
1021 | return map_StringForGamepadType[type]; |
1022 | } |
1023 | return NULL; |
1024 | } |
1025 | |
1026 | static const char *map_StringForGamepadAxis[] = { |
1027 | "leftx" , |
1028 | "lefty" , |
1029 | "rightx" , |
1030 | "righty" , |
1031 | "lefttrigger" , |
1032 | "righttrigger" |
1033 | }; |
1034 | SDL_COMPILE_TIME_ASSERT(map_StringForGamepadAxis, SDL_arraysize(map_StringForGamepadAxis) == SDL_GAMEPAD_AXIS_COUNT); |
1035 | |
1036 | /* |
1037 | * convert a string to its enum equivalent |
1038 | */ |
1039 | SDL_GamepadAxis SDL_GetGamepadAxisFromString(const char *str) |
1040 | { |
1041 | int i; |
1042 | |
1043 | if (!str || str[0] == '\0') { |
1044 | return SDL_GAMEPAD_AXIS_INVALID; |
1045 | } |
1046 | |
1047 | if (*str == '+' || *str == '-') { |
1048 | ++str; |
1049 | } |
1050 | |
1051 | for (i = 0; i < SDL_arraysize(map_StringForGamepadAxis); ++i) { |
1052 | if (SDL_strcasecmp(str, map_StringForGamepadAxis[i]) == 0) { |
1053 | return (SDL_GamepadAxis)i; |
1054 | } |
1055 | } |
1056 | return SDL_GAMEPAD_AXIS_INVALID; |
1057 | } |
1058 | |
1059 | /* |
1060 | * convert an enum to its string equivalent |
1061 | */ |
1062 | const char *SDL_GetGamepadStringForAxis(SDL_GamepadAxis axis) |
1063 | { |
1064 | if (axis > SDL_GAMEPAD_AXIS_INVALID && axis < SDL_GAMEPAD_AXIS_COUNT) { |
1065 | return map_StringForGamepadAxis[axis]; |
1066 | } |
1067 | return NULL; |
1068 | } |
1069 | |
1070 | static const char *map_StringForGamepadButton[] = { |
1071 | "a" , |
1072 | "b" , |
1073 | "x" , |
1074 | "y" , |
1075 | "back" , |
1076 | "guide" , |
1077 | "start" , |
1078 | "leftstick" , |
1079 | "rightstick" , |
1080 | "leftshoulder" , |
1081 | "rightshoulder" , |
1082 | "dpup" , |
1083 | "dpdown" , |
1084 | "dpleft" , |
1085 | "dpright" , |
1086 | "misc1" , |
1087 | "paddle1" , |
1088 | "paddle2" , |
1089 | "paddle3" , |
1090 | "paddle4" , |
1091 | "touchpad" , |
1092 | "misc2" , |
1093 | "misc3" , |
1094 | "misc4" , |
1095 | "misc5" , |
1096 | "misc6" |
1097 | }; |
1098 | SDL_COMPILE_TIME_ASSERT(map_StringForGamepadButton, SDL_arraysize(map_StringForGamepadButton) == SDL_GAMEPAD_BUTTON_COUNT); |
1099 | |
1100 | /* |
1101 | * convert a string to its enum equivalent |
1102 | */ |
1103 | static SDL_GamepadButton SDL_PrivateGetGamepadButtonFromString(const char *str, bool baxy) |
1104 | { |
1105 | int i; |
1106 | |
1107 | if (!str || str[0] == '\0') { |
1108 | return SDL_GAMEPAD_BUTTON_INVALID; |
1109 | } |
1110 | |
1111 | for (i = 0; i < SDL_arraysize(map_StringForGamepadButton); ++i) { |
1112 | if (SDL_strcasecmp(str, map_StringForGamepadButton[i]) == 0) { |
1113 | if (baxy) { |
1114 | // Need to swap face buttons |
1115 | switch (i) { |
1116 | case SDL_GAMEPAD_BUTTON_SOUTH: |
1117 | return SDL_GAMEPAD_BUTTON_EAST; |
1118 | case SDL_GAMEPAD_BUTTON_EAST: |
1119 | return SDL_GAMEPAD_BUTTON_SOUTH; |
1120 | case SDL_GAMEPAD_BUTTON_WEST: |
1121 | return SDL_GAMEPAD_BUTTON_NORTH; |
1122 | case SDL_GAMEPAD_BUTTON_NORTH: |
1123 | return SDL_GAMEPAD_BUTTON_WEST; |
1124 | default: |
1125 | break; |
1126 | } |
1127 | } |
1128 | return (SDL_GamepadButton)i; |
1129 | } |
1130 | } |
1131 | return SDL_GAMEPAD_BUTTON_INVALID; |
1132 | } |
1133 | SDL_GamepadButton SDL_GetGamepadButtonFromString(const char *str) |
1134 | { |
1135 | return SDL_PrivateGetGamepadButtonFromString(str, false); |
1136 | } |
1137 | |
1138 | /* |
1139 | * convert an enum to its string equivalent |
1140 | */ |
1141 | const char *SDL_GetGamepadStringForButton(SDL_GamepadButton button) |
1142 | { |
1143 | if (button > SDL_GAMEPAD_BUTTON_INVALID && button < SDL_GAMEPAD_BUTTON_COUNT) { |
1144 | return map_StringForGamepadButton[button]; |
1145 | } |
1146 | return NULL; |
1147 | } |
1148 | |
1149 | /* |
1150 | * given a gamepad button name and a joystick name update our mapping structure with it |
1151 | */ |
1152 | static bool SDL_PrivateParseGamepadElement(SDL_Gamepad *gamepad, const char *szGameButton, const char *szJoystickButton) |
1153 | { |
1154 | SDL_GamepadBinding bind; |
1155 | SDL_GamepadButton button; |
1156 | SDL_GamepadAxis axis; |
1157 | bool invert_input = false; |
1158 | char half_axis_input = 0; |
1159 | char half_axis_output = 0; |
1160 | int i; |
1161 | SDL_GamepadBinding *new_bindings; |
1162 | bool baxy_mapping = false; |
1163 | |
1164 | SDL_AssertJoysticksLocked(); |
1165 | |
1166 | SDL_zero(bind); |
1167 | |
1168 | if (*szGameButton == '+' || *szGameButton == '-') { |
1169 | half_axis_output = *szGameButton++; |
1170 | } |
1171 | |
1172 | if (SDL_strstr(gamepad->mapping->mapping, ",hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1" ) != NULL) { |
1173 | baxy_mapping = true; |
1174 | } |
1175 | |
1176 | axis = SDL_GetGamepadAxisFromString(szGameButton); |
1177 | button = SDL_PrivateGetGamepadButtonFromString(szGameButton, baxy_mapping); |
1178 | if (axis != SDL_GAMEPAD_AXIS_INVALID) { |
1179 | bind.output_type = SDL_GAMEPAD_BINDTYPE_AXIS; |
1180 | bind.output.axis.axis = axis; |
1181 | if (axis == SDL_GAMEPAD_AXIS_LEFT_TRIGGER || axis == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) { |
1182 | bind.output.axis.axis_min = 0; |
1183 | bind.output.axis.axis_max = SDL_JOYSTICK_AXIS_MAX; |
1184 | } else { |
1185 | if (half_axis_output == '+') { |
1186 | bind.output.axis.axis_min = 0; |
1187 | bind.output.axis.axis_max = SDL_JOYSTICK_AXIS_MAX; |
1188 | } else if (half_axis_output == '-') { |
1189 | bind.output.axis.axis_min = 0; |
1190 | bind.output.axis.axis_max = SDL_JOYSTICK_AXIS_MIN; |
1191 | } else { |
1192 | bind.output.axis.axis_min = SDL_JOYSTICK_AXIS_MIN; |
1193 | bind.output.axis.axis_max = SDL_JOYSTICK_AXIS_MAX; |
1194 | } |
1195 | } |
1196 | } else if (button != SDL_GAMEPAD_BUTTON_INVALID) { |
1197 | bind.output_type = SDL_GAMEPAD_BINDTYPE_BUTTON; |
1198 | bind.output.button = button; |
1199 | } else { |
1200 | return false; |
1201 | } |
1202 | |
1203 | if (*szJoystickButton == '+' || *szJoystickButton == '-') { |
1204 | half_axis_input = *szJoystickButton++; |
1205 | } |
1206 | if (szJoystickButton[SDL_strlen(szJoystickButton) - 1] == '~') { |
1207 | invert_input = true; |
1208 | } |
1209 | |
1210 | if (szJoystickButton[0] == 'a' && SDL_isdigit((unsigned char)szJoystickButton[1])) { |
1211 | bind.input_type = SDL_GAMEPAD_BINDTYPE_AXIS; |
1212 | bind.input.axis.axis = SDL_atoi(&szJoystickButton[1]); |
1213 | if (half_axis_input == '+') { |
1214 | bind.input.axis.axis_min = 0; |
1215 | bind.input.axis.axis_max = SDL_JOYSTICK_AXIS_MAX; |
1216 | } else if (half_axis_input == '-') { |
1217 | bind.input.axis.axis_min = 0; |
1218 | bind.input.axis.axis_max = SDL_JOYSTICK_AXIS_MIN; |
1219 | } else { |
1220 | bind.input.axis.axis_min = SDL_JOYSTICK_AXIS_MIN; |
1221 | bind.input.axis.axis_max = SDL_JOYSTICK_AXIS_MAX; |
1222 | } |
1223 | if (invert_input) { |
1224 | int tmp = bind.input.axis.axis_min; |
1225 | bind.input.axis.axis_min = bind.input.axis.axis_max; |
1226 | bind.input.axis.axis_max = tmp; |
1227 | } |
1228 | } else if (szJoystickButton[0] == 'b' && SDL_isdigit((unsigned char)szJoystickButton[1])) { |
1229 | bind.input_type = SDL_GAMEPAD_BINDTYPE_BUTTON; |
1230 | bind.input.button = SDL_atoi(&szJoystickButton[1]); |
1231 | } else if (szJoystickButton[0] == 'h' && SDL_isdigit((unsigned char)szJoystickButton[1]) && |
1232 | szJoystickButton[2] == '.' && SDL_isdigit((unsigned char)szJoystickButton[3])) { |
1233 | int hat = SDL_atoi(&szJoystickButton[1]); |
1234 | int mask = SDL_atoi(&szJoystickButton[3]); |
1235 | bind.input_type = SDL_GAMEPAD_BINDTYPE_HAT; |
1236 | bind.input.hat.hat = hat; |
1237 | bind.input.hat.hat_mask = mask; |
1238 | } else { |
1239 | return false; |
1240 | } |
1241 | |
1242 | for (i = 0; i < gamepad->num_bindings; ++i) { |
1243 | if (SDL_memcmp(&gamepad->bindings[i], &bind, sizeof(bind)) == 0) { |
1244 | // We already have this binding, could be different face button names? |
1245 | return true; |
1246 | } |
1247 | } |
1248 | |
1249 | ++gamepad->num_bindings; |
1250 | new_bindings = (SDL_GamepadBinding *)SDL_realloc(gamepad->bindings, gamepad->num_bindings * sizeof(*gamepad->bindings)); |
1251 | if (!new_bindings) { |
1252 | SDL_free(gamepad->bindings); |
1253 | gamepad->num_bindings = 0; |
1254 | gamepad->bindings = NULL; |
1255 | return false; |
1256 | } |
1257 | gamepad->bindings = new_bindings; |
1258 | gamepad->bindings[gamepad->num_bindings - 1] = bind; |
1259 | return true; |
1260 | } |
1261 | |
1262 | /* |
1263 | * given a gamepad mapping string update our mapping object |
1264 | */ |
1265 | static bool SDL_PrivateParseGamepadConfigString(SDL_Gamepad *gamepad, const char *pchString) |
1266 | { |
1267 | char szGameButton[20]; |
1268 | char szJoystickButton[20]; |
1269 | bool bGameButton = true; |
1270 | int i = 0; |
1271 | const char *pchPos = pchString; |
1272 | |
1273 | SDL_zeroa(szGameButton); |
1274 | SDL_zeroa(szJoystickButton); |
1275 | |
1276 | while (pchPos && *pchPos) { |
1277 | if (*pchPos == ':') { |
1278 | i = 0; |
1279 | bGameButton = false; |
1280 | } else if (*pchPos == ' ') { |
1281 | |
1282 | } else if (*pchPos == ',') { |
1283 | i = 0; |
1284 | bGameButton = true; |
1285 | SDL_PrivateParseGamepadElement(gamepad, szGameButton, szJoystickButton); |
1286 | SDL_zeroa(szGameButton); |
1287 | SDL_zeroa(szJoystickButton); |
1288 | |
1289 | } else if (bGameButton) { |
1290 | if (i >= sizeof(szGameButton)) { |
1291 | szGameButton[sizeof(szGameButton) - 1] = '\0'; |
1292 | return SDL_SetError("Button name too large: %s" , szGameButton); |
1293 | } |
1294 | szGameButton[i] = *pchPos; |
1295 | i++; |
1296 | } else { |
1297 | if (i >= sizeof(szJoystickButton)) { |
1298 | szJoystickButton[sizeof(szJoystickButton) - 1] = '\0'; |
1299 | return SDL_SetError("Joystick button name too large: %s" , szJoystickButton); |
1300 | } |
1301 | szJoystickButton[i] = *pchPos; |
1302 | i++; |
1303 | } |
1304 | pchPos++; |
1305 | } |
1306 | |
1307 | // No more values if the string was terminated by a comma. Don't report an error. |
1308 | if (szGameButton[0] != '\0' || szJoystickButton[0] != '\0') { |
1309 | SDL_PrivateParseGamepadElement(gamepad, szGameButton, szJoystickButton); |
1310 | } |
1311 | return true; |
1312 | } |
1313 | |
1314 | static void SDL_UpdateGamepadType(SDL_Gamepad *gamepad) |
1315 | { |
1316 | char *type_string, *comma; |
1317 | |
1318 | SDL_AssertJoysticksLocked(); |
1319 | |
1320 | gamepad->type = SDL_GAMEPAD_TYPE_UNKNOWN; |
1321 | |
1322 | type_string = SDL_strstr(gamepad->mapping->mapping, SDL_GAMEPAD_TYPE_FIELD); |
1323 | if (type_string) { |
1324 | type_string += SDL_GAMEPAD_TYPE_FIELD_SIZE; |
1325 | comma = SDL_strchr(type_string, ','); |
1326 | if (comma) { |
1327 | *comma = '\0'; |
1328 | gamepad->type = SDL_GetGamepadTypeFromString(type_string); |
1329 | *comma = ','; |
1330 | } else { |
1331 | gamepad->type = SDL_GetGamepadTypeFromString(type_string); |
1332 | } |
1333 | } |
1334 | if (gamepad->type == SDL_GAMEPAD_TYPE_UNKNOWN) { |
1335 | gamepad->type = SDL_GetRealGamepadTypeForID(gamepad->joystick->instance_id); |
1336 | } |
1337 | } |
1338 | |
1339 | static SDL_GamepadFaceStyle SDL_GetGamepadFaceStyleFromString(const char *string) |
1340 | { |
1341 | if (SDL_strcmp(string, "abxy" ) == 0) { |
1342 | return SDL_GAMEPAD_FACE_STYLE_ABXY; |
1343 | } else if (SDL_strcmp(string, "bayx" ) == 0) { |
1344 | return SDL_GAMEPAD_FACE_STYLE_BAYX; |
1345 | } else if (SDL_strcmp(string, "sony" ) == 0) { |
1346 | return SDL_GAMEPAD_FACE_STYLE_SONY; |
1347 | } else { |
1348 | return SDL_GAMEPAD_FACE_STYLE_UNKNOWN; |
1349 | } |
1350 | } |
1351 | |
1352 | static SDL_GamepadFaceStyle SDL_GetGamepadFaceStyleForGamepadType(SDL_GamepadType type) |
1353 | { |
1354 | switch (type) { |
1355 | case SDL_GAMEPAD_TYPE_PS3: |
1356 | case SDL_GAMEPAD_TYPE_PS4: |
1357 | case SDL_GAMEPAD_TYPE_PS5: |
1358 | return SDL_GAMEPAD_FACE_STYLE_SONY; |
1359 | case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO: |
1360 | case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT: |
1361 | case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT: |
1362 | case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_PAIR: |
1363 | return SDL_GAMEPAD_FACE_STYLE_BAYX; |
1364 | default: |
1365 | return SDL_GAMEPAD_FACE_STYLE_ABXY; |
1366 | } |
1367 | } |
1368 | |
1369 | static void SDL_UpdateGamepadFaceStyle(SDL_Gamepad *gamepad) |
1370 | { |
1371 | char *face_string, *comma; |
1372 | |
1373 | SDL_AssertJoysticksLocked(); |
1374 | |
1375 | gamepad->face_style = SDL_GAMEPAD_FACE_STYLE_UNKNOWN; |
1376 | |
1377 | face_string = SDL_strstr(gamepad->mapping->mapping, SDL_GAMEPAD_FACE_FIELD); |
1378 | if (face_string) { |
1379 | face_string += SDL_GAMEPAD_TYPE_FIELD_SIZE; |
1380 | comma = SDL_strchr(face_string, ','); |
1381 | if (comma) { |
1382 | *comma = '\0'; |
1383 | gamepad->face_style = SDL_GetGamepadFaceStyleFromString(face_string); |
1384 | *comma = ','; |
1385 | } else { |
1386 | gamepad->face_style = SDL_GetGamepadFaceStyleFromString(face_string); |
1387 | } |
1388 | } |
1389 | |
1390 | if (gamepad->face_style == SDL_GAMEPAD_FACE_STYLE_UNKNOWN && |
1391 | SDL_strstr(gamepad->mapping->mapping, "SDL_GAMECONTROLLER_USE_BUTTON_LABELS" ) != NULL) { |
1392 | // This controller uses Nintendo button style |
1393 | gamepad->face_style = SDL_GAMEPAD_FACE_STYLE_BAYX; |
1394 | } |
1395 | if (gamepad->face_style == SDL_GAMEPAD_FACE_STYLE_UNKNOWN) { |
1396 | gamepad->face_style = SDL_GetGamepadFaceStyleForGamepadType(gamepad->type); |
1397 | } |
1398 | } |
1399 | |
1400 | static void SDL_FixupHIDAPIMapping(SDL_Gamepad *gamepad) |
1401 | { |
1402 | // Check to see if we need fixup |
1403 | bool need_fixup = false; |
1404 | for (int i = 0; i < gamepad->num_bindings; ++i) { |
1405 | SDL_GamepadBinding *binding = &gamepad->bindings[i]; |
1406 | if (binding->output_type == SDL_GAMEPAD_BINDTYPE_BUTTON && |
1407 | binding->output.button >= SDL_GAMEPAD_BUTTON_DPAD_UP) { |
1408 | if (binding->input_type == SDL_GAMEPAD_BINDTYPE_BUTTON && |
1409 | binding->input.button == binding->output.button) { |
1410 | // Old style binding |
1411 | need_fixup = true; |
1412 | } |
1413 | break; |
1414 | } |
1415 | } |
1416 | if (!need_fixup) { |
1417 | return; |
1418 | } |
1419 | |
1420 | for (int i = 0; i < gamepad->num_bindings; ++i) { |
1421 | SDL_GamepadBinding *binding = &gamepad->bindings[i]; |
1422 | if (binding->input_type == SDL_GAMEPAD_BINDTYPE_BUTTON && |
1423 | binding->output_type == SDL_GAMEPAD_BINDTYPE_BUTTON) { |
1424 | switch (binding->output.button) { |
1425 | case SDL_GAMEPAD_BUTTON_DPAD_UP: |
1426 | binding->input_type = SDL_GAMEPAD_BINDTYPE_HAT; |
1427 | binding->input.hat.hat = 0; |
1428 | binding->input.hat.hat_mask = SDL_HAT_UP; |
1429 | break; |
1430 | case SDL_GAMEPAD_BUTTON_DPAD_DOWN: |
1431 | binding->input_type = SDL_GAMEPAD_BINDTYPE_HAT; |
1432 | binding->input.hat.hat = 0; |
1433 | binding->input.hat.hat_mask = SDL_HAT_DOWN; |
1434 | break; |
1435 | case SDL_GAMEPAD_BUTTON_DPAD_LEFT: |
1436 | binding->input_type = SDL_GAMEPAD_BINDTYPE_HAT; |
1437 | binding->input.hat.hat = 0; |
1438 | binding->input.hat.hat_mask = SDL_HAT_LEFT; |
1439 | break; |
1440 | case SDL_GAMEPAD_BUTTON_DPAD_RIGHT: |
1441 | binding->input_type = SDL_GAMEPAD_BINDTYPE_HAT; |
1442 | binding->input.hat.hat = 0; |
1443 | binding->input.hat.hat_mask = SDL_HAT_RIGHT; |
1444 | break; |
1445 | default: |
1446 | if (binding->output.button > SDL_GAMEPAD_BUTTON_DPAD_RIGHT) { |
1447 | binding->input.button -= 4; |
1448 | } |
1449 | break; |
1450 | } |
1451 | } |
1452 | } |
1453 | } |
1454 | |
1455 | /* |
1456 | * Make a new button mapping struct |
1457 | */ |
1458 | static void SDL_PrivateLoadButtonMapping(SDL_Gamepad *gamepad, GamepadMapping_t *pGamepadMapping) |
1459 | { |
1460 | int i; |
1461 | |
1462 | SDL_AssertJoysticksLocked(); |
1463 | |
1464 | gamepad->name = pGamepadMapping->name; |
1465 | gamepad->num_bindings = 0; |
1466 | gamepad->mapping = pGamepadMapping; |
1467 | if (gamepad->joystick->naxes != 0 && gamepad->last_match_axis) { |
1468 | SDL_memset(gamepad->last_match_axis, 0, gamepad->joystick->naxes * sizeof(*gamepad->last_match_axis)); |
1469 | } |
1470 | |
1471 | SDL_UpdateGamepadType(gamepad); |
1472 | SDL_UpdateGamepadFaceStyle(gamepad); |
1473 | |
1474 | SDL_PrivateParseGamepadConfigString(gamepad, pGamepadMapping->mapping); |
1475 | |
1476 | if (SDL_IsJoystickHIDAPI(pGamepadMapping->guid)) { |
1477 | SDL_FixupHIDAPIMapping(gamepad); |
1478 | } |
1479 | |
1480 | // Set the zero point for triggers |
1481 | for (i = 0; i < gamepad->num_bindings; ++i) { |
1482 | SDL_GamepadBinding *binding = &gamepad->bindings[i]; |
1483 | if (binding->input_type == SDL_GAMEPAD_BINDTYPE_AXIS && |
1484 | binding->output_type == SDL_GAMEPAD_BINDTYPE_AXIS && |
1485 | (binding->output.axis.axis == SDL_GAMEPAD_AXIS_LEFT_TRIGGER || |
1486 | binding->output.axis.axis == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER)) { |
1487 | if (binding->input.axis.axis < gamepad->joystick->naxes) { |
1488 | gamepad->joystick->axes[binding->input.axis.axis].value = |
1489 | gamepad->joystick->axes[binding->input.axis.axis].zero = (Sint16)binding->input.axis.axis_min; |
1490 | } |
1491 | } |
1492 | } |
1493 | } |
1494 | |
1495 | /* |
1496 | * grab the guid string from a mapping string |
1497 | */ |
1498 | static char *SDL_PrivateGetGamepadGUIDFromMappingString(const char *pMapping) |
1499 | { |
1500 | const char *pFirstComma = SDL_strchr(pMapping, ','); |
1501 | if (pFirstComma) { |
1502 | char *pchGUID = (char *)SDL_malloc(pFirstComma - pMapping + 1); |
1503 | if (!pchGUID) { |
1504 | return NULL; |
1505 | } |
1506 | SDL_memcpy(pchGUID, pMapping, pFirstComma - pMapping); |
1507 | pchGUID[pFirstComma - pMapping] = '\0'; |
1508 | |
1509 | // Convert old style GUIDs to the new style in 2.0.5 |
1510 | #if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK) |
1511 | if (SDL_strlen(pchGUID) == 32 && |
1512 | SDL_memcmp(&pchGUID[20], "504944564944" , 12) == 0) { |
1513 | SDL_memcpy(&pchGUID[20], "000000000000" , 12); |
1514 | SDL_memcpy(&pchGUID[16], &pchGUID[4], 4); |
1515 | SDL_memcpy(&pchGUID[8], &pchGUID[0], 4); |
1516 | SDL_memcpy(&pchGUID[0], "03000000" , 8); |
1517 | } |
1518 | #elif defined(SDL_PLATFORM_MACOS) |
1519 | if (SDL_strlen(pchGUID) == 32 && |
1520 | SDL_memcmp(&pchGUID[4], "000000000000" , 12) == 0 && |
1521 | SDL_memcmp(&pchGUID[20], "000000000000" , 12) == 0) { |
1522 | SDL_memcpy(&pchGUID[20], "000000000000" , 12); |
1523 | SDL_memcpy(&pchGUID[8], &pchGUID[0], 4); |
1524 | SDL_memcpy(&pchGUID[0], "03000000" , 8); |
1525 | } |
1526 | #endif |
1527 | return pchGUID; |
1528 | } |
1529 | return NULL; |
1530 | } |
1531 | |
1532 | /* |
1533 | * grab the name string from a mapping string |
1534 | */ |
1535 | static char *SDL_PrivateGetGamepadNameFromMappingString(const char *pMapping) |
1536 | { |
1537 | const char *pFirstComma, *pSecondComma; |
1538 | char *pchName; |
1539 | |
1540 | pFirstComma = SDL_strchr(pMapping, ','); |
1541 | if (!pFirstComma) { |
1542 | return NULL; |
1543 | } |
1544 | |
1545 | pSecondComma = SDL_strchr(pFirstComma + 1, ','); |
1546 | if (!pSecondComma) { |
1547 | return NULL; |
1548 | } |
1549 | |
1550 | pchName = (char *)SDL_malloc(pSecondComma - pFirstComma); |
1551 | if (!pchName) { |
1552 | return NULL; |
1553 | } |
1554 | SDL_memcpy(pchName, pFirstComma + 1, pSecondComma - pFirstComma); |
1555 | pchName[pSecondComma - pFirstComma - 1] = 0; |
1556 | return pchName; |
1557 | } |
1558 | |
1559 | /* |
1560 | * grab the button mapping string from a mapping string |
1561 | */ |
1562 | static char *SDL_PrivateGetGamepadMappingFromMappingString(const char *pMapping) |
1563 | { |
1564 | const char *pFirstComma, *pSecondComma; |
1565 | char *result; |
1566 | size_t length; |
1567 | |
1568 | pFirstComma = SDL_strchr(pMapping, ','); |
1569 | if (!pFirstComma) { |
1570 | return NULL; |
1571 | } |
1572 | |
1573 | pSecondComma = SDL_strchr(pFirstComma + 1, ','); |
1574 | if (!pSecondComma) { |
1575 | return NULL; |
1576 | } |
1577 | |
1578 | // Skip whitespace |
1579 | while (SDL_isspace(pSecondComma[1])) { |
1580 | ++pSecondComma; |
1581 | } |
1582 | |
1583 | result = SDL_strdup(pSecondComma + 1); // mapping is everything after the 3rd comma |
1584 | |
1585 | // Trim whitespace |
1586 | length = SDL_strlen(result); |
1587 | while (length > 0 && SDL_isspace(result[length - 1])) { |
1588 | --length; |
1589 | } |
1590 | result[length] = '\0'; |
1591 | |
1592 | return result; |
1593 | } |
1594 | |
1595 | /* |
1596 | * Helper function to add a mapping for a guid |
1597 | */ |
1598 | static GamepadMapping_t *SDL_PrivateAddMappingForGUID(SDL_GUID jGUID, const char *mappingString, bool *existing, SDL_GamepadMappingPriority priority) |
1599 | { |
1600 | char *pchName; |
1601 | char *pchMapping; |
1602 | GamepadMapping_t *pGamepadMapping; |
1603 | Uint16 crc; |
1604 | |
1605 | SDL_AssertJoysticksLocked(); |
1606 | |
1607 | pchName = SDL_PrivateGetGamepadNameFromMappingString(mappingString); |
1608 | if (!pchName) { |
1609 | SDL_SetError("Couldn't parse name from %s" , mappingString); |
1610 | return NULL; |
1611 | } |
1612 | |
1613 | pchMapping = SDL_PrivateGetGamepadMappingFromMappingString(mappingString); |
1614 | if (!pchMapping) { |
1615 | SDL_free(pchName); |
1616 | SDL_SetError("Couldn't parse %s" , mappingString); |
1617 | return NULL; |
1618 | } |
1619 | |
1620 | // Fix up the GUID and the mapping with the CRC, if needed |
1621 | SDL_GetJoystickGUIDInfo(jGUID, NULL, NULL, NULL, &crc); |
1622 | if (crc) { |
1623 | // Make sure the mapping has the CRC |
1624 | char *new_mapping; |
1625 | const char *optional_comma; |
1626 | size_t mapping_length; |
1627 | char *crc_end = "" ; |
1628 | char *crc_string = SDL_strstr(pchMapping, SDL_GAMEPAD_CRC_FIELD); |
1629 | if (crc_string) { |
1630 | crc_end = SDL_strchr(crc_string, ','); |
1631 | if (crc_end) { |
1632 | ++crc_end; |
1633 | } else { |
1634 | crc_end = "" ; |
1635 | } |
1636 | *crc_string = '\0'; |
1637 | } |
1638 | |
1639 | // Make sure there's a comma before the CRC |
1640 | mapping_length = SDL_strlen(pchMapping); |
1641 | if (mapping_length == 0 || pchMapping[mapping_length - 1] == ',') { |
1642 | optional_comma = "" ; |
1643 | } else { |
1644 | optional_comma = "," ; |
1645 | } |
1646 | |
1647 | if (SDL_asprintf(&new_mapping, "%s%s%s%.4x,%s" , pchMapping, optional_comma, SDL_GAMEPAD_CRC_FIELD, crc, crc_end) >= 0) { |
1648 | SDL_free(pchMapping); |
1649 | pchMapping = new_mapping; |
1650 | } |
1651 | } else { |
1652 | // Make sure the GUID has the CRC, for matching purposes |
1653 | char *crc_string = SDL_strstr(pchMapping, SDL_GAMEPAD_CRC_FIELD); |
1654 | if (crc_string) { |
1655 | crc = (Uint16)SDL_strtol(crc_string + SDL_GAMEPAD_CRC_FIELD_SIZE, NULL, 16); |
1656 | if (crc) { |
1657 | SDL_SetJoystickGUIDCRC(&jGUID, crc); |
1658 | } |
1659 | } |
1660 | } |
1661 | |
1662 | PushMappingChangeTracking(); |
1663 | |
1664 | pGamepadMapping = SDL_PrivateGetGamepadMappingForGUID(jGUID, true); |
1665 | if (pGamepadMapping) { |
1666 | // Only overwrite the mapping if the priority is the same or higher. |
1667 | if (pGamepadMapping->priority <= priority) { |
1668 | // Update existing mapping |
1669 | SDL_free(pGamepadMapping->name); |
1670 | pGamepadMapping->name = pchName; |
1671 | SDL_free(pGamepadMapping->mapping); |
1672 | pGamepadMapping->mapping = pchMapping; |
1673 | pGamepadMapping->priority = priority; |
1674 | } else { |
1675 | SDL_free(pchName); |
1676 | SDL_free(pchMapping); |
1677 | } |
1678 | if (existing) { |
1679 | *existing = true; |
1680 | } |
1681 | AddMappingChangeTracking(pGamepadMapping); |
1682 | } else { |
1683 | pGamepadMapping = (GamepadMapping_t *)SDL_malloc(sizeof(*pGamepadMapping)); |
1684 | if (!pGamepadMapping) { |
1685 | PopMappingChangeTracking(); |
1686 | SDL_free(pchName); |
1687 | SDL_free(pchMapping); |
1688 | return NULL; |
1689 | } |
1690 | // Clear the CRC, we've already added it to the mapping |
1691 | if (crc) { |
1692 | SDL_SetJoystickGUIDCRC(&jGUID, 0); |
1693 | } |
1694 | pGamepadMapping->guid = jGUID; |
1695 | pGamepadMapping->name = pchName; |
1696 | pGamepadMapping->mapping = pchMapping; |
1697 | pGamepadMapping->next = NULL; |
1698 | pGamepadMapping->priority = priority; |
1699 | |
1700 | if (s_pSupportedGamepads) { |
1701 | // Add the mapping to the end of the list |
1702 | GamepadMapping_t *pCurrMapping, *pPrevMapping; |
1703 | |
1704 | for (pPrevMapping = s_pSupportedGamepads, pCurrMapping = pPrevMapping->next; |
1705 | pCurrMapping; |
1706 | pPrevMapping = pCurrMapping, pCurrMapping = pCurrMapping->next) { |
1707 | // continue; |
1708 | } |
1709 | pPrevMapping->next = pGamepadMapping; |
1710 | } else { |
1711 | s_pSupportedGamepads = pGamepadMapping; |
1712 | } |
1713 | if (existing) { |
1714 | *existing = false; |
1715 | } |
1716 | } |
1717 | |
1718 | PopMappingChangeTracking(); |
1719 | |
1720 | return pGamepadMapping; |
1721 | } |
1722 | |
1723 | /* |
1724 | * Helper function to determine pre-calculated offset to certain joystick mappings |
1725 | */ |
1726 | static GamepadMapping_t *SDL_PrivateGetGamepadMappingForNameAndGUID(const char *name, SDL_GUID guid) |
1727 | { |
1728 | GamepadMapping_t *mapping; |
1729 | |
1730 | SDL_AssertJoysticksLocked(); |
1731 | |
1732 | mapping = SDL_PrivateGetGamepadMappingForGUID(guid, false); |
1733 | #ifdef SDL_PLATFORM_LINUX |
1734 | if (!mapping && name) { |
1735 | if (SDL_strstr(name, "Xbox 360 Wireless Receiver" )) { |
1736 | // The Linux driver xpad.c maps the wireless dpad to buttons |
1737 | bool existing; |
1738 | mapping = SDL_PrivateAddMappingForGUID(guid, |
1739 | "none,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3," , |
1740 | &existing, SDL_GAMEPAD_MAPPING_PRIORITY_DEFAULT); |
1741 | } |
1742 | } |
1743 | #endif // SDL_PLATFORM_LINUX |
1744 | |
1745 | return mapping; |
1746 | } |
1747 | |
1748 | static void SDL_PrivateAppendToMappingString(char *mapping_string, |
1749 | size_t mapping_string_len, |
1750 | const char *input_name, |
1751 | SDL_InputMapping *mapping) |
1752 | { |
1753 | char buffer[16]; |
1754 | if (mapping->kind == EMappingKind_None) { |
1755 | return; |
1756 | } |
1757 | |
1758 | SDL_strlcat(mapping_string, input_name, mapping_string_len); |
1759 | SDL_strlcat(mapping_string, ":" , mapping_string_len); |
1760 | switch (mapping->kind) { |
1761 | case EMappingKind_Button: |
1762 | (void)SDL_snprintf(buffer, sizeof(buffer), "b%u" , mapping->target); |
1763 | break; |
1764 | case EMappingKind_Axis: |
1765 | (void)SDL_snprintf(buffer, sizeof(buffer), "%sa%u%s" , |
1766 | mapping->half_axis_positive ? "+" : |
1767 | mapping->half_axis_negative ? "-" : "" , |
1768 | mapping->target, |
1769 | mapping->axis_reversed ? "~" : "" ); |
1770 | break; |
1771 | case EMappingKind_Hat: |
1772 | (void)SDL_snprintf(buffer, sizeof(buffer), "h%i.%i" , mapping->target >> 4, mapping->target & 0x0F); |
1773 | break; |
1774 | default: |
1775 | SDL_assert(false); |
1776 | } |
1777 | |
1778 | SDL_strlcat(mapping_string, buffer, mapping_string_len); |
1779 | SDL_strlcat(mapping_string, "," , mapping_string_len); |
1780 | } |
1781 | |
1782 | static GamepadMapping_t *SDL_PrivateGenerateAutomaticGamepadMapping(const char *name, |
1783 | SDL_GUID guid, |
1784 | SDL_GamepadMapping *raw_map) |
1785 | { |
1786 | bool existing; |
1787 | char name_string[128]; |
1788 | char mapping[1024]; |
1789 | |
1790 | // Remove any commas in the name |
1791 | SDL_strlcpy(name_string, name, sizeof(name_string)); |
1792 | { |
1793 | char *spot; |
1794 | for (spot = name_string; *spot; ++spot) { |
1795 | if (*spot == ',') { |
1796 | *spot = ' '; |
1797 | } |
1798 | } |
1799 | } |
1800 | (void)SDL_snprintf(mapping, sizeof(mapping), "none,%s," , name_string); |
1801 | SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "a" , &raw_map->a); |
1802 | SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "b" , &raw_map->b); |
1803 | SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "x" , &raw_map->x); |
1804 | SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "y" , &raw_map->y); |
1805 | SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "back" , &raw_map->back); |
1806 | SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "guide" , &raw_map->guide); |
1807 | SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "start" , &raw_map->start); |
1808 | SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "leftstick" , &raw_map->leftstick); |
1809 | SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "rightstick" , &raw_map->rightstick); |
1810 | SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "leftshoulder" , &raw_map->leftshoulder); |
1811 | SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "rightshoulder" , &raw_map->rightshoulder); |
1812 | SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "dpup" , &raw_map->dpup); |
1813 | SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "dpdown" , &raw_map->dpdown); |
1814 | SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "dpleft" , &raw_map->dpleft); |
1815 | SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "dpright" , &raw_map->dpright); |
1816 | SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "misc1" , &raw_map->misc1); |
1817 | SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "misc2" , &raw_map->misc2); |
1818 | SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "misc3" , &raw_map->misc3); |
1819 | SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "misc4" , &raw_map->misc4); |
1820 | SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "misc5" , &raw_map->misc5); |
1821 | SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "misc6" , &raw_map->misc6); |
1822 | /* Keep using paddle1-4 in the generated mapping so that it can be |
1823 | * reused with SDL2 */ |
1824 | SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "paddle1" , &raw_map->right_paddle1); |
1825 | SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "paddle2" , &raw_map->left_paddle1); |
1826 | SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "paddle3" , &raw_map->right_paddle2); |
1827 | SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "paddle4" , &raw_map->left_paddle2); |
1828 | SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "leftx" , &raw_map->leftx); |
1829 | SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "lefty" , &raw_map->lefty); |
1830 | SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "rightx" , &raw_map->rightx); |
1831 | SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "righty" , &raw_map->righty); |
1832 | SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "lefttrigger" , &raw_map->lefttrigger); |
1833 | SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "righttrigger" , &raw_map->righttrigger); |
1834 | SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "touchpad" , &raw_map->touchpad); |
1835 | |
1836 | return SDL_PrivateAddMappingForGUID(guid, mapping, &existing, SDL_GAMEPAD_MAPPING_PRIORITY_DEFAULT); |
1837 | } |
1838 | |
1839 | static GamepadMapping_t *SDL_PrivateGetGamepadMapping(SDL_JoystickID instance_id, bool create_mapping) |
1840 | { |
1841 | const char *name; |
1842 | SDL_GUID guid; |
1843 | GamepadMapping_t *mapping; |
1844 | |
1845 | SDL_AssertJoysticksLocked(); |
1846 | |
1847 | name = SDL_GetJoystickNameForID(instance_id); |
1848 | guid = SDL_GetJoystickGUIDForID(instance_id); |
1849 | mapping = SDL_PrivateGetGamepadMappingForNameAndGUID(name, guid); |
1850 | if (!mapping && create_mapping) { |
1851 | SDL_GamepadMapping raw_map; |
1852 | |
1853 | SDL_zero(raw_map); |
1854 | if (SDL_PrivateJoystickGetAutoGamepadMapping(instance_id, &raw_map)) { |
1855 | mapping = SDL_PrivateGenerateAutomaticGamepadMapping(name, guid, &raw_map); |
1856 | } |
1857 | } |
1858 | |
1859 | if (!mapping) { |
1860 | mapping = s_pDefaultMapping; |
1861 | } |
1862 | return mapping; |
1863 | } |
1864 | |
1865 | /* |
1866 | * Add or update an entry into the Mappings Database |
1867 | */ |
1868 | int SDL_AddGamepadMappingsFromIO(SDL_IOStream *src, bool closeio) |
1869 | { |
1870 | const char *platform = SDL_GetPlatform(); |
1871 | int gamepads = 0; |
1872 | char *buf, *line, *line_end, *tmp, *comma, line_platform[64]; |
1873 | size_t db_size; |
1874 | size_t platform_len; |
1875 | |
1876 | buf = (char *)SDL_LoadFile_IO(src, &db_size, closeio); |
1877 | if (!buf) { |
1878 | SDL_SetError("Could not allocate space to read DB into memory" ); |
1879 | return -1; |
1880 | } |
1881 | line = buf; |
1882 | |
1883 | SDL_LockJoysticks(); |
1884 | |
1885 | PushMappingChangeTracking(); |
1886 | |
1887 | while (line < buf + db_size) { |
1888 | line_end = SDL_strchr(line, '\n'); |
1889 | if (line_end) { |
1890 | *line_end = '\0'; |
1891 | } else { |
1892 | line_end = buf + db_size; |
1893 | } |
1894 | |
1895 | // Extract and verify the platform |
1896 | tmp = SDL_strstr(line, SDL_GAMEPAD_PLATFORM_FIELD); |
1897 | if (tmp) { |
1898 | tmp += SDL_GAMEPAD_PLATFORM_FIELD_SIZE; |
1899 | comma = SDL_strchr(tmp, ','); |
1900 | if (comma) { |
1901 | platform_len = comma - tmp + 1; |
1902 | if (platform_len + 1 < SDL_arraysize(line_platform)) { |
1903 | SDL_strlcpy(line_platform, tmp, platform_len); |
1904 | if (SDL_strncasecmp(line_platform, platform, platform_len) == 0 && |
1905 | SDL_AddGamepadMapping(line) > 0) { |
1906 | gamepads++; |
1907 | } |
1908 | } |
1909 | } |
1910 | } |
1911 | |
1912 | line = line_end + 1; |
1913 | } |
1914 | |
1915 | PopMappingChangeTracking(); |
1916 | |
1917 | SDL_UnlockJoysticks(); |
1918 | |
1919 | SDL_free(buf); |
1920 | return gamepads; |
1921 | } |
1922 | |
1923 | int SDL_AddGamepadMappingsFromFile(const char *file) |
1924 | { |
1925 | return SDL_AddGamepadMappingsFromIO(SDL_IOFromFile(file, "rb" ), true); |
1926 | } |
1927 | |
1928 | bool SDL_ReloadGamepadMappings(void) |
1929 | { |
1930 | SDL_Gamepad *gamepad; |
1931 | |
1932 | SDL_LockJoysticks(); |
1933 | |
1934 | PushMappingChangeTracking(); |
1935 | |
1936 | for (gamepad = SDL_gamepads; gamepad; gamepad = gamepad->next) { |
1937 | AddMappingChangeTracking(gamepad->mapping); |
1938 | } |
1939 | |
1940 | SDL_QuitGamepadMappings(); |
1941 | SDL_InitGamepadMappings(); |
1942 | |
1943 | PopMappingChangeTracking(); |
1944 | |
1945 | SDL_UnlockJoysticks(); |
1946 | |
1947 | return true; |
1948 | } |
1949 | |
1950 | static char *SDL_ConvertMappingToPositional(const char *mapping) |
1951 | { |
1952 | // Add space for '!' and null terminator |
1953 | size_t length = SDL_strlen(mapping) + 1 + 1; |
1954 | char *remapped = (char *)SDL_malloc(length); |
1955 | if (remapped) { |
1956 | char *button_A; |
1957 | char *button_B; |
1958 | char *button_X; |
1959 | char *button_Y; |
1960 | char *hint; |
1961 | |
1962 | SDL_strlcpy(remapped, mapping, length); |
1963 | button_A = SDL_strstr(remapped, "a:" ); |
1964 | button_B = SDL_strstr(remapped, "b:" ); |
1965 | button_X = SDL_strstr(remapped, "x:" ); |
1966 | button_Y = SDL_strstr(remapped, "y:" ); |
1967 | hint = SDL_strstr(remapped, "hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS" ); |
1968 | |
1969 | if (button_A) { |
1970 | *button_A = 'b'; |
1971 | } |
1972 | if (button_B) { |
1973 | *button_B = 'a'; |
1974 | } |
1975 | if (button_X) { |
1976 | *button_X = 'y'; |
1977 | } |
1978 | if (button_Y) { |
1979 | *button_Y = 'x'; |
1980 | } |
1981 | if (hint) { |
1982 | hint += 5; |
1983 | SDL_memmove(hint + 1, hint, SDL_strlen(hint) + 1); |
1984 | *hint = '!'; |
1985 | } |
1986 | } |
1987 | return remapped; |
1988 | } |
1989 | |
1990 | /* |
1991 | * Add or update an entry into the Mappings Database with a priority |
1992 | */ |
1993 | static int SDL_PrivateAddGamepadMapping(const char *mappingString, SDL_GamepadMappingPriority priority) |
1994 | { |
1995 | char *remapped = NULL; |
1996 | char *pchGUID; |
1997 | SDL_GUID jGUID; |
1998 | bool is_default_mapping = false; |
1999 | bool is_xinput_mapping = false; |
2000 | bool existing = false; |
2001 | GamepadMapping_t *pGamepadMapping; |
2002 | int result = -1; |
2003 | |
2004 | SDL_AssertJoysticksLocked(); |
2005 | |
2006 | if (!mappingString) { |
2007 | SDL_InvalidParamError("mappingString" ); |
2008 | return -1; |
2009 | } |
2010 | |
2011 | { // Extract and verify the hint field |
2012 | const char *tmp; |
2013 | |
2014 | tmp = SDL_strstr(mappingString, SDL_GAMEPAD_HINT_FIELD); |
2015 | if (tmp) { |
2016 | bool default_value, value, negate; |
2017 | int len; |
2018 | char hint[128]; |
2019 | |
2020 | tmp += SDL_GAMEPAD_HINT_FIELD_SIZE; |
2021 | |
2022 | if (*tmp == '!') { |
2023 | negate = true; |
2024 | ++tmp; |
2025 | } else { |
2026 | negate = false; |
2027 | } |
2028 | |
2029 | len = 0; |
2030 | while (*tmp && *tmp != ',' && *tmp != ':' && len < (sizeof(hint) - 1)) { |
2031 | hint[len++] = *tmp++; |
2032 | } |
2033 | hint[len] = '\0'; |
2034 | |
2035 | if (tmp[0] == ':' && tmp[1] == '=') { |
2036 | tmp += 2; |
2037 | default_value = SDL_atoi(tmp); |
2038 | } else { |
2039 | default_value = false; |
2040 | } |
2041 | |
2042 | if (SDL_strcmp(hint, "SDL_GAMECONTROLLER_USE_BUTTON_LABELS" ) == 0) { |
2043 | // This hint is used to signal whether the mapping uses positional buttons or not |
2044 | if (negate) { |
2045 | // This mapping uses positional buttons, we can use it as-is |
2046 | } else { |
2047 | // This mapping uses labeled buttons, we need to swap them to positional |
2048 | remapped = SDL_ConvertMappingToPositional(mappingString); |
2049 | if (!remapped) { |
2050 | goto done; |
2051 | } |
2052 | mappingString = remapped; |
2053 | } |
2054 | } else { |
2055 | value = SDL_GetHintBoolean(hint, default_value); |
2056 | if (negate) { |
2057 | value = !value; |
2058 | } |
2059 | if (!value) { |
2060 | result = 0; |
2061 | goto done; |
2062 | } |
2063 | } |
2064 | } |
2065 | } |
2066 | |
2067 | #ifdef ANDROID |
2068 | { // Extract and verify the SDK version |
2069 | const char *tmp; |
2070 | |
2071 | tmp = SDL_strstr(mappingString, SDL_GAMEPAD_SDKGE_FIELD); |
2072 | if (tmp) { |
2073 | tmp += SDL_GAMEPAD_SDKGE_FIELD_SIZE; |
2074 | if (!(SDL_GetAndroidSDKVersion() >= SDL_atoi(tmp))) { |
2075 | SDL_SetError("SDK version %d < minimum version %d" , SDL_GetAndroidSDKVersion(), SDL_atoi(tmp)); |
2076 | goto done; |
2077 | } |
2078 | } |
2079 | tmp = SDL_strstr(mappingString, SDL_GAMEPAD_SDKLE_FIELD); |
2080 | if (tmp) { |
2081 | tmp += SDL_GAMEPAD_SDKLE_FIELD_SIZE; |
2082 | if (!(SDL_GetAndroidSDKVersion() <= SDL_atoi(tmp))) { |
2083 | SDL_SetError("SDK version %d > maximum version %d" , SDL_GetAndroidSDKVersion(), SDL_atoi(tmp)); |
2084 | goto done; |
2085 | } |
2086 | } |
2087 | } |
2088 | #endif |
2089 | |
2090 | pchGUID = SDL_PrivateGetGamepadGUIDFromMappingString(mappingString); |
2091 | if (!pchGUID) { |
2092 | SDL_SetError("Couldn't parse GUID from %s" , mappingString); |
2093 | goto done; |
2094 | } |
2095 | if (!SDL_strcasecmp(pchGUID, "default" )) { |
2096 | is_default_mapping = true; |
2097 | } else if (!SDL_strcasecmp(pchGUID, "xinput" )) { |
2098 | is_xinput_mapping = true; |
2099 | } |
2100 | jGUID = SDL_StringToGUID(pchGUID); |
2101 | SDL_free(pchGUID); |
2102 | |
2103 | pGamepadMapping = SDL_PrivateAddMappingForGUID(jGUID, mappingString, &existing, priority); |
2104 | if (!pGamepadMapping) { |
2105 | goto done; |
2106 | } |
2107 | |
2108 | if (existing) { |
2109 | result = 0; |
2110 | } else { |
2111 | if (is_default_mapping) { |
2112 | s_pDefaultMapping = pGamepadMapping; |
2113 | } else if (is_xinput_mapping) { |
2114 | s_pXInputMapping = pGamepadMapping; |
2115 | } |
2116 | result = 1; |
2117 | } |
2118 | done: |
2119 | if (remapped) { |
2120 | SDL_free(remapped); |
2121 | } |
2122 | return result; |
2123 | } |
2124 | |
2125 | /* |
2126 | * Add or update an entry into the Mappings Database |
2127 | */ |
2128 | int SDL_AddGamepadMapping(const char *mapping) |
2129 | { |
2130 | int result; |
2131 | |
2132 | SDL_LockJoysticks(); |
2133 | { |
2134 | result = SDL_PrivateAddGamepadMapping(mapping, SDL_GAMEPAD_MAPPING_PRIORITY_API); |
2135 | } |
2136 | SDL_UnlockJoysticks(); |
2137 | |
2138 | return result; |
2139 | } |
2140 | |
2141 | /* |
2142 | * Create a mapping string for a mapping |
2143 | */ |
2144 | static char *CreateMappingString(GamepadMapping_t *mapping, SDL_GUID guid) |
2145 | { |
2146 | char *pMappingString, *pPlatformString; |
2147 | char pchGUID[33]; |
2148 | size_t needed; |
2149 | bool need_platform = false; |
2150 | const char *platform = NULL; |
2151 | |
2152 | SDL_AssertJoysticksLocked(); |
2153 | |
2154 | SDL_GUIDToString(guid, pchGUID, sizeof(pchGUID)); |
2155 | |
2156 | // allocate enough memory for GUID + ',' + name + ',' + mapping + \0 |
2157 | needed = SDL_strlen(pchGUID) + 1 + SDL_strlen(mapping->name) + 1 + SDL_strlen(mapping->mapping) + 1; |
2158 | |
2159 | if (!SDL_strstr(mapping->mapping, SDL_GAMEPAD_PLATFORM_FIELD)) { |
2160 | // add memory for ',' + platform:PLATFORM |
2161 | need_platform = true; |
2162 | if (mapping->mapping[SDL_strlen(mapping->mapping) - 1] != ',') { |
2163 | needed += 1; |
2164 | } |
2165 | platform = SDL_GetPlatform(); |
2166 | needed += SDL_GAMEPAD_PLATFORM_FIELD_SIZE + SDL_strlen(platform) + 1; |
2167 | } |
2168 | |
2169 | pMappingString = (char *)SDL_malloc(needed); |
2170 | if (!pMappingString) { |
2171 | return NULL; |
2172 | } |
2173 | |
2174 | (void)SDL_snprintf(pMappingString, needed, "%s,%s,%s" , pchGUID, mapping->name, mapping->mapping); |
2175 | |
2176 | if (need_platform) { |
2177 | if (mapping->mapping[SDL_strlen(mapping->mapping) - 1] != ',') { |
2178 | SDL_strlcat(pMappingString, "," , needed); |
2179 | } |
2180 | SDL_strlcat(pMappingString, SDL_GAMEPAD_PLATFORM_FIELD, needed); |
2181 | SDL_strlcat(pMappingString, platform, needed); |
2182 | SDL_strlcat(pMappingString, "," , needed); |
2183 | } |
2184 | |
2185 | // Make sure multiple platform strings haven't made their way into the mapping |
2186 | pPlatformString = SDL_strstr(pMappingString, SDL_GAMEPAD_PLATFORM_FIELD); |
2187 | if (pPlatformString) { |
2188 | pPlatformString = SDL_strstr(pPlatformString + 1, SDL_GAMEPAD_PLATFORM_FIELD); |
2189 | if (pPlatformString) { |
2190 | *pPlatformString = '\0'; |
2191 | } |
2192 | } |
2193 | return pMappingString; |
2194 | } |
2195 | |
2196 | char **SDL_GetGamepadMappings(int *count) |
2197 | { |
2198 | int num_mappings = 0; |
2199 | char **result = NULL; |
2200 | char **mappings = NULL; |
2201 | |
2202 | if (count) { |
2203 | *count = 0; |
2204 | } |
2205 | |
2206 | SDL_LockJoysticks(); |
2207 | |
2208 | for (GamepadMapping_t *mapping = s_pSupportedGamepads; mapping; mapping = mapping->next) { |
2209 | if (SDL_memcmp(&mapping->guid, &s_zeroGUID, sizeof(mapping->guid)) == 0) { |
2210 | continue; |
2211 | } |
2212 | num_mappings++; |
2213 | } |
2214 | |
2215 | size_t final_allocation = sizeof (char *); // for the NULL terminator element. |
2216 | bool failed = false; |
2217 | mappings = (char **) SDL_calloc(num_mappings + 1, sizeof (char *)); |
2218 | if (!mappings) { |
2219 | failed = true; |
2220 | } else { |
2221 | int i = 0; |
2222 | for (GamepadMapping_t *mapping = s_pSupportedGamepads; mapping; mapping = mapping->next) { |
2223 | if (SDL_memcmp(&mapping->guid, &s_zeroGUID, sizeof(mapping->guid)) == 0) { |
2224 | continue; |
2225 | } |
2226 | |
2227 | char *mappingstr = CreateMappingString(mapping, mapping->guid); |
2228 | if (!mappingstr) { |
2229 | failed = true; |
2230 | break; // error string is already set. |
2231 | } |
2232 | |
2233 | SDL_assert(i < num_mappings); |
2234 | mappings[i++] = mappingstr; |
2235 | |
2236 | final_allocation += SDL_strlen(mappingstr) + 1 + sizeof (char *); |
2237 | } |
2238 | } |
2239 | |
2240 | SDL_UnlockJoysticks(); |
2241 | |
2242 | if (!failed) { |
2243 | result = (char **) SDL_malloc(final_allocation); |
2244 | if (result) { |
2245 | final_allocation -= (sizeof (char *) * num_mappings + 1); |
2246 | char *strptr = (char *) (result + (num_mappings + 1)); |
2247 | for (int i = 0; i < num_mappings; i++) { |
2248 | result[i] = strptr; |
2249 | const size_t slen = SDL_strlcpy(strptr, mappings[i], final_allocation) + 1; |
2250 | SDL_assert(final_allocation >= slen); |
2251 | final_allocation -= slen; |
2252 | strptr += slen; |
2253 | } |
2254 | result[num_mappings] = NULL; |
2255 | |
2256 | if (count) { |
2257 | *count = num_mappings; |
2258 | } |
2259 | } |
2260 | } |
2261 | |
2262 | if (mappings) { |
2263 | for (int i = 0; i < num_mappings; i++) { |
2264 | SDL_free(mappings[i]); |
2265 | } |
2266 | SDL_free(mappings); |
2267 | } |
2268 | |
2269 | return result; |
2270 | } |
2271 | |
2272 | /* |
2273 | * Get the mapping string for this GUID |
2274 | */ |
2275 | char *SDL_GetGamepadMappingForGUID(SDL_GUID guid) |
2276 | { |
2277 | char *result; |
2278 | |
2279 | SDL_LockJoysticks(); |
2280 | { |
2281 | GamepadMapping_t *mapping = SDL_PrivateGetGamepadMappingForGUID(guid, false); |
2282 | if (mapping) { |
2283 | result = CreateMappingString(mapping, guid); |
2284 | } else { |
2285 | SDL_SetError("Mapping not available" ); |
2286 | result = NULL; |
2287 | } |
2288 | } |
2289 | SDL_UnlockJoysticks(); |
2290 | |
2291 | return result; |
2292 | } |
2293 | |
2294 | /* |
2295 | * Get the mapping string for this device |
2296 | */ |
2297 | char *SDL_GetGamepadMapping(SDL_Gamepad *gamepad) |
2298 | { |
2299 | char *result; |
2300 | |
2301 | SDL_LockJoysticks(); |
2302 | { |
2303 | CHECK_GAMEPAD_MAGIC(gamepad, NULL); |
2304 | |
2305 | result = CreateMappingString(gamepad->mapping, gamepad->joystick->guid); |
2306 | } |
2307 | SDL_UnlockJoysticks(); |
2308 | |
2309 | return result; |
2310 | } |
2311 | |
2312 | /* |
2313 | * Set the mapping string for this device |
2314 | */ |
2315 | bool SDL_SetGamepadMapping(SDL_JoystickID instance_id, const char *mapping) |
2316 | { |
2317 | SDL_GUID guid = SDL_GetJoystickGUIDForID(instance_id); |
2318 | bool result = false; |
2319 | |
2320 | if (SDL_memcmp(&guid, &s_zeroGUID, sizeof(guid)) == 0) { |
2321 | return SDL_InvalidParamError("instance_id" ); |
2322 | } |
2323 | |
2324 | if (!mapping) { |
2325 | mapping = "*,*," ; |
2326 | } |
2327 | |
2328 | SDL_LockJoysticks(); |
2329 | { |
2330 | if (SDL_PrivateAddMappingForGUID(guid, mapping, NULL, SDL_GAMEPAD_MAPPING_PRIORITY_API)) { |
2331 | result = true; |
2332 | } |
2333 | } |
2334 | SDL_UnlockJoysticks(); |
2335 | |
2336 | return result; |
2337 | } |
2338 | |
2339 | static void SDL_LoadGamepadHints(void) |
2340 | { |
2341 | const char *hint = SDL_GetHint(SDL_HINT_GAMECONTROLLERCONFIG); |
2342 | if (hint && hint[0]) { |
2343 | char *pTempMappings = SDL_strdup(hint); |
2344 | char *pUserMappings = pTempMappings; |
2345 | |
2346 | PushMappingChangeTracking(); |
2347 | |
2348 | while (pUserMappings) { |
2349 | char *pchNewLine = NULL; |
2350 | |
2351 | pchNewLine = SDL_strchr(pUserMappings, '\n'); |
2352 | if (pchNewLine) { |
2353 | *pchNewLine = '\0'; |
2354 | } |
2355 | |
2356 | SDL_PrivateAddGamepadMapping(pUserMappings, SDL_GAMEPAD_MAPPING_PRIORITY_USER); |
2357 | |
2358 | if (pchNewLine) { |
2359 | pUserMappings = pchNewLine + 1; |
2360 | } else { |
2361 | pUserMappings = NULL; |
2362 | } |
2363 | } |
2364 | |
2365 | PopMappingChangeTracking(); |
2366 | |
2367 | SDL_free(pTempMappings); |
2368 | } |
2369 | } |
2370 | |
2371 | /* |
2372 | * Fill the given buffer with the expected gamepad mapping filepath. |
2373 | * Usually this will just be SDL_HINT_GAMECONTROLLERCONFIG_FILE, but for |
2374 | * Android, we want to get the internal storage path. |
2375 | */ |
2376 | static bool SDL_GetGamepadMappingFilePath(char *path, size_t size) |
2377 | { |
2378 | const char *hint = SDL_GetHint(SDL_HINT_GAMECONTROLLERCONFIG_FILE); |
2379 | if (hint && *hint) { |
2380 | return SDL_strlcpy(path, hint, size) < size; |
2381 | } |
2382 | |
2383 | #ifdef SDL_PLATFORM_ANDROID |
2384 | return SDL_snprintf(path, size, "%s/gamepad_map.txt" , SDL_GetAndroidInternalStoragePath()) < size; |
2385 | #else |
2386 | return false; |
2387 | #endif |
2388 | } |
2389 | |
2390 | /* |
2391 | * Initialize the gamepad system, mostly load our DB of gamepad config mappings |
2392 | */ |
2393 | bool SDL_InitGamepadMappings(void) |
2394 | { |
2395 | char szGamepadMapPath[1024]; |
2396 | int i = 0; |
2397 | const char *pMappingString = NULL; |
2398 | |
2399 | SDL_AssertJoysticksLocked(); |
2400 | |
2401 | PushMappingChangeTracking(); |
2402 | |
2403 | pMappingString = s_GamepadMappings[i]; |
2404 | while (pMappingString) { |
2405 | SDL_PrivateAddGamepadMapping(pMappingString, SDL_GAMEPAD_MAPPING_PRIORITY_DEFAULT); |
2406 | |
2407 | i++; |
2408 | pMappingString = s_GamepadMappings[i]; |
2409 | } |
2410 | |
2411 | if (SDL_GetGamepadMappingFilePath(szGamepadMapPath, sizeof(szGamepadMapPath))) { |
2412 | SDL_AddGamepadMappingsFromFile(szGamepadMapPath); |
2413 | } |
2414 | |
2415 | // load in any user supplied config |
2416 | SDL_LoadGamepadHints(); |
2417 | |
2418 | SDL_LoadVIDPIDList(&SDL_allowed_gamepads); |
2419 | SDL_LoadVIDPIDList(&SDL_ignored_gamepads); |
2420 | |
2421 | PopMappingChangeTracking(); |
2422 | |
2423 | return true; |
2424 | } |
2425 | |
2426 | bool SDL_InitGamepads(void) |
2427 | { |
2428 | int i; |
2429 | SDL_JoystickID *joysticks; |
2430 | |
2431 | SDL_gamepads_initialized = true; |
2432 | |
2433 | // Watch for joystick events and fire gamepad ones if needed |
2434 | SDL_AddEventWatch(SDL_GamepadEventWatcher, NULL); |
2435 | |
2436 | // Send added events for gamepads currently attached |
2437 | joysticks = SDL_GetJoysticks(NULL); |
2438 | if (joysticks) { |
2439 | for (i = 0; joysticks[i]; ++i) { |
2440 | if (SDL_IsGamepad(joysticks[i])) { |
2441 | SDL_PrivateGamepadAdded(joysticks[i]); |
2442 | } |
2443 | } |
2444 | SDL_free(joysticks); |
2445 | } |
2446 | |
2447 | return true; |
2448 | } |
2449 | |
2450 | bool SDL_HasGamepad(void) |
2451 | { |
2452 | int num_joysticks = 0; |
2453 | int num_gamepads = 0; |
2454 | SDL_JoystickID *joysticks = SDL_GetJoysticks(&num_joysticks); |
2455 | if (joysticks) { |
2456 | int i; |
2457 | for (i = num_joysticks - 1; i >= 0 && num_gamepads == 0; --i) { |
2458 | if (SDL_IsGamepad(joysticks[i])) { |
2459 | ++num_gamepads; |
2460 | } |
2461 | } |
2462 | SDL_free(joysticks); |
2463 | } |
2464 | if (num_gamepads > 0) { |
2465 | return true; |
2466 | } |
2467 | return false; |
2468 | } |
2469 | |
2470 | SDL_JoystickID *SDL_GetGamepads(int *count) |
2471 | { |
2472 | int num_joysticks = 0; |
2473 | int num_gamepads = 0; |
2474 | SDL_JoystickID *joysticks = SDL_GetJoysticks(&num_joysticks); |
2475 | if (joysticks) { |
2476 | int i; |
2477 | for (i = num_joysticks - 1; i >= 0; --i) { |
2478 | if (SDL_IsGamepad(joysticks[i])) { |
2479 | ++num_gamepads; |
2480 | } else { |
2481 | SDL_memmove(&joysticks[i], &joysticks[i+1], (num_gamepads + 1) * sizeof(joysticks[i])); |
2482 | } |
2483 | } |
2484 | } |
2485 | if (count) { |
2486 | *count = num_gamepads; |
2487 | } |
2488 | return joysticks; |
2489 | } |
2490 | |
2491 | const char *SDL_GetGamepadNameForID(SDL_JoystickID instance_id) |
2492 | { |
2493 | const char *result = NULL; |
2494 | |
2495 | SDL_LockJoysticks(); |
2496 | { |
2497 | GamepadMapping_t *mapping = SDL_PrivateGetGamepadMapping(instance_id, true); |
2498 | if (mapping) { |
2499 | if (SDL_strcmp(mapping->name, "*" ) == 0) { |
2500 | result = SDL_GetJoystickNameForID(instance_id); |
2501 | } else { |
2502 | result = SDL_GetPersistentString(mapping->name); |
2503 | } |
2504 | } |
2505 | } |
2506 | SDL_UnlockJoysticks(); |
2507 | |
2508 | return result; |
2509 | } |
2510 | |
2511 | const char *SDL_GetGamepadPathForID(SDL_JoystickID instance_id) |
2512 | { |
2513 | return SDL_GetJoystickPathForID(instance_id); |
2514 | } |
2515 | |
2516 | int SDL_GetGamepadPlayerIndexForID(SDL_JoystickID instance_id) |
2517 | { |
2518 | return SDL_GetJoystickPlayerIndexForID(instance_id); |
2519 | } |
2520 | |
2521 | SDL_GUID SDL_GetGamepadGUIDForID(SDL_JoystickID instance_id) |
2522 | { |
2523 | return SDL_GetJoystickGUIDForID(instance_id); |
2524 | } |
2525 | |
2526 | Uint16 SDL_GetGamepadVendorForID(SDL_JoystickID instance_id) |
2527 | { |
2528 | return SDL_GetJoystickVendorForID(instance_id); |
2529 | } |
2530 | |
2531 | Uint16 SDL_GetGamepadProductForID(SDL_JoystickID instance_id) |
2532 | { |
2533 | return SDL_GetJoystickProductForID(instance_id); |
2534 | } |
2535 | |
2536 | Uint16 SDL_GetGamepadProductVersionForID(SDL_JoystickID instance_id) |
2537 | { |
2538 | return SDL_GetJoystickProductVersionForID(instance_id); |
2539 | } |
2540 | |
2541 | SDL_GamepadType SDL_GetGamepadTypeForID(SDL_JoystickID instance_id) |
2542 | { |
2543 | SDL_GamepadType type = SDL_GAMEPAD_TYPE_UNKNOWN; |
2544 | |
2545 | SDL_LockJoysticks(); |
2546 | { |
2547 | GamepadMapping_t *mapping = SDL_PrivateGetGamepadMapping(instance_id, true); |
2548 | if (mapping) { |
2549 | char *type_string, *comma; |
2550 | |
2551 | type_string = SDL_strstr(mapping->mapping, SDL_GAMEPAD_TYPE_FIELD); |
2552 | if (type_string) { |
2553 | type_string += SDL_GAMEPAD_TYPE_FIELD_SIZE; |
2554 | comma = SDL_strchr(type_string, ','); |
2555 | if (comma) { |
2556 | *comma = '\0'; |
2557 | type = SDL_GetGamepadTypeFromString(type_string); |
2558 | *comma = ','; |
2559 | } |
2560 | } |
2561 | } |
2562 | } |
2563 | SDL_UnlockJoysticks(); |
2564 | |
2565 | if (type != SDL_GAMEPAD_TYPE_UNKNOWN) { |
2566 | return type; |
2567 | } |
2568 | return SDL_GetRealGamepadTypeForID(instance_id); |
2569 | } |
2570 | |
2571 | SDL_GamepadType SDL_GetRealGamepadTypeForID(SDL_JoystickID instance_id) |
2572 | { |
2573 | SDL_GamepadType type = SDL_GAMEPAD_TYPE_UNKNOWN; |
2574 | const SDL_SteamVirtualGamepadInfo *info; |
2575 | |
2576 | SDL_LockJoysticks(); |
2577 | { |
2578 | info = SDL_GetJoystickVirtualGamepadInfoForID(instance_id); |
2579 | if (info) { |
2580 | type = info->type; |
2581 | } else { |
2582 | type = SDL_GetGamepadTypeFromGUID(SDL_GetJoystickGUIDForID(instance_id), SDL_GetJoystickNameForID(instance_id)); |
2583 | } |
2584 | } |
2585 | SDL_UnlockJoysticks(); |
2586 | |
2587 | return type; |
2588 | } |
2589 | |
2590 | char *SDL_GetGamepadMappingForID(SDL_JoystickID instance_id) |
2591 | { |
2592 | char *result = NULL; |
2593 | |
2594 | SDL_LockJoysticks(); |
2595 | { |
2596 | GamepadMapping_t *mapping = SDL_PrivateGetGamepadMapping(instance_id, true); |
2597 | if (mapping) { |
2598 | char pchGUID[33]; |
2599 | SDL_GUID guid = SDL_GetJoystickGUIDForID(instance_id); |
2600 | SDL_GUIDToString(guid, pchGUID, sizeof(pchGUID)); |
2601 | SDL_asprintf(&result, "%s,%s,%s" , pchGUID, mapping->name, mapping->mapping); |
2602 | } |
2603 | } |
2604 | SDL_UnlockJoysticks(); |
2605 | |
2606 | return result; |
2607 | } |
2608 | |
2609 | /* |
2610 | * Return 1 if the joystick with this name and GUID is a supported gamepad |
2611 | */ |
2612 | bool SDL_IsGamepadNameAndGUID(const char *name, SDL_GUID guid) |
2613 | { |
2614 | bool result; |
2615 | |
2616 | SDL_LockJoysticks(); |
2617 | { |
2618 | if (s_pDefaultMapping || SDL_PrivateGetGamepadMappingForNameAndGUID(name, guid) != NULL) { |
2619 | result = true; |
2620 | } else { |
2621 | result = false; |
2622 | } |
2623 | } |
2624 | SDL_UnlockJoysticks(); |
2625 | |
2626 | return result; |
2627 | } |
2628 | |
2629 | /* |
2630 | * Return 1 if the joystick at this device index is a supported gamepad |
2631 | */ |
2632 | bool SDL_IsGamepad(SDL_JoystickID instance_id) |
2633 | { |
2634 | bool result; |
2635 | |
2636 | SDL_LockJoysticks(); |
2637 | { |
2638 | const void *value; |
2639 | if (SDL_FindInHashTable(s_gamepadInstanceIDs, (void *)(uintptr_t)instance_id, &value)) { |
2640 | result = (bool)(uintptr_t)value; |
2641 | } else { |
2642 | if (SDL_PrivateGetGamepadMapping(instance_id, true) != NULL) { |
2643 | result = true; |
2644 | } else { |
2645 | result = false; |
2646 | } |
2647 | |
2648 | if (!s_gamepadInstanceIDs) { |
2649 | s_gamepadInstanceIDs = SDL_CreateHashTable(0, false, SDL_HashID, SDL_KeyMatchID, NULL, NULL); |
2650 | } |
2651 | SDL_InsertIntoHashTable(s_gamepadInstanceIDs, (void *)(uintptr_t)instance_id, (void *)(uintptr_t)result, true); |
2652 | } |
2653 | } |
2654 | SDL_UnlockJoysticks(); |
2655 | |
2656 | return result; |
2657 | } |
2658 | |
2659 | /* |
2660 | * Return 1 if the gamepad should be ignored by SDL |
2661 | */ |
2662 | bool SDL_ShouldIgnoreGamepad(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name) |
2663 | { |
2664 | #ifdef SDL_PLATFORM_LINUX |
2665 | if (SDL_endswith(name, " Motion Sensors" )) { |
2666 | // Don't treat the PS3 and PS4 motion controls as a separate gamepad |
2667 | return true; |
2668 | } |
2669 | if (SDL_strncmp(name, "Nintendo " , 9) == 0 && SDL_strstr(name, " IMU" ) != NULL) { |
2670 | // Don't treat the Nintendo IMU as a separate gamepad |
2671 | return true; |
2672 | } |
2673 | if (SDL_endswith(name, " Accelerometer" ) || |
2674 | SDL_endswith(name, " IR" ) || |
2675 | SDL_endswith(name, " Motion Plus" ) || |
2676 | SDL_endswith(name, " Nunchuk" )) { |
2677 | // Don't treat the Wii extension controls as a separate gamepad |
2678 | return true; |
2679 | } |
2680 | #endif |
2681 | |
2682 | if (name && SDL_strcmp(name, "uinput-fpc" ) == 0) { |
2683 | // The Google Pixel fingerprint sensor reports itself as a joystick |
2684 | return true; |
2685 | } |
2686 | |
2687 | #ifdef SDL_PLATFORM_WIN32 |
2688 | if (SDL_GetHintBoolean("SDL_GAMECONTROLLER_ALLOW_STEAM_VIRTUAL_GAMEPAD" , false) && |
2689 | SDL_GetHintBoolean("STEAM_COMPAT_PROTON" , false)) { |
2690 | // We are launched by Steam and running under Proton |
2691 | // We can't tell whether this controller is a Steam Virtual Gamepad, |
2692 | // so assume that Proton is doing the appropriate filtering of controllers |
2693 | // and anything we see here is fine to use. |
2694 | return false; |
2695 | } |
2696 | #endif // SDL_PLATFORM_WIN32 |
2697 | |
2698 | if (SDL_IsJoystickSteamVirtualGamepad(vendor_id, product_id, version)) { |
2699 | return !SDL_GetHintBoolean("SDL_GAMECONTROLLER_ALLOW_STEAM_VIRTUAL_GAMEPAD" , false); |
2700 | } |
2701 | |
2702 | if (SDL_allowed_gamepads.num_included_entries > 0) { |
2703 | if (SDL_VIDPIDInList(vendor_id, product_id, &SDL_allowed_gamepads)) { |
2704 | return false; |
2705 | } |
2706 | return true; |
2707 | } else { |
2708 | if (SDL_VIDPIDInList(vendor_id, product_id, &SDL_ignored_gamepads)) { |
2709 | return true; |
2710 | } |
2711 | return false; |
2712 | } |
2713 | } |
2714 | |
2715 | /* |
2716 | * Open a gamepad for use |
2717 | * |
2718 | * This function returns a gamepad identifier, or NULL if an error occurred. |
2719 | */ |
2720 | SDL_Gamepad *SDL_OpenGamepad(SDL_JoystickID instance_id) |
2721 | { |
2722 | SDL_Gamepad *gamepad; |
2723 | SDL_Gamepad *gamepadlist; |
2724 | GamepadMapping_t *pSupportedGamepad = NULL; |
2725 | |
2726 | SDL_LockJoysticks(); |
2727 | |
2728 | gamepadlist = SDL_gamepads; |
2729 | // If the gamepad is already open, return it |
2730 | while (gamepadlist) { |
2731 | if (instance_id == gamepadlist->joystick->instance_id) { |
2732 | gamepad = gamepadlist; |
2733 | ++gamepad->ref_count; |
2734 | SDL_UnlockJoysticks(); |
2735 | return gamepad; |
2736 | } |
2737 | gamepadlist = gamepadlist->next; |
2738 | } |
2739 | |
2740 | // Find a gamepad mapping |
2741 | pSupportedGamepad = SDL_PrivateGetGamepadMapping(instance_id, true); |
2742 | if (!pSupportedGamepad) { |
2743 | SDL_SetError("Couldn't find mapping for device (%" SDL_PRIu32 ")" , instance_id); |
2744 | SDL_UnlockJoysticks(); |
2745 | return NULL; |
2746 | } |
2747 | |
2748 | // Create and initialize the gamepad |
2749 | gamepad = (SDL_Gamepad *)SDL_calloc(1, sizeof(*gamepad)); |
2750 | if (!gamepad) { |
2751 | SDL_UnlockJoysticks(); |
2752 | return NULL; |
2753 | } |
2754 | SDL_SetObjectValid(gamepad, SDL_OBJECT_TYPE_GAMEPAD, true); |
2755 | |
2756 | gamepad->joystick = SDL_OpenJoystick(instance_id); |
2757 | if (!gamepad->joystick) { |
2758 | SDL_free(gamepad); |
2759 | SDL_UnlockJoysticks(); |
2760 | return NULL; |
2761 | } |
2762 | |
2763 | if (gamepad->joystick->naxes) { |
2764 | gamepad->last_match_axis = (SDL_GamepadBinding **)SDL_calloc(gamepad->joystick->naxes, sizeof(*gamepad->last_match_axis)); |
2765 | if (!gamepad->last_match_axis) { |
2766 | SDL_CloseJoystick(gamepad->joystick); |
2767 | SDL_free(gamepad); |
2768 | SDL_UnlockJoysticks(); |
2769 | return NULL; |
2770 | } |
2771 | } |
2772 | if (gamepad->joystick->nhats) { |
2773 | gamepad->last_hat_mask = (Uint8 *)SDL_calloc(gamepad->joystick->nhats, sizeof(*gamepad->last_hat_mask)); |
2774 | if (!gamepad->last_hat_mask) { |
2775 | SDL_CloseJoystick(gamepad->joystick); |
2776 | SDL_free(gamepad->last_match_axis); |
2777 | SDL_free(gamepad); |
2778 | SDL_UnlockJoysticks(); |
2779 | return NULL; |
2780 | } |
2781 | } |
2782 | |
2783 | SDL_PrivateLoadButtonMapping(gamepad, pSupportedGamepad); |
2784 | |
2785 | // Add the gamepad to list |
2786 | ++gamepad->ref_count; |
2787 | // Link the gamepad in the list |
2788 | gamepad->next = SDL_gamepads; |
2789 | SDL_gamepads = gamepad; |
2790 | |
2791 | SDL_UnlockJoysticks(); |
2792 | |
2793 | return gamepad; |
2794 | } |
2795 | |
2796 | /* |
2797 | * Manually pump for gamepad updates. |
2798 | */ |
2799 | void SDL_UpdateGamepads(void) |
2800 | { |
2801 | // Just for API completeness; the joystick API does all the work. |
2802 | SDL_UpdateJoysticks(); |
2803 | } |
2804 | |
2805 | /** |
2806 | * Return whether a gamepad has a given axis |
2807 | */ |
2808 | bool SDL_GamepadHasAxis(SDL_Gamepad *gamepad, SDL_GamepadAxis axis) |
2809 | { |
2810 | bool result = false; |
2811 | |
2812 | SDL_LockJoysticks(); |
2813 | { |
2814 | int i; |
2815 | |
2816 | CHECK_GAMEPAD_MAGIC(gamepad, false); |
2817 | |
2818 | for (i = 0; i < gamepad->num_bindings; ++i) { |
2819 | const SDL_GamepadBinding *binding = &gamepad->bindings[i]; |
2820 | if (binding->output_type == SDL_GAMEPAD_BINDTYPE_AXIS && binding->output.axis.axis == axis) { |
2821 | result = true; |
2822 | break; |
2823 | } |
2824 | } |
2825 | } |
2826 | SDL_UnlockJoysticks(); |
2827 | |
2828 | return result; |
2829 | } |
2830 | |
2831 | /* |
2832 | * Get the current state of an axis control on a gamepad |
2833 | */ |
2834 | Sint16 SDL_GetGamepadAxis(SDL_Gamepad *gamepad, SDL_GamepadAxis axis) |
2835 | { |
2836 | Sint16 result = 0; |
2837 | |
2838 | SDL_LockJoysticks(); |
2839 | { |
2840 | int i; |
2841 | |
2842 | CHECK_GAMEPAD_MAGIC(gamepad, 0); |
2843 | |
2844 | for (i = 0; i < gamepad->num_bindings; ++i) { |
2845 | const SDL_GamepadBinding *binding = &gamepad->bindings[i]; |
2846 | if (binding->output_type == SDL_GAMEPAD_BINDTYPE_AXIS && binding->output.axis.axis == axis) { |
2847 | int value = 0; |
2848 | bool valid_input_range; |
2849 | bool valid_output_range; |
2850 | |
2851 | if (binding->input_type == SDL_GAMEPAD_BINDTYPE_AXIS) { |
2852 | value = SDL_GetJoystickAxis(gamepad->joystick, binding->input.axis.axis); |
2853 | if (binding->input.axis.axis_min < binding->input.axis.axis_max) { |
2854 | valid_input_range = (value >= binding->input.axis.axis_min && value <= binding->input.axis.axis_max); |
2855 | } else { |
2856 | valid_input_range = (value >= binding->input.axis.axis_max && value <= binding->input.axis.axis_min); |
2857 | } |
2858 | if (valid_input_range) { |
2859 | if (binding->input.axis.axis_min != binding->output.axis.axis_min || binding->input.axis.axis_max != binding->output.axis.axis_max) { |
2860 | float normalized_value = (float)(value - binding->input.axis.axis_min) / (binding->input.axis.axis_max - binding->input.axis.axis_min); |
2861 | value = binding->output.axis.axis_min + (int)(normalized_value * (binding->output.axis.axis_max - binding->output.axis.axis_min)); |
2862 | } |
2863 | } else { |
2864 | value = 0; |
2865 | } |
2866 | } else if (binding->input_type == SDL_GAMEPAD_BINDTYPE_BUTTON) { |
2867 | if (SDL_GetJoystickButton(gamepad->joystick, binding->input.button)) { |
2868 | value = binding->output.axis.axis_max; |
2869 | } |
2870 | } else if (binding->input_type == SDL_GAMEPAD_BINDTYPE_HAT) { |
2871 | int hat_mask = SDL_GetJoystickHat(gamepad->joystick, binding->input.hat.hat); |
2872 | if (hat_mask & binding->input.hat.hat_mask) { |
2873 | value = binding->output.axis.axis_max; |
2874 | } |
2875 | } |
2876 | |
2877 | if (binding->output.axis.axis_min < binding->output.axis.axis_max) { |
2878 | valid_output_range = (value >= binding->output.axis.axis_min && value <= binding->output.axis.axis_max); |
2879 | } else { |
2880 | valid_output_range = (value >= binding->output.axis.axis_max && value <= binding->output.axis.axis_min); |
2881 | } |
2882 | // If the value is zero, there might be another binding that makes it non-zero |
2883 | if (value != 0 && valid_output_range) { |
2884 | result = (Sint16)value; |
2885 | break; |
2886 | } |
2887 | } |
2888 | } |
2889 | } |
2890 | SDL_UnlockJoysticks(); |
2891 | |
2892 | return result; |
2893 | } |
2894 | |
2895 | /** |
2896 | * Return whether a gamepad has a given button |
2897 | */ |
2898 | bool SDL_GamepadHasButton(SDL_Gamepad *gamepad, SDL_GamepadButton button) |
2899 | { |
2900 | bool result = false; |
2901 | |
2902 | SDL_LockJoysticks(); |
2903 | { |
2904 | int i; |
2905 | |
2906 | CHECK_GAMEPAD_MAGIC(gamepad, false); |
2907 | |
2908 | for (i = 0; i < gamepad->num_bindings; ++i) { |
2909 | const SDL_GamepadBinding *binding = &gamepad->bindings[i]; |
2910 | if (binding->output_type == SDL_GAMEPAD_BINDTYPE_BUTTON && binding->output.button == button) { |
2911 | result = true; |
2912 | break; |
2913 | } |
2914 | } |
2915 | } |
2916 | SDL_UnlockJoysticks(); |
2917 | |
2918 | return result; |
2919 | } |
2920 | |
2921 | /* |
2922 | * Get the current state of a button on a gamepad |
2923 | */ |
2924 | bool SDL_GetGamepadButton(SDL_Gamepad *gamepad, SDL_GamepadButton button) |
2925 | { |
2926 | bool result = false; |
2927 | |
2928 | SDL_LockJoysticks(); |
2929 | { |
2930 | int i; |
2931 | |
2932 | CHECK_GAMEPAD_MAGIC(gamepad, false); |
2933 | |
2934 | for (i = 0; i < gamepad->num_bindings; ++i) { |
2935 | const SDL_GamepadBinding *binding = &gamepad->bindings[i]; |
2936 | if (binding->output_type == SDL_GAMEPAD_BINDTYPE_BUTTON && binding->output.button == button) { |
2937 | if (binding->input_type == SDL_GAMEPAD_BINDTYPE_AXIS) { |
2938 | bool valid_input_range; |
2939 | |
2940 | int value = SDL_GetJoystickAxis(gamepad->joystick, binding->input.axis.axis); |
2941 | int threshold = binding->input.axis.axis_min + (binding->input.axis.axis_max - binding->input.axis.axis_min) / 2; |
2942 | if (binding->input.axis.axis_min < binding->input.axis.axis_max) { |
2943 | valid_input_range = (value >= binding->input.axis.axis_min && value <= binding->input.axis.axis_max); |
2944 | if (valid_input_range) { |
2945 | result |= (value >= threshold); |
2946 | } |
2947 | } else { |
2948 | valid_input_range = (value >= binding->input.axis.axis_max && value <= binding->input.axis.axis_min); |
2949 | if (valid_input_range) { |
2950 | result |= (value <= threshold); |
2951 | } |
2952 | } |
2953 | } else if (binding->input_type == SDL_GAMEPAD_BINDTYPE_BUTTON) { |
2954 | result |= SDL_GetJoystickButton(gamepad->joystick, binding->input.button); |
2955 | } else if (binding->input_type == SDL_GAMEPAD_BINDTYPE_HAT) { |
2956 | int hat_mask = SDL_GetJoystickHat(gamepad->joystick, binding->input.hat.hat); |
2957 | result |= ((hat_mask & binding->input.hat.hat_mask) != 0); |
2958 | } |
2959 | } |
2960 | } |
2961 | } |
2962 | SDL_UnlockJoysticks(); |
2963 | |
2964 | return result; |
2965 | } |
2966 | |
2967 | /** |
2968 | * Get the label of a button on a gamepad. |
2969 | */ |
2970 | static SDL_GamepadButtonLabel SDL_GetGamepadButtonLabelForFaceStyle(SDL_GamepadFaceStyle face_style, SDL_GamepadButton button) |
2971 | { |
2972 | SDL_GamepadButtonLabel label = SDL_GAMEPAD_BUTTON_LABEL_UNKNOWN; |
2973 | |
2974 | switch (face_style) { |
2975 | case SDL_GAMEPAD_FACE_STYLE_ABXY: |
2976 | switch (button) { |
2977 | case SDL_GAMEPAD_BUTTON_SOUTH: |
2978 | label = SDL_GAMEPAD_BUTTON_LABEL_A; |
2979 | break; |
2980 | case SDL_GAMEPAD_BUTTON_EAST: |
2981 | label = SDL_GAMEPAD_BUTTON_LABEL_B; |
2982 | break; |
2983 | case SDL_GAMEPAD_BUTTON_WEST: |
2984 | label = SDL_GAMEPAD_BUTTON_LABEL_X; |
2985 | break; |
2986 | case SDL_GAMEPAD_BUTTON_NORTH: |
2987 | label = SDL_GAMEPAD_BUTTON_LABEL_Y; |
2988 | break; |
2989 | default: |
2990 | break; |
2991 | } |
2992 | break; |
2993 | case SDL_GAMEPAD_FACE_STYLE_BAYX: |
2994 | switch (button) { |
2995 | case SDL_GAMEPAD_BUTTON_SOUTH: |
2996 | label = SDL_GAMEPAD_BUTTON_LABEL_B; |
2997 | break; |
2998 | case SDL_GAMEPAD_BUTTON_EAST: |
2999 | label = SDL_GAMEPAD_BUTTON_LABEL_A; |
3000 | break; |
3001 | case SDL_GAMEPAD_BUTTON_WEST: |
3002 | label = SDL_GAMEPAD_BUTTON_LABEL_Y; |
3003 | break; |
3004 | case SDL_GAMEPAD_BUTTON_NORTH: |
3005 | label = SDL_GAMEPAD_BUTTON_LABEL_X; |
3006 | break; |
3007 | default: |
3008 | break; |
3009 | } |
3010 | break; |
3011 | case SDL_GAMEPAD_FACE_STYLE_SONY: |
3012 | switch (button) { |
3013 | case SDL_GAMEPAD_BUTTON_SOUTH: |
3014 | label = SDL_GAMEPAD_BUTTON_LABEL_CROSS; |
3015 | break; |
3016 | case SDL_GAMEPAD_BUTTON_EAST: |
3017 | label = SDL_GAMEPAD_BUTTON_LABEL_CIRCLE; |
3018 | break; |
3019 | case SDL_GAMEPAD_BUTTON_WEST: |
3020 | label = SDL_GAMEPAD_BUTTON_LABEL_SQUARE; |
3021 | break; |
3022 | case SDL_GAMEPAD_BUTTON_NORTH: |
3023 | label = SDL_GAMEPAD_BUTTON_LABEL_TRIANGLE; |
3024 | break; |
3025 | default: |
3026 | break; |
3027 | } |
3028 | break; |
3029 | default: |
3030 | break; |
3031 | } |
3032 | return label; |
3033 | } |
3034 | |
3035 | /** |
3036 | * Get the label of a button on a gamepad. |
3037 | */ |
3038 | SDL_GamepadButtonLabel SDL_GetGamepadButtonLabelForType(SDL_GamepadType type, SDL_GamepadButton button) |
3039 | { |
3040 | return SDL_GetGamepadButtonLabelForFaceStyle(SDL_GetGamepadFaceStyleForGamepadType(type), button); |
3041 | } |
3042 | |
3043 | /** |
3044 | * Get the label of a button on a gamepad. |
3045 | */ |
3046 | SDL_GamepadButtonLabel SDL_GetGamepadButtonLabel(SDL_Gamepad *gamepad, SDL_GamepadButton button) |
3047 | { |
3048 | SDL_GamepadFaceStyle face_style; |
3049 | |
3050 | SDL_LockJoysticks(); |
3051 | { |
3052 | CHECK_GAMEPAD_MAGIC(gamepad, SDL_GAMEPAD_BUTTON_LABEL_UNKNOWN); |
3053 | |
3054 | face_style = gamepad->face_style; |
3055 | } |
3056 | SDL_UnlockJoysticks(); |
3057 | |
3058 | return SDL_GetGamepadButtonLabelForFaceStyle(face_style, button); |
3059 | } |
3060 | |
3061 | /** |
3062 | * Get the number of touchpads on a gamepad. |
3063 | */ |
3064 | int SDL_GetNumGamepadTouchpads(SDL_Gamepad *gamepad) |
3065 | { |
3066 | int result = 0; |
3067 | |
3068 | SDL_LockJoysticks(); |
3069 | { |
3070 | SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad); |
3071 | if (joystick) { |
3072 | result = joystick->ntouchpads; |
3073 | } |
3074 | } |
3075 | SDL_UnlockJoysticks(); |
3076 | |
3077 | return result; |
3078 | } |
3079 | |
3080 | /** |
3081 | * Get the number of supported simultaneous fingers on a touchpad on a gamepad. |
3082 | */ |
3083 | int SDL_GetNumGamepadTouchpadFingers(SDL_Gamepad *gamepad, int touchpad) |
3084 | { |
3085 | int result = 0; |
3086 | |
3087 | SDL_LockJoysticks(); |
3088 | { |
3089 | SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad); |
3090 | if (joystick) { |
3091 | if (touchpad >= 0 && touchpad < joystick->ntouchpads) { |
3092 | result = joystick->touchpads[touchpad].nfingers; |
3093 | } |
3094 | } |
3095 | } |
3096 | SDL_UnlockJoysticks(); |
3097 | |
3098 | return result; |
3099 | } |
3100 | |
3101 | /** |
3102 | * Get the current state of a finger on a touchpad on a gamepad. |
3103 | */ |
3104 | bool SDL_GetGamepadTouchpadFinger(SDL_Gamepad *gamepad, int touchpad, int finger, bool *down, float *x, float *y, float *pressure) |
3105 | { |
3106 | bool result = false; |
3107 | |
3108 | SDL_LockJoysticks(); |
3109 | { |
3110 | SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad); |
3111 | if (joystick) { |
3112 | if (touchpad >= 0 && touchpad < joystick->ntouchpads) { |
3113 | SDL_JoystickTouchpadInfo *touchpad_info = &joystick->touchpads[touchpad]; |
3114 | if (finger >= 0 && finger < touchpad_info->nfingers) { |
3115 | SDL_JoystickTouchpadFingerInfo *info = &touchpad_info->fingers[finger]; |
3116 | |
3117 | if (down) { |
3118 | *down = info->down; |
3119 | } |
3120 | if (x) { |
3121 | *x = info->x; |
3122 | } |
3123 | if (y) { |
3124 | *y = info->y; |
3125 | } |
3126 | if (pressure) { |
3127 | *pressure = info->pressure; |
3128 | } |
3129 | result = true; |
3130 | } else { |
3131 | result = SDL_InvalidParamError("finger" ); |
3132 | } |
3133 | } else { |
3134 | result = SDL_InvalidParamError("touchpad" ); |
3135 | } |
3136 | } |
3137 | } |
3138 | SDL_UnlockJoysticks(); |
3139 | |
3140 | return result; |
3141 | } |
3142 | |
3143 | /** |
3144 | * Return whether a gamepad has a particular sensor. |
3145 | */ |
3146 | bool SDL_GamepadHasSensor(SDL_Gamepad *gamepad, SDL_SensorType type) |
3147 | { |
3148 | bool result = false; |
3149 | |
3150 | SDL_LockJoysticks(); |
3151 | { |
3152 | SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad); |
3153 | if (joystick) { |
3154 | int i; |
3155 | for (i = 0; i < joystick->nsensors; ++i) { |
3156 | if (joystick->sensors[i].type == type) { |
3157 | result = true; |
3158 | break; |
3159 | } |
3160 | } |
3161 | } |
3162 | } |
3163 | SDL_UnlockJoysticks(); |
3164 | |
3165 | return result; |
3166 | } |
3167 | |
3168 | /* |
3169 | * Set whether data reporting for a gamepad sensor is enabled |
3170 | */ |
3171 | bool SDL_SetGamepadSensorEnabled(SDL_Gamepad *gamepad, SDL_SensorType type, bool enabled) |
3172 | { |
3173 | SDL_LockJoysticks(); |
3174 | { |
3175 | SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad); |
3176 | if (joystick) { |
3177 | int i; |
3178 | for (i = 0; i < joystick->nsensors; ++i) { |
3179 | SDL_JoystickSensorInfo *sensor = &joystick->sensors[i]; |
3180 | |
3181 | if (sensor->type == type) { |
3182 | if (sensor->enabled == (enabled != false)) { |
3183 | SDL_UnlockJoysticks(); |
3184 | return true; |
3185 | } |
3186 | |
3187 | if (type == SDL_SENSOR_ACCEL && joystick->accel_sensor) { |
3188 | if (enabled) { |
3189 | joystick->accel = SDL_OpenSensor(joystick->accel_sensor); |
3190 | if (!joystick->accel) { |
3191 | SDL_UnlockJoysticks(); |
3192 | return false; |
3193 | } |
3194 | } else { |
3195 | if (joystick->accel) { |
3196 | SDL_CloseSensor(joystick->accel); |
3197 | joystick->accel = NULL; |
3198 | } |
3199 | } |
3200 | } else if (type == SDL_SENSOR_GYRO && joystick->gyro_sensor) { |
3201 | if (enabled) { |
3202 | joystick->gyro = SDL_OpenSensor(joystick->gyro_sensor); |
3203 | if (!joystick->gyro) { |
3204 | SDL_UnlockJoysticks(); |
3205 | return false; |
3206 | } |
3207 | } else { |
3208 | if (joystick->gyro) { |
3209 | SDL_CloseSensor(joystick->gyro); |
3210 | joystick->gyro = NULL; |
3211 | } |
3212 | } |
3213 | } else { |
3214 | if (enabled) { |
3215 | if (joystick->nsensors_enabled == 0) { |
3216 | if (!joystick->driver->SetSensorsEnabled(joystick, true)) { |
3217 | SDL_UnlockJoysticks(); |
3218 | return false; |
3219 | } |
3220 | } |
3221 | ++joystick->nsensors_enabled; |
3222 | } else { |
3223 | if (joystick->nsensors_enabled == 1) { |
3224 | if (!joystick->driver->SetSensorsEnabled(joystick, false)) { |
3225 | SDL_UnlockJoysticks(); |
3226 | return false; |
3227 | } |
3228 | } |
3229 | --joystick->nsensors_enabled; |
3230 | } |
3231 | } |
3232 | |
3233 | sensor->enabled = enabled; |
3234 | SDL_UnlockJoysticks(); |
3235 | return true; |
3236 | } |
3237 | } |
3238 | } |
3239 | } |
3240 | SDL_UnlockJoysticks(); |
3241 | |
3242 | return SDL_Unsupported(); |
3243 | } |
3244 | |
3245 | /* |
3246 | * Query whether sensor data reporting is enabled for a gamepad |
3247 | */ |
3248 | bool SDL_GamepadSensorEnabled(SDL_Gamepad *gamepad, SDL_SensorType type) |
3249 | { |
3250 | bool result = false; |
3251 | |
3252 | SDL_LockJoysticks(); |
3253 | { |
3254 | SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad); |
3255 | if (joystick) { |
3256 | int i; |
3257 | for (i = 0; i < joystick->nsensors; ++i) { |
3258 | if (joystick->sensors[i].type == type) { |
3259 | result = joystick->sensors[i].enabled; |
3260 | break; |
3261 | } |
3262 | } |
3263 | } |
3264 | } |
3265 | SDL_UnlockJoysticks(); |
3266 | |
3267 | return result; |
3268 | } |
3269 | |
3270 | /* |
3271 | * Get the data rate of a gamepad sensor. |
3272 | */ |
3273 | float SDL_GetGamepadSensorDataRate(SDL_Gamepad *gamepad, SDL_SensorType type) |
3274 | { |
3275 | float result = 0.0f; |
3276 | |
3277 | SDL_LockJoysticks(); |
3278 | { |
3279 | SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad); |
3280 | if (joystick) { |
3281 | int i; |
3282 | for (i = 0; i < joystick->nsensors; ++i) { |
3283 | SDL_JoystickSensorInfo *sensor = &joystick->sensors[i]; |
3284 | |
3285 | if (sensor->type == type) { |
3286 | result = sensor->rate; |
3287 | break; |
3288 | } |
3289 | } |
3290 | } |
3291 | } |
3292 | SDL_UnlockJoysticks(); |
3293 | |
3294 | return result; |
3295 | } |
3296 | |
3297 | /* |
3298 | * Get the current state of a gamepad sensor. |
3299 | */ |
3300 | bool SDL_GetGamepadSensorData(SDL_Gamepad *gamepad, SDL_SensorType type, float *data, int num_values) |
3301 | { |
3302 | SDL_LockJoysticks(); |
3303 | { |
3304 | SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad); |
3305 | if (joystick) { |
3306 | int i; |
3307 | for (i = 0; i < joystick->nsensors; ++i) { |
3308 | SDL_JoystickSensorInfo *sensor = &joystick->sensors[i]; |
3309 | |
3310 | if (sensor->type == type) { |
3311 | num_values = SDL_min(num_values, SDL_arraysize(sensor->data)); |
3312 | SDL_memcpy(data, sensor->data, num_values * sizeof(*data)); |
3313 | SDL_UnlockJoysticks(); |
3314 | return true; |
3315 | } |
3316 | } |
3317 | } |
3318 | } |
3319 | SDL_UnlockJoysticks(); |
3320 | |
3321 | return SDL_Unsupported(); |
3322 | } |
3323 | |
3324 | SDL_JoystickID SDL_GetGamepadID(SDL_Gamepad *gamepad) |
3325 | { |
3326 | SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad); |
3327 | |
3328 | if (!joystick) { |
3329 | return 0; |
3330 | } |
3331 | return SDL_GetJoystickID(joystick); |
3332 | } |
3333 | |
3334 | SDL_PropertiesID SDL_GetGamepadProperties(SDL_Gamepad *gamepad) |
3335 | { |
3336 | SDL_PropertiesID result = 0; |
3337 | |
3338 | SDL_LockJoysticks(); |
3339 | { |
3340 | CHECK_GAMEPAD_MAGIC(gamepad, 0); |
3341 | |
3342 | result = SDL_GetJoystickProperties(gamepad->joystick); |
3343 | } |
3344 | SDL_UnlockJoysticks(); |
3345 | |
3346 | return result; |
3347 | } |
3348 | |
3349 | const char *SDL_GetGamepadName(SDL_Gamepad *gamepad) |
3350 | { |
3351 | const char *result = NULL; |
3352 | |
3353 | SDL_LockJoysticks(); |
3354 | { |
3355 | CHECK_GAMEPAD_MAGIC(gamepad, NULL); |
3356 | |
3357 | if (SDL_strcmp(gamepad->name, "*" ) == 0 || |
3358 | gamepad->joystick->steam_handle != 0) { |
3359 | result = SDL_GetJoystickName(gamepad->joystick); |
3360 | } else { |
3361 | result = SDL_GetPersistentString(gamepad->name); |
3362 | } |
3363 | } |
3364 | SDL_UnlockJoysticks(); |
3365 | |
3366 | return result; |
3367 | } |
3368 | |
3369 | const char *SDL_GetGamepadPath(SDL_Gamepad *gamepad) |
3370 | { |
3371 | SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad); |
3372 | |
3373 | if (!joystick) { |
3374 | return NULL; |
3375 | } |
3376 | return SDL_GetJoystickPath(joystick); |
3377 | } |
3378 | |
3379 | SDL_GamepadType SDL_GetGamepadType(SDL_Gamepad *gamepad) |
3380 | { |
3381 | SDL_GamepadType type; |
3382 | const SDL_SteamVirtualGamepadInfo *info; |
3383 | |
3384 | SDL_LockJoysticks(); |
3385 | { |
3386 | CHECK_GAMEPAD_MAGIC(gamepad, SDL_GAMEPAD_TYPE_UNKNOWN); |
3387 | |
3388 | info = SDL_GetJoystickVirtualGamepadInfoForID(gamepad->joystick->instance_id); |
3389 | if (info) { |
3390 | type = info->type; |
3391 | } else { |
3392 | type = gamepad->type; |
3393 | } |
3394 | } |
3395 | SDL_UnlockJoysticks(); |
3396 | |
3397 | return type; |
3398 | } |
3399 | |
3400 | SDL_GamepadType SDL_GetRealGamepadType(SDL_Gamepad *gamepad) |
3401 | { |
3402 | SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad); |
3403 | |
3404 | if (!joystick) { |
3405 | return SDL_GAMEPAD_TYPE_UNKNOWN; |
3406 | } |
3407 | return SDL_GetGamepadTypeFromGUID(SDL_GetJoystickGUID(joystick), SDL_GetJoystickName(joystick)); |
3408 | } |
3409 | |
3410 | int SDL_GetGamepadPlayerIndex(SDL_Gamepad *gamepad) |
3411 | { |
3412 | SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad); |
3413 | |
3414 | if (!joystick) { |
3415 | return -1; |
3416 | } |
3417 | return SDL_GetJoystickPlayerIndex(joystick); |
3418 | } |
3419 | |
3420 | /** |
3421 | * Set the player index of an opened gamepad |
3422 | */ |
3423 | bool SDL_SetGamepadPlayerIndex(SDL_Gamepad *gamepad, int player_index) |
3424 | { |
3425 | SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad); |
3426 | |
3427 | if (!joystick) { |
3428 | // SDL_SetError() will have been called already by SDL_GetGamepadJoystick() |
3429 | return false; |
3430 | } |
3431 | return SDL_SetJoystickPlayerIndex(joystick, player_index); |
3432 | } |
3433 | |
3434 | Uint16 SDL_GetGamepadVendor(SDL_Gamepad *gamepad) |
3435 | { |
3436 | SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad); |
3437 | |
3438 | if (!joystick) { |
3439 | return 0; |
3440 | } |
3441 | return SDL_GetJoystickVendor(joystick); |
3442 | } |
3443 | |
3444 | Uint16 SDL_GetGamepadProduct(SDL_Gamepad *gamepad) |
3445 | { |
3446 | SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad); |
3447 | |
3448 | if (!joystick) { |
3449 | return 0; |
3450 | } |
3451 | return SDL_GetJoystickProduct(joystick); |
3452 | } |
3453 | |
3454 | Uint16 SDL_GetGamepadProductVersion(SDL_Gamepad *gamepad) |
3455 | { |
3456 | SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad); |
3457 | |
3458 | if (!joystick) { |
3459 | return 0; |
3460 | } |
3461 | return SDL_GetJoystickProductVersion(joystick); |
3462 | } |
3463 | |
3464 | Uint16 SDL_GetGamepadFirmwareVersion(SDL_Gamepad *gamepad) |
3465 | { |
3466 | SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad); |
3467 | |
3468 | if (!joystick) { |
3469 | return 0; |
3470 | } |
3471 | return SDL_GetJoystickFirmwareVersion(joystick); |
3472 | } |
3473 | |
3474 | const char * SDL_GetGamepadSerial(SDL_Gamepad *gamepad) |
3475 | { |
3476 | SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad); |
3477 | |
3478 | if (!joystick) { |
3479 | return NULL; |
3480 | } |
3481 | return SDL_GetJoystickSerial(joystick); |
3482 | |
3483 | } |
3484 | |
3485 | Uint64 SDL_GetGamepadSteamHandle(SDL_Gamepad *gamepad) |
3486 | { |
3487 | Uint64 handle = 0; |
3488 | |
3489 | SDL_LockJoysticks(); |
3490 | { |
3491 | CHECK_GAMEPAD_MAGIC(gamepad, 0); |
3492 | |
3493 | handle = gamepad->joystick->steam_handle; |
3494 | } |
3495 | SDL_UnlockJoysticks(); |
3496 | |
3497 | return handle; |
3498 | } |
3499 | |
3500 | SDL_JoystickConnectionState SDL_GetGamepadConnectionState(SDL_Gamepad *gamepad) |
3501 | { |
3502 | SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad); |
3503 | |
3504 | if (!joystick) { |
3505 | return SDL_JOYSTICK_CONNECTION_INVALID; |
3506 | } |
3507 | return SDL_GetJoystickConnectionState(joystick); |
3508 | } |
3509 | |
3510 | SDL_PowerState SDL_GetGamepadPowerInfo(SDL_Gamepad *gamepad, int *percent) |
3511 | { |
3512 | SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad); |
3513 | |
3514 | if (percent) { |
3515 | *percent = -1; |
3516 | } |
3517 | if (!joystick) { |
3518 | return SDL_POWERSTATE_ERROR; |
3519 | } |
3520 | return SDL_GetJoystickPowerInfo(joystick, percent); |
3521 | } |
3522 | |
3523 | /* |
3524 | * Return if the gamepad in question is currently attached to the system, |
3525 | * \return 0 if not plugged in, 1 if still present. |
3526 | */ |
3527 | bool SDL_GamepadConnected(SDL_Gamepad *gamepad) |
3528 | { |
3529 | SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad); |
3530 | |
3531 | if (!joystick) { |
3532 | return false; |
3533 | } |
3534 | return SDL_JoystickConnected(joystick); |
3535 | } |
3536 | |
3537 | /* |
3538 | * Get the joystick for this gamepad |
3539 | */ |
3540 | SDL_Joystick *SDL_GetGamepadJoystick(SDL_Gamepad *gamepad) |
3541 | { |
3542 | SDL_Joystick *joystick; |
3543 | |
3544 | SDL_LockJoysticks(); |
3545 | { |
3546 | CHECK_GAMEPAD_MAGIC(gamepad, NULL); |
3547 | |
3548 | joystick = gamepad->joystick; |
3549 | } |
3550 | SDL_UnlockJoysticks(); |
3551 | |
3552 | return joystick; |
3553 | } |
3554 | |
3555 | /* |
3556 | * Return the SDL_Gamepad associated with an instance id. |
3557 | */ |
3558 | SDL_Gamepad *SDL_GetGamepadFromID(SDL_JoystickID joyid) |
3559 | { |
3560 | SDL_Gamepad *gamepad; |
3561 | |
3562 | SDL_LockJoysticks(); |
3563 | gamepad = SDL_gamepads; |
3564 | while (gamepad) { |
3565 | if (gamepad->joystick->instance_id == joyid) { |
3566 | SDL_UnlockJoysticks(); |
3567 | return gamepad; |
3568 | } |
3569 | gamepad = gamepad->next; |
3570 | } |
3571 | SDL_UnlockJoysticks(); |
3572 | return NULL; |
3573 | } |
3574 | |
3575 | /** |
3576 | * Return the SDL_Gamepad associated with a player index. |
3577 | */ |
3578 | SDL_Gamepad *SDL_GetGamepadFromPlayerIndex(int player_index) |
3579 | { |
3580 | SDL_Gamepad *result = NULL; |
3581 | |
3582 | SDL_LockJoysticks(); |
3583 | { |
3584 | SDL_Joystick *joystick = SDL_GetJoystickFromPlayerIndex(player_index); |
3585 | if (joystick) { |
3586 | result = SDL_GetGamepadFromID(joystick->instance_id); |
3587 | } |
3588 | } |
3589 | SDL_UnlockJoysticks(); |
3590 | |
3591 | return result; |
3592 | } |
3593 | |
3594 | /* |
3595 | * Get the SDL joystick layer bindings for this gamepad |
3596 | */ |
3597 | SDL_GamepadBinding **SDL_GetGamepadBindings(SDL_Gamepad *gamepad, int *count) |
3598 | { |
3599 | SDL_GamepadBinding **bindings = NULL; |
3600 | |
3601 | if (count) { |
3602 | *count = 0; |
3603 | } |
3604 | |
3605 | SDL_LockJoysticks(); |
3606 | { |
3607 | CHECK_GAMEPAD_MAGIC(gamepad, NULL); |
3608 | |
3609 | size_t pointers_size = ((gamepad->num_bindings + 1) * sizeof(SDL_GamepadBinding *)); |
3610 | size_t elements_size = (gamepad->num_bindings * sizeof(SDL_GamepadBinding)); |
3611 | bindings = (SDL_GamepadBinding **)SDL_malloc(pointers_size + elements_size); |
3612 | if (bindings) { |
3613 | SDL_GamepadBinding *binding = (SDL_GamepadBinding *)((Uint8 *)bindings + pointers_size); |
3614 | int i; |
3615 | for (i = 0; i < gamepad->num_bindings; ++i, ++binding) { |
3616 | bindings[i] = binding; |
3617 | SDL_copyp(binding, &gamepad->bindings[i]); |
3618 | } |
3619 | bindings[i] = NULL; |
3620 | |
3621 | if (count) { |
3622 | *count = gamepad->num_bindings; |
3623 | } |
3624 | } |
3625 | } |
3626 | SDL_UnlockJoysticks(); |
3627 | |
3628 | return bindings; |
3629 | } |
3630 | |
3631 | bool SDL_RumbleGamepad(SDL_Gamepad *gamepad, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms) |
3632 | { |
3633 | SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad); |
3634 | |
3635 | if (!joystick) { |
3636 | return false; |
3637 | } |
3638 | return SDL_RumbleJoystick(joystick, low_frequency_rumble, high_frequency_rumble, duration_ms); |
3639 | } |
3640 | |
3641 | bool SDL_RumbleGamepadTriggers(SDL_Gamepad *gamepad, Uint16 left_rumble, Uint16 right_rumble, Uint32 duration_ms) |
3642 | { |
3643 | SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad); |
3644 | |
3645 | if (!joystick) { |
3646 | return false; |
3647 | } |
3648 | return SDL_RumbleJoystickTriggers(joystick, left_rumble, right_rumble, duration_ms); |
3649 | } |
3650 | |
3651 | bool SDL_SetGamepadLED(SDL_Gamepad *gamepad, Uint8 red, Uint8 green, Uint8 blue) |
3652 | { |
3653 | SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad); |
3654 | |
3655 | if (!joystick) { |
3656 | return false; |
3657 | } |
3658 | return SDL_SetJoystickLED(joystick, red, green, blue); |
3659 | } |
3660 | |
3661 | bool SDL_SendGamepadEffect(SDL_Gamepad *gamepad, const void *data, int size) |
3662 | { |
3663 | SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad); |
3664 | |
3665 | if (!joystick) { |
3666 | return false; |
3667 | } |
3668 | return SDL_SendJoystickEffect(joystick, data, size); |
3669 | } |
3670 | |
3671 | void SDL_CloseGamepad(SDL_Gamepad *gamepad) |
3672 | { |
3673 | SDL_Gamepad *gamepadlist, *gamepadlistprev; |
3674 | |
3675 | SDL_LockJoysticks(); |
3676 | |
3677 | if (!SDL_ObjectValid(gamepad, SDL_OBJECT_TYPE_GAMEPAD)) { |
3678 | SDL_UnlockJoysticks(); |
3679 | return; |
3680 | } |
3681 | |
3682 | // First decrement ref count |
3683 | if (--gamepad->ref_count > 0) { |
3684 | SDL_UnlockJoysticks(); |
3685 | return; |
3686 | } |
3687 | |
3688 | SDL_CloseJoystick(gamepad->joystick); |
3689 | |
3690 | gamepadlist = SDL_gamepads; |
3691 | gamepadlistprev = NULL; |
3692 | while (gamepadlist) { |
3693 | if (gamepad == gamepadlist) { |
3694 | if (gamepadlistprev) { |
3695 | // unlink this entry |
3696 | gamepadlistprev->next = gamepadlist->next; |
3697 | } else { |
3698 | SDL_gamepads = gamepad->next; |
3699 | } |
3700 | break; |
3701 | } |
3702 | gamepadlistprev = gamepadlist; |
3703 | gamepadlist = gamepadlist->next; |
3704 | } |
3705 | |
3706 | SDL_SetObjectValid(gamepad, SDL_OBJECT_TYPE_GAMEPAD, false); |
3707 | SDL_free(gamepad->bindings); |
3708 | SDL_free(gamepad->last_match_axis); |
3709 | SDL_free(gamepad->last_hat_mask); |
3710 | SDL_free(gamepad); |
3711 | |
3712 | SDL_UnlockJoysticks(); |
3713 | } |
3714 | |
3715 | /* |
3716 | * Quit the gamepad subsystem |
3717 | */ |
3718 | void SDL_QuitGamepads(void) |
3719 | { |
3720 | SDL_Gamepad *gamepad; |
3721 | |
3722 | SDL_LockJoysticks(); |
3723 | |
3724 | for (gamepad = SDL_gamepads; gamepad; gamepad = gamepad->next) { |
3725 | SDL_PrivateGamepadRemoved(gamepad->joystick->instance_id); |
3726 | } |
3727 | |
3728 | SDL_gamepads_initialized = false; |
3729 | |
3730 | SDL_RemoveEventWatch(SDL_GamepadEventWatcher, NULL); |
3731 | |
3732 | while (SDL_gamepads) { |
3733 | SDL_gamepads->ref_count = 1; |
3734 | SDL_CloseGamepad(SDL_gamepads); |
3735 | } |
3736 | |
3737 | SDL_UnlockJoysticks(); |
3738 | } |
3739 | |
3740 | void SDL_QuitGamepadMappings(void) |
3741 | { |
3742 | GamepadMapping_t *pGamepadMap; |
3743 | |
3744 | SDL_AssertJoysticksLocked(); |
3745 | |
3746 | while (s_pSupportedGamepads) { |
3747 | pGamepadMap = s_pSupportedGamepads; |
3748 | s_pSupportedGamepads = s_pSupportedGamepads->next; |
3749 | SDL_free(pGamepadMap->name); |
3750 | SDL_free(pGamepadMap->mapping); |
3751 | SDL_free(pGamepadMap); |
3752 | } |
3753 | |
3754 | SDL_FreeVIDPIDList(&SDL_allowed_gamepads); |
3755 | SDL_FreeVIDPIDList(&SDL_ignored_gamepads); |
3756 | |
3757 | if (s_gamepadInstanceIDs) { |
3758 | SDL_DestroyHashTable(s_gamepadInstanceIDs); |
3759 | s_gamepadInstanceIDs = NULL; |
3760 | } |
3761 | } |
3762 | |
3763 | /* |
3764 | * Event filter to transform joystick events into appropriate gamepad ones |
3765 | */ |
3766 | static void SDL_SendGamepadAxis(Uint64 timestamp, SDL_Gamepad *gamepad, SDL_GamepadAxis axis, Sint16 value) |
3767 | { |
3768 | SDL_AssertJoysticksLocked(); |
3769 | |
3770 | // translate the event, if desired |
3771 | if (SDL_EventEnabled(SDL_EVENT_GAMEPAD_AXIS_MOTION)) { |
3772 | SDL_Event event; |
3773 | event.type = SDL_EVENT_GAMEPAD_AXIS_MOTION; |
3774 | event.common.timestamp = timestamp; |
3775 | event.gaxis.which = gamepad->joystick->instance_id; |
3776 | event.gaxis.axis = axis; |
3777 | event.gaxis.value = value; |
3778 | SDL_PushEvent(&event); |
3779 | } |
3780 | } |
3781 | |
3782 | static void SDL_SendGamepadButton(Uint64 timestamp, SDL_Gamepad *gamepad, SDL_GamepadButton button, bool down) |
3783 | { |
3784 | SDL_Event event; |
3785 | |
3786 | SDL_AssertJoysticksLocked(); |
3787 | |
3788 | if (button == SDL_GAMEPAD_BUTTON_INVALID) { |
3789 | return; |
3790 | } |
3791 | |
3792 | if (down) { |
3793 | event.type = SDL_EVENT_GAMEPAD_BUTTON_DOWN; |
3794 | } else { |
3795 | event.type = SDL_EVENT_GAMEPAD_BUTTON_UP; |
3796 | } |
3797 | |
3798 | if (button == SDL_GAMEPAD_BUTTON_GUIDE) { |
3799 | Uint64 now = SDL_GetTicks(); |
3800 | if (down) { |
3801 | gamepad->guide_button_down = now; |
3802 | |
3803 | if (gamepad->joystick->delayed_guide_button) { |
3804 | // Skip duplicate press |
3805 | return; |
3806 | } |
3807 | } else { |
3808 | if (now < (gamepad->guide_button_down + SDL_MINIMUM_GUIDE_BUTTON_DELAY_MS)) { |
3809 | gamepad->joystick->delayed_guide_button = true; |
3810 | return; |
3811 | } |
3812 | gamepad->joystick->delayed_guide_button = false; |
3813 | } |
3814 | } |
3815 | |
3816 | // translate the event, if desired |
3817 | if (SDL_EventEnabled(event.type)) { |
3818 | event.common.timestamp = timestamp; |
3819 | event.gbutton.which = gamepad->joystick->instance_id; |
3820 | event.gbutton.button = button; |
3821 | event.gbutton.down = down; |
3822 | SDL_PushEvent(&event); |
3823 | } |
3824 | } |
3825 | |
3826 | static const Uint32 SDL_gamepad_event_list[] = { |
3827 | SDL_EVENT_GAMEPAD_AXIS_MOTION, |
3828 | SDL_EVENT_GAMEPAD_BUTTON_DOWN, |
3829 | SDL_EVENT_GAMEPAD_BUTTON_UP, |
3830 | SDL_EVENT_GAMEPAD_ADDED, |
3831 | SDL_EVENT_GAMEPAD_REMOVED, |
3832 | SDL_EVENT_GAMEPAD_REMAPPED, |
3833 | SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN, |
3834 | SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION, |
3835 | SDL_EVENT_GAMEPAD_TOUCHPAD_UP, |
3836 | SDL_EVENT_GAMEPAD_SENSOR_UPDATE, |
3837 | }; |
3838 | |
3839 | void SDL_SetGamepadEventsEnabled(bool enabled) |
3840 | { |
3841 | unsigned int i; |
3842 | |
3843 | for (i = 0; i < SDL_arraysize(SDL_gamepad_event_list); ++i) { |
3844 | SDL_SetEventEnabled(SDL_gamepad_event_list[i], enabled); |
3845 | } |
3846 | } |
3847 | |
3848 | bool SDL_GamepadEventsEnabled(void) |
3849 | { |
3850 | bool enabled = false; |
3851 | unsigned int i; |
3852 | |
3853 | for (i = 0; i < SDL_arraysize(SDL_gamepad_event_list); ++i) { |
3854 | enabled = SDL_EventEnabled(SDL_gamepad_event_list[i]); |
3855 | if (enabled) { |
3856 | break; |
3857 | } |
3858 | } |
3859 | return enabled; |
3860 | } |
3861 | |
3862 | void SDL_GamepadHandleDelayedGuideButton(SDL_Joystick *joystick) |
3863 | { |
3864 | SDL_Gamepad *gamepad; |
3865 | |
3866 | SDL_AssertJoysticksLocked(); |
3867 | |
3868 | for (gamepad = SDL_gamepads; gamepad; gamepad = gamepad->next) { |
3869 | if (gamepad->joystick == joystick) { |
3870 | SDL_SendGamepadButton(0, gamepad, SDL_GAMEPAD_BUTTON_GUIDE, false); |
3871 | |
3872 | // Make sure we send an update complete event for this change |
3873 | if (!gamepad->joystick->update_complete) { |
3874 | gamepad->joystick->update_complete = SDL_GetTicksNS(); |
3875 | } |
3876 | break; |
3877 | } |
3878 | } |
3879 | } |
3880 | |
3881 | const char *SDL_GetGamepadAppleSFSymbolsNameForButton(SDL_Gamepad *gamepad, SDL_GamepadButton button) |
3882 | { |
3883 | const char *result = NULL; |
3884 | #ifdef SDL_JOYSTICK_MFI |
3885 | const char *IOS_GetAppleSFSymbolsNameForButton(SDL_Gamepad *gamepad, SDL_GamepadButton button); |
3886 | |
3887 | SDL_LockJoysticks(); |
3888 | { |
3889 | CHECK_GAMEPAD_MAGIC(gamepad, NULL); |
3890 | |
3891 | result = IOS_GetAppleSFSymbolsNameForButton(gamepad, button); |
3892 | } |
3893 | SDL_UnlockJoysticks(); |
3894 | #endif |
3895 | return result; |
3896 | } |
3897 | |
3898 | const char *SDL_GetGamepadAppleSFSymbolsNameForAxis(SDL_Gamepad *gamepad, SDL_GamepadAxis axis) |
3899 | { |
3900 | const char *result = NULL; |
3901 | #ifdef SDL_JOYSTICK_MFI |
3902 | const char *IOS_GetAppleSFSymbolsNameForAxis(SDL_Gamepad *gamepad, SDL_GamepadAxis axis); |
3903 | |
3904 | SDL_LockJoysticks(); |
3905 | { |
3906 | CHECK_GAMEPAD_MAGIC(gamepad, NULL); |
3907 | |
3908 | result = IOS_GetAppleSFSymbolsNameForAxis(gamepad, axis); |
3909 | } |
3910 | SDL_UnlockJoysticks(); |
3911 | #endif |
3912 | return result; |
3913 | } |
3914 | |