1 | /** |
2 | * Copyright (c) 2006-2023 LOVE Development Team |
3 | * |
4 | * This software is provided 'as-is', without any express or implied |
5 | * warranty. In no event will the authors be held liable for any damages |
6 | * arising from the use of this software. |
7 | * |
8 | * Permission is granted to anyone to use this software for any purpose, |
9 | * including commercial applications, and to alter it and redistribute it |
10 | * freely, subject to the following restrictions: |
11 | * |
12 | * 1. The origin of this software must not be misrepresented; you must not |
13 | * claim that you wrote the original software. If you use this software |
14 | * in a product, an acknowledgment in the product documentation would be |
15 | * appreciated but is not required. |
16 | * 2. Altered source versions must be plainly marked as such, and must not be |
17 | * misrepresented as being the original software. |
18 | * 3. This notice may not be removed or altered from any source distribution. |
19 | **/ |
20 | |
21 | // LOVE |
22 | #include "common/config.h" |
23 | #include "Joystick.h" |
24 | #include "common/int.h" |
25 | |
26 | // C++ |
27 | #include <algorithm> |
28 | #include <limits> |
29 | |
30 | #ifndef SDL_TICKS_PASSED |
31 | #define SDL_TICKS_PASSED(A, B) ((Sint32)((B) - (A)) <= 0) |
32 | #endif |
33 | |
34 | namespace love |
35 | { |
36 | namespace joystick |
37 | { |
38 | namespace sdl |
39 | { |
40 | |
41 | Joystick::Joystick(int id) |
42 | : joyhandle(nullptr) |
43 | , controller(nullptr) |
44 | , haptic(nullptr) |
45 | , instanceid(-1) |
46 | , id(id) |
47 | , vibration() |
48 | { |
49 | } |
50 | |
51 | Joystick::Joystick(int id, int joyindex) |
52 | : joyhandle(nullptr) |
53 | , controller(nullptr) |
54 | , haptic(nullptr) |
55 | , instanceid(-1) |
56 | , id(id) |
57 | , vibration() |
58 | { |
59 | open(joyindex); |
60 | } |
61 | |
62 | Joystick::~Joystick() |
63 | { |
64 | close(); |
65 | } |
66 | |
67 | bool Joystick::open(int deviceindex) |
68 | { |
69 | close(); |
70 | |
71 | joyhandle = SDL_JoystickOpen(deviceindex); |
72 | |
73 | if (joyhandle) |
74 | { |
75 | instanceid = SDL_JoystickInstanceID(joyhandle); |
76 | |
77 | // SDL_JoystickGetGUIDString uses 32 bytes plus the null terminator. |
78 | char cstr[33]; |
79 | |
80 | SDL_JoystickGUID sdlguid = SDL_JoystickGetGUID(joyhandle); |
81 | SDL_JoystickGetGUIDString(sdlguid, cstr, (int) sizeof(cstr)); |
82 | |
83 | pguid = std::string(cstr); |
84 | |
85 | // See if SDL thinks this is a Game Controller. |
86 | openGamepad(deviceindex); |
87 | |
88 | // Prefer the Joystick name for consistency. |
89 | const char *joyname = SDL_JoystickName(joyhandle); |
90 | if (!joyname && controller) |
91 | joyname = SDL_GameControllerName(controller); |
92 | |
93 | if (joyname) |
94 | name = joyname; |
95 | } |
96 | |
97 | return isConnected(); |
98 | } |
99 | |
100 | void Joystick::close() |
101 | { |
102 | if (haptic) |
103 | SDL_HapticClose(haptic); |
104 | |
105 | if (controller) |
106 | SDL_GameControllerClose(controller); |
107 | |
108 | if (joyhandle) |
109 | SDL_JoystickClose(joyhandle); |
110 | |
111 | joyhandle = nullptr; |
112 | controller = nullptr; |
113 | haptic = nullptr; |
114 | instanceid = -1; |
115 | vibration = Vibration(); |
116 | } |
117 | |
118 | bool Joystick::isConnected() const |
119 | { |
120 | return joyhandle != nullptr && SDL_JoystickGetAttached(joyhandle); |
121 | } |
122 | |
123 | const char *Joystick::getName() const |
124 | { |
125 | return name.c_str(); |
126 | } |
127 | |
128 | int Joystick::getAxisCount() const |
129 | { |
130 | return isConnected() ? SDL_JoystickNumAxes(joyhandle) : 0; |
131 | } |
132 | |
133 | int Joystick::getButtonCount() const |
134 | { |
135 | return isConnected() ? SDL_JoystickNumButtons(joyhandle) : 0; |
136 | } |
137 | |
138 | int Joystick::getHatCount() const |
139 | { |
140 | return isConnected() ? SDL_JoystickNumHats(joyhandle) : 0; |
141 | } |
142 | |
143 | float Joystick::getAxis(int axisindex) const |
144 | { |
145 | if (!isConnected() || axisindex < 0 || axisindex >= getAxisCount()) |
146 | return 0; |
147 | |
148 | return clampval(((float) SDL_JoystickGetAxis(joyhandle, axisindex))/32768.0f); |
149 | } |
150 | |
151 | std::vector<float> Joystick::getAxes() const |
152 | { |
153 | std::vector<float> axes; |
154 | int count = getAxisCount(); |
155 | |
156 | if (!isConnected() || count <= 0) |
157 | return axes; |
158 | |
159 | axes.reserve(count); |
160 | |
161 | for (int i = 0; i < count; i++) |
162 | axes.push_back(clampval(((float) SDL_JoystickGetAxis(joyhandle, i))/32768.0f)); |
163 | |
164 | return axes; |
165 | } |
166 | |
167 | Joystick::Hat Joystick::getHat(int hatindex) const |
168 | { |
169 | Hat h = HAT_INVALID; |
170 | |
171 | if (!isConnected() || hatindex < 0 || hatindex >= getHatCount()) |
172 | return h; |
173 | |
174 | getConstant(SDL_JoystickGetHat(joyhandle, hatindex), h); |
175 | |
176 | return h; |
177 | } |
178 | |
179 | bool Joystick::isDown(const std::vector<int> &buttonlist) const |
180 | { |
181 | if (!isConnected()) |
182 | return false; |
183 | |
184 | int numbuttons = getButtonCount(); |
185 | |
186 | for (int button : buttonlist) |
187 | { |
188 | if (button < 0 || button >= numbuttons) |
189 | continue; |
190 | |
191 | if (SDL_JoystickGetButton(joyhandle, button) == 1) |
192 | return true; |
193 | } |
194 | |
195 | return false; |
196 | } |
197 | |
198 | bool Joystick::openGamepad(int deviceindex) |
199 | { |
200 | if (!SDL_IsGameController(deviceindex)) |
201 | return false; |
202 | |
203 | if (isGamepad()) |
204 | { |
205 | SDL_GameControllerClose(controller); |
206 | controller = nullptr; |
207 | } |
208 | |
209 | controller = SDL_GameControllerOpen(deviceindex); |
210 | return isGamepad(); |
211 | } |
212 | |
213 | bool Joystick::isGamepad() const |
214 | { |
215 | return controller != nullptr; |
216 | } |
217 | |
218 | float Joystick::getGamepadAxis(love::joystick::Joystick::GamepadAxis axis) const |
219 | { |
220 | if (!isConnected() || !isGamepad()) |
221 | return 0.f; |
222 | |
223 | SDL_GameControllerAxis sdlaxis; |
224 | if (!getConstant(axis, sdlaxis)) |
225 | return 0.f; |
226 | |
227 | Sint16 value = SDL_GameControllerGetAxis(controller, sdlaxis); |
228 | |
229 | return clampval((float) value / 32768.0f); |
230 | } |
231 | |
232 | bool Joystick::isGamepadDown(const std::vector<GamepadButton> &blist) const |
233 | { |
234 | if (!isConnected() || !isGamepad()) |
235 | return false; |
236 | |
237 | SDL_GameControllerButton sdlbutton; |
238 | |
239 | for (GamepadButton button : blist) |
240 | { |
241 | if (!getConstant(button, sdlbutton)) |
242 | continue; |
243 | |
244 | if (SDL_GameControllerGetButton(controller, sdlbutton) == 1) |
245 | return true; |
246 | } |
247 | |
248 | return false; |
249 | } |
250 | |
251 | Joystick::JoystickInput Joystick::getGamepadMapping(const GamepadInput &input) const |
252 | { |
253 | Joystick::JoystickInput jinput; |
254 | jinput.type = INPUT_TYPE_MAX_ENUM; |
255 | |
256 | if (!isGamepad()) |
257 | return jinput; |
258 | |
259 | SDL_GameControllerButtonBind sdlbind = {}; |
260 | sdlbind.bindType = SDL_CONTROLLER_BINDTYPE_NONE; |
261 | |
262 | SDL_GameControllerButton sdlbutton; |
263 | SDL_GameControllerAxis sdlaxis; |
264 | |
265 | switch (input.type) |
266 | { |
267 | case INPUT_TYPE_BUTTON: |
268 | if (getConstant(input.button, sdlbutton)) |
269 | sdlbind = SDL_GameControllerGetBindForButton(controller, sdlbutton); |
270 | break; |
271 | case INPUT_TYPE_AXIS: |
272 | if (getConstant(input.axis, sdlaxis)) |
273 | sdlbind = SDL_GameControllerGetBindForAxis(controller, sdlaxis); |
274 | break; |
275 | default: |
276 | break; |
277 | } |
278 | |
279 | switch (sdlbind.bindType) |
280 | { |
281 | case SDL_CONTROLLER_BINDTYPE_BUTTON: |
282 | jinput.type = INPUT_TYPE_BUTTON; |
283 | jinput.button = sdlbind.value.button; |
284 | break; |
285 | case SDL_CONTROLLER_BINDTYPE_AXIS: |
286 | jinput.type = INPUT_TYPE_AXIS; |
287 | jinput.axis = sdlbind.value.axis; |
288 | break; |
289 | case SDL_CONTROLLER_BINDTYPE_HAT: |
290 | if (getConstant(sdlbind.value.hat.hat_mask, jinput.hat.value)) |
291 | { |
292 | jinput.type = INPUT_TYPE_HAT; |
293 | jinput.hat.index = sdlbind.value.hat.hat; |
294 | } |
295 | break; |
296 | case SDL_CONTROLLER_BINDTYPE_NONE: |
297 | default: |
298 | break; |
299 | } |
300 | |
301 | return jinput; |
302 | } |
303 | |
304 | std::string Joystick::getGamepadMappingString() const |
305 | { |
306 | char *sdlmapping = nullptr; |
307 | |
308 | if (controller != nullptr) |
309 | sdlmapping = SDL_GameControllerMapping(controller); |
310 | |
311 | if (sdlmapping == nullptr) |
312 | { |
313 | SDL_JoystickGUID sdlguid = SDL_JoystickGetGUIDFromString(pguid.c_str()); |
314 | sdlmapping = SDL_GameControllerMappingForGUID(sdlguid); |
315 | } |
316 | |
317 | if (sdlmapping == nullptr) |
318 | return "" ; |
319 | |
320 | std::string mappingstr(sdlmapping); |
321 | SDL_free(sdlmapping); |
322 | |
323 | // Matches SDL_GameControllerAddMappingsFromRW. |
324 | if (mappingstr.find_last_of(',') != mappingstr.length() - 1) |
325 | mappingstr += "," ; |
326 | mappingstr += "platform:" + std::string(SDL_GetPlatform()); |
327 | |
328 | return mappingstr; |
329 | } |
330 | |
331 | void *Joystick::getHandle() const |
332 | { |
333 | return joyhandle; |
334 | } |
335 | |
336 | std::string Joystick::getGUID() const |
337 | { |
338 | // SDL2's GUIDs identify *classes* of devices, instead of unique devices. |
339 | return pguid; |
340 | } |
341 | |
342 | int Joystick::getInstanceID() const |
343 | { |
344 | return instanceid; |
345 | } |
346 | |
347 | int Joystick::getID() const |
348 | { |
349 | return id; |
350 | } |
351 | |
352 | void Joystick::getDeviceInfo(int &vendorID, int &productID, int &productVersion) const |
353 | { |
354 | #if SDL_VERSION_ATLEAST(2, 0, 6) |
355 | if (joyhandle != nullptr) |
356 | { |
357 | vendorID = SDL_JoystickGetVendor(joyhandle); |
358 | productID = SDL_JoystickGetProduct(joyhandle); |
359 | productVersion = SDL_JoystickGetProductVersion(joyhandle); |
360 | } |
361 | else |
362 | #endif |
363 | { |
364 | vendorID = 0; |
365 | productID = 0; |
366 | productVersion = 0; |
367 | } |
368 | } |
369 | |
370 | bool Joystick::checkCreateHaptic() |
371 | { |
372 | if (!isConnected()) |
373 | return false; |
374 | |
375 | if (!SDL_WasInit(SDL_INIT_HAPTIC) && SDL_InitSubSystem(SDL_INIT_HAPTIC) < 0) |
376 | return false; |
377 | |
378 | if (haptic && SDL_HapticIndex(haptic) != -1) |
379 | return true; |
380 | |
381 | if (haptic) |
382 | { |
383 | SDL_HapticClose(haptic); |
384 | haptic = nullptr; |
385 | } |
386 | |
387 | haptic = SDL_HapticOpenFromJoystick(joyhandle); |
388 | vibration = Vibration(); |
389 | |
390 | return haptic != nullptr; |
391 | } |
392 | |
393 | bool Joystick::isVibrationSupported() |
394 | { |
395 | #if SDL_VERSION_ATLEAST(2, 0, 18) |
396 | if (isConnected() && SDL_JoystickHasRumble(joyhandle) == SDL_TRUE) |
397 | return true; |
398 | #endif |
399 | |
400 | if (!checkCreateHaptic()) |
401 | return false; |
402 | |
403 | unsigned int features = SDL_HapticQuery(haptic); |
404 | |
405 | if ((features & SDL_HAPTIC_LEFTRIGHT) != 0) |
406 | return true; |
407 | |
408 | // Some gamepad drivers only support left/right motors via a custom effect. |
409 | if (isGamepad() && (features & SDL_HAPTIC_CUSTOM) != 0) |
410 | return true; |
411 | |
412 | // Test for simple sine wave support as a last resort. |
413 | if ((features & SDL_HAPTIC_SINE) != 0) |
414 | return true; |
415 | |
416 | return false; |
417 | } |
418 | |
419 | bool Joystick::runVibrationEffect() |
420 | { |
421 | if (vibration.id != -1) |
422 | { |
423 | if (SDL_HapticUpdateEffect(haptic, vibration.id, &vibration.effect) == 0) |
424 | { |
425 | if (SDL_HapticRunEffect(haptic, vibration.id, 1) == 0) |
426 | return true; |
427 | } |
428 | |
429 | // If the effect fails to update, we should destroy and re-create it. |
430 | SDL_HapticDestroyEffect(haptic, vibration.id); |
431 | vibration.id = -1; |
432 | } |
433 | |
434 | vibration.id = SDL_HapticNewEffect(haptic, &vibration.effect); |
435 | |
436 | if (vibration.id != -1 && SDL_HapticRunEffect(haptic, vibration.id, 1) == 0) |
437 | return true; |
438 | |
439 | return false; |
440 | } |
441 | |
442 | bool Joystick::setVibration(float left, float right, float duration) |
443 | { |
444 | left = std::min(std::max(left, 0.0f), 1.0f); |
445 | right = std::min(std::max(right, 0.0f), 1.0f); |
446 | |
447 | if (left == 0.0f && right == 0.0f) |
448 | return setVibration(); |
449 | |
450 | if (!isConnected()) |
451 | { |
452 | vibration.left = vibration.right = 0.0f; |
453 | vibration.endtime = SDL_HAPTIC_INFINITY; |
454 | return false; |
455 | } |
456 | |
457 | Uint32 length = SDL_HAPTIC_INFINITY; |
458 | if (duration >= 0.0f) |
459 | { |
460 | float maxduration = std::numeric_limits<Uint32>::max() / 1000.0f; |
461 | length = Uint32(std::min(duration, maxduration) * 1000); |
462 | } |
463 | |
464 | bool success = false; |
465 | |
466 | #if SDL_VERSION_ATLEAST(2, 0, 9) |
467 | if (SDL_JoystickRumble(joyhandle, (Uint16)(left * LOVE_UINT16_MAX), (Uint16)(right * LOVE_UINT16_MAX), length) == 0) |
468 | success = true; |
469 | #endif |
470 | |
471 | if (!success && !checkCreateHaptic()) |
472 | return false; |
473 | |
474 | unsigned int features = SDL_HapticQuery(haptic); |
475 | int axes = SDL_HapticNumAxes(haptic); |
476 | |
477 | if (!success && (features & SDL_HAPTIC_LEFTRIGHT) != 0) |
478 | { |
479 | memset(&vibration.effect, 0, sizeof(SDL_HapticEffect)); |
480 | vibration.effect.type = SDL_HAPTIC_LEFTRIGHT; |
481 | |
482 | vibration.effect.leftright.length = length; |
483 | vibration.effect.leftright.large_magnitude = Uint16(left * LOVE_UINT16_MAX); |
484 | vibration.effect.leftright.small_magnitude = Uint16(right * LOVE_UINT16_MAX); |
485 | |
486 | success = runVibrationEffect(); |
487 | } |
488 | |
489 | // Some gamepad drivers only give support for controlling individual motors |
490 | // through a custom FF effect. |
491 | if (!success && isGamepad() && (features & SDL_HAPTIC_CUSTOM) && axes == 2) |
492 | { |
493 | // NOTE: this may cause issues with drivers which support custom effects |
494 | // but aren't similar to https://github.com/d235j/360Controller . |
495 | |
496 | // Custom effect data is clamped to 0x7FFF in SDL. |
497 | vibration.data[0] = vibration.data[2] = Uint16(left * 0x7FFF); |
498 | vibration.data[1] = vibration.data[3] = Uint16(right * 0x7FFF); |
499 | |
500 | memset(&vibration.effect, 0, sizeof(SDL_HapticEffect)); |
501 | vibration.effect.type = SDL_HAPTIC_CUSTOM; |
502 | |
503 | vibration.effect.custom.length = length; |
504 | vibration.effect.custom.channels = 2; |
505 | vibration.effect.custom.period = 10; |
506 | vibration.effect.custom.samples = 2; |
507 | vibration.effect.custom.data = vibration.data; |
508 | |
509 | success = runVibrationEffect(); |
510 | } |
511 | |
512 | // Fall back to a simple sine wave if all else fails. This only supports a |
513 | // single strength value. |
514 | if (!success && (features & SDL_HAPTIC_SINE) != 0) |
515 | { |
516 | memset(&vibration.effect, 0, sizeof(SDL_HapticEffect)); |
517 | vibration.effect.type = SDL_HAPTIC_SINE; |
518 | |
519 | vibration.effect.periodic.length = length; |
520 | vibration.effect.periodic.period = 10; |
521 | |
522 | float strength = std::max(left, right); |
523 | vibration.effect.periodic.magnitude = Sint16(strength * 0x7FFF); |
524 | |
525 | success = runVibrationEffect(); |
526 | } |
527 | |
528 | if (success) |
529 | { |
530 | vibration.left = left; |
531 | vibration.right = right; |
532 | |
533 | if (length == SDL_HAPTIC_INFINITY) |
534 | vibration.endtime = SDL_HAPTIC_INFINITY; |
535 | else |
536 | vibration.endtime = SDL_GetTicks() + length; |
537 | } |
538 | else |
539 | { |
540 | vibration.left = vibration.right = 0.0f; |
541 | vibration.endtime = SDL_HAPTIC_INFINITY; |
542 | } |
543 | |
544 | return success; |
545 | } |
546 | |
547 | bool Joystick::setVibration() |
548 | { |
549 | bool success = false; |
550 | |
551 | #if SDL_VERSION_ATLEAST(2, 0, 9) |
552 | if (!success) |
553 | success = isConnected() && SDL_JoystickRumble(joyhandle, 0, 0, 0) == 0; |
554 | #endif |
555 | |
556 | if (!success && SDL_WasInit(SDL_INIT_HAPTIC) && haptic && SDL_HapticIndex(haptic) != -1) |
557 | success = (SDL_HapticStopEffect(haptic, vibration.id) == 0); |
558 | |
559 | if (success) |
560 | vibration.left = vibration.right = 0.0f; |
561 | |
562 | return success; |
563 | } |
564 | |
565 | void Joystick::getVibration(float &left, float &right) |
566 | { |
567 | if (vibration.endtime != SDL_HAPTIC_INFINITY) |
568 | { |
569 | // With some drivers, the effect physically stops at the right time, but |
570 | // SDL_HapticGetEffectStatus still thinks it's playing. So we explicitly |
571 | // stop it once it's done, just to be sure. |
572 | if (SDL_TICKS_PASSED(SDL_GetTicks(), vibration.endtime)) |
573 | { |
574 | setVibration(); |
575 | vibration.endtime = SDL_HAPTIC_INFINITY; |
576 | } |
577 | } |
578 | |
579 | // Check if the haptic effect has stopped playing. |
580 | int id = vibration.id; |
581 | if (!haptic || id == -1 || SDL_HapticGetEffectStatus(haptic, id) != 1) |
582 | vibration.left = vibration.right = 0.0f; |
583 | |
584 | left = vibration.left; |
585 | right = vibration.right; |
586 | } |
587 | |
588 | bool Joystick::getConstant(Uint8 in, Joystick::Hat &out) |
589 | { |
590 | return hats.find(in, out); |
591 | } |
592 | |
593 | bool Joystick::getConstant(Joystick::Hat in, Uint8 &out) |
594 | { |
595 | return hats.find(in, out); |
596 | } |
597 | |
598 | bool Joystick::getConstant(SDL_GameControllerAxis in, Joystick::GamepadAxis &out) |
599 | { |
600 | return gpAxes.find(in, out); |
601 | } |
602 | |
603 | bool Joystick::getConstant(Joystick::GamepadAxis in, SDL_GameControllerAxis &out) |
604 | { |
605 | return gpAxes.find(in, out); |
606 | } |
607 | |
608 | bool Joystick::getConstant(SDL_GameControllerButton in, Joystick::GamepadButton &out) |
609 | { |
610 | return gpButtons.find(in, out); |
611 | } |
612 | |
613 | bool Joystick::getConstant(Joystick::GamepadButton in, SDL_GameControllerButton &out) |
614 | { |
615 | return gpButtons.find(in, out); |
616 | } |
617 | |
618 | EnumMap<Joystick::Hat, Uint8, Joystick::HAT_MAX_ENUM>::Entry Joystick::hatEntries[] = |
619 | { |
620 | {Joystick::HAT_CENTERED, SDL_HAT_CENTERED}, |
621 | {Joystick::HAT_UP, SDL_HAT_UP}, |
622 | {Joystick::HAT_RIGHT, SDL_HAT_RIGHT}, |
623 | {Joystick::HAT_DOWN, SDL_HAT_DOWN}, |
624 | {Joystick::HAT_LEFT, SDL_HAT_LEFT}, |
625 | {Joystick::HAT_RIGHTUP, SDL_HAT_RIGHTUP}, |
626 | {Joystick::HAT_RIGHTDOWN, SDL_HAT_RIGHTDOWN}, |
627 | {Joystick::HAT_LEFTUP, SDL_HAT_LEFTUP}, |
628 | {Joystick::HAT_LEFTDOWN, SDL_HAT_LEFTDOWN}, |
629 | }; |
630 | |
631 | EnumMap<Joystick::Hat, Uint8, Joystick::HAT_MAX_ENUM> Joystick::hats(Joystick::hatEntries, sizeof(Joystick::hatEntries)); |
632 | |
633 | EnumMap<Joystick::GamepadAxis, SDL_GameControllerAxis, Joystick::GAMEPAD_AXIS_MAX_ENUM>::Entry Joystick::gpAxisEntries[] = |
634 | { |
635 | {Joystick::GAMEPAD_AXIS_LEFTX, SDL_CONTROLLER_AXIS_LEFTX}, |
636 | {Joystick::GAMEPAD_AXIS_LEFTY, SDL_CONTROLLER_AXIS_LEFTY}, |
637 | {Joystick::GAMEPAD_AXIS_RIGHTX, SDL_CONTROLLER_AXIS_RIGHTX}, |
638 | {Joystick::GAMEPAD_AXIS_RIGHTY, SDL_CONTROLLER_AXIS_RIGHTY}, |
639 | {Joystick::GAMEPAD_AXIS_TRIGGERLEFT, SDL_CONTROLLER_AXIS_TRIGGERLEFT}, |
640 | {Joystick::GAMEPAD_AXIS_TRIGGERRIGHT, SDL_CONTROLLER_AXIS_TRIGGERRIGHT}, |
641 | }; |
642 | |
643 | EnumMap<Joystick::GamepadAxis, SDL_GameControllerAxis, Joystick::GAMEPAD_AXIS_MAX_ENUM> Joystick::gpAxes(Joystick::gpAxisEntries, sizeof(Joystick::gpAxisEntries)); |
644 | |
645 | EnumMap<Joystick::GamepadButton, SDL_GameControllerButton, Joystick::GAMEPAD_BUTTON_MAX_ENUM>::Entry Joystick::gpButtonEntries[] = |
646 | { |
647 | {Joystick::GAMEPAD_BUTTON_A, SDL_CONTROLLER_BUTTON_A}, |
648 | {Joystick::GAMEPAD_BUTTON_B, SDL_CONTROLLER_BUTTON_B}, |
649 | {Joystick::GAMEPAD_BUTTON_X, SDL_CONTROLLER_BUTTON_X}, |
650 | {Joystick::GAMEPAD_BUTTON_Y, SDL_CONTROLLER_BUTTON_Y}, |
651 | {Joystick::GAMEPAD_BUTTON_BACK, SDL_CONTROLLER_BUTTON_BACK}, |
652 | {Joystick::GAMEPAD_BUTTON_GUIDE, SDL_CONTROLLER_BUTTON_GUIDE}, |
653 | {Joystick::GAMEPAD_BUTTON_START, SDL_CONTROLLER_BUTTON_START}, |
654 | {Joystick::GAMEPAD_BUTTON_LEFTSTICK, SDL_CONTROLLER_BUTTON_LEFTSTICK}, |
655 | {Joystick::GAMEPAD_BUTTON_RIGHTSTICK, SDL_CONTROLLER_BUTTON_RIGHTSTICK}, |
656 | {Joystick::GAMEPAD_BUTTON_LEFTSHOULDER, SDL_CONTROLLER_BUTTON_LEFTSHOULDER}, |
657 | {Joystick::GAMEPAD_BUTTON_RIGHTSHOULDER, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER}, |
658 | {Joystick::GAMEPAD_BUTTON_DPAD_UP, SDL_CONTROLLER_BUTTON_DPAD_UP}, |
659 | {Joystick::GAMEPAD_BUTTON_DPAD_DOWN, SDL_CONTROLLER_BUTTON_DPAD_DOWN}, |
660 | {Joystick::GAMEPAD_BUTTON_DPAD_LEFT, SDL_CONTROLLER_BUTTON_DPAD_LEFT}, |
661 | {Joystick::GAMEPAD_BUTTON_DPAD_RIGHT, SDL_CONTROLLER_BUTTON_DPAD_RIGHT}, |
662 | }; |
663 | |
664 | EnumMap<Joystick::GamepadButton, SDL_GameControllerButton, Joystick::GAMEPAD_BUTTON_MAX_ENUM> Joystick::gpButtons(Joystick::gpButtonEntries, sizeof(Joystick::gpButtonEntries)); |
665 | |
666 | } // sdl |
667 | } // joystick |
668 | } // love |
669 | |