1 /* RetroArch - A frontend for libretro.
2 * Copyright (C) 2010-2014 - Hans-Kristian Arntzen
3 * Copyright (C) 2011-2017 - Daniel De Matteis
4 * Copyright (C) 2014-2017 - Higor Euripedes
5 *
6 * RetroArch is free software: you can redistribute it and/or modify it under the terms
7 * of the GNU General Public License as published by the Free Software Found-
8 * ation, either version 3 of the License, or (at your option) any later version.
9 *
10 * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12 * PURPOSE. See the GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along with RetroArch.
15 * If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <stdint.h>
19
20 #include <compat/strl.h>
21
22 #include "SDL.h"
23
24 #include "../input_driver.h"
25
26 #include "../../tasks/tasks_internal.h"
27 #include "../../verbosity.h"
28
29 typedef struct _sdl_joypad
30 {
31 SDL_Joystick *joypad;
32 #ifdef HAVE_SDL2
33 SDL_GameController *controller;
34 SDL_Haptic *haptic;
35 int rumble_effect; /* -1 = not initialized, -2 = error/unsupported */
36 #endif
37 unsigned num_axes;
38 unsigned num_buttons;
39 unsigned num_hats;
40 #ifdef HAVE_SDL2
41 unsigned num_balls;
42 #endif
43 } sdl_joypad_t;
44
45 /* TODO/FIXME - static globals */
46 static sdl_joypad_t sdl_pads[MAX_USERS];
47 #ifdef HAVE_SDL2
48 static bool g_has_haptic = false;
49 #endif
50
sdl_joypad_name(unsigned pad)51 static const char *sdl_joypad_name(unsigned pad)
52 {
53 if (pad >= MAX_USERS)
54 return NULL;
55
56 #ifdef HAVE_SDL2
57 if (sdl_pads[pad].controller)
58 return SDL_GameControllerNameForIndex(pad);
59 return SDL_JoystickNameForIndex(pad);
60 #else
61 return SDL_JoystickName(pad);
62 #endif
63 }
64
sdl_pad_get_button(sdl_joypad_t * pad,unsigned button)65 static uint8_t sdl_pad_get_button(sdl_joypad_t *pad, unsigned button)
66 {
67 #ifdef HAVE_SDL2
68 /* TODO: see if a LUT like xinput_joypad.c's button_index_to_bitmap_code is needed. */
69 if (pad->controller)
70 return SDL_GameControllerGetButton(pad->controller, (SDL_GameControllerButton)button);
71 #endif
72 return SDL_JoystickGetButton(pad->joypad, button);
73 }
74
sdl_pad_get_hat(sdl_joypad_t * pad,unsigned hat)75 static uint8_t sdl_pad_get_hat(sdl_joypad_t *pad, unsigned hat)
76 {
77 #ifdef HAVE_SDL2
78 if (pad->controller)
79 return sdl_pad_get_button(pad, hat);
80 #endif
81 return SDL_JoystickGetHat(pad->joypad, hat);
82 }
83
sdl_pad_get_axis(sdl_joypad_t * pad,unsigned axis)84 static int16_t sdl_pad_get_axis(sdl_joypad_t *pad, unsigned axis)
85 {
86 #ifdef HAVE_SDL2
87 /* TODO: see if a rarch <-> sdl translation is needed. */
88 if (pad->controller)
89 return SDL_GameControllerGetAxis(pad->controller, (SDL_GameControllerAxis)axis);
90 #endif
91 return SDL_JoystickGetAxis(pad->joypad, axis);
92 }
93
sdl_pad_connect(unsigned id)94 static void sdl_pad_connect(unsigned id)
95 {
96 sdl_joypad_t *pad = (sdl_joypad_t*)&sdl_pads[id];
97 bool success = false;
98 int32_t product = 0;
99 int32_t vendor = 0;
100
101 #ifdef HAVE_SDL2
102 SDL_JoystickGUID guid;
103 uint16_t *guid_ptr = NULL;
104
105 if (SDL_IsGameController(id))
106 {
107 pad->controller = SDL_GameControllerOpen(id);
108 pad->joypad = SDL_GameControllerGetJoystick(pad->controller);
109
110 success = pad->joypad != NULL && pad->controller != NULL;
111 }
112 else
113 #endif
114 {
115 pad->joypad = SDL_JoystickOpen(id);
116 success = pad->joypad != NULL;
117 }
118
119 if (!success)
120 {
121 RARCH_ERR("[SDL]: Couldn't open joystick #%u: %s.\n", id, SDL_GetError());
122
123 if (pad->joypad)
124 SDL_JoystickClose(pad->joypad);
125
126 pad->joypad = NULL;
127
128 return;
129 }
130
131 #ifdef HAVE_SDL2
132 guid = SDL_JoystickGetGUID(pad->joypad);
133 guid_ptr = (uint16_t*)guid.data;
134 #ifdef __linux
135 vendor = guid_ptr[2];
136 product = guid_ptr[4];
137 #elif _WIN32
138 vendor = guid_ptr[0];
139 product = guid_ptr[1];
140 #endif
141 #endif
142
143 input_autoconfigure_connect(
144 sdl_joypad_name(id),
145 NULL,
146 sdl_joypad.ident,
147 id,
148 vendor,
149 product);
150
151 #ifdef HAVE_SDL2
152 if (pad->controller)
153 {
154 /* SDL_GameController internally supports all axis/button IDs, even if
155 * the controller's mapping does not have a binding for it.
156 *
157 * So, we can claim to support all axes/buttons, and when we try to poll
158 * an unbound ID, SDL simply returns the correct unpressed value.
159 *
160 * Note that, in addition to 0 trackballs, we also have 0 hats. This is
161 * because the d-pad is in the button list, as the last 4 enum entries.
162 *
163 * -flibit
164 */
165 pad->num_axes = SDL_CONTROLLER_AXIS_MAX;
166 pad->num_buttons = SDL_CONTROLLER_BUTTON_MAX;
167 pad->num_hats = 0;
168 pad->num_balls = 0;
169
170 /* SDL Device supports Game Controller API. */
171 }
172 else
173 {
174 pad->num_axes = SDL_JoystickNumAxes(pad->joypad);
175 pad->num_buttons = SDL_JoystickNumButtons(pad->joypad);
176 pad->num_hats = SDL_JoystickNumHats(pad->joypad);
177 pad->num_balls = SDL_JoystickNumBalls(pad->joypad);
178 }
179
180 pad->haptic = NULL;
181
182 if (g_has_haptic)
183 {
184 pad->haptic = SDL_HapticOpenFromJoystick(pad->joypad);
185
186 if (!pad->haptic)
187 RARCH_WARN("[SDL]: Couldn't open haptic device of the joypad #%u: %s\n",
188 id, SDL_GetError());
189 }
190
191 pad->rumble_effect = -1;
192
193 if (pad->haptic)
194 {
195 SDL_HapticEffect efx;
196 efx.type = SDL_HAPTIC_LEFTRIGHT;
197 efx.leftright.type = SDL_HAPTIC_LEFTRIGHT;
198 efx.leftright.large_magnitude = efx.leftright.small_magnitude = 0x4000;
199 efx.leftright.length = 5000;
200
201 if (SDL_HapticEffectSupported(pad->haptic, &efx) == SDL_FALSE)
202 {
203 pad->rumble_effect = -2;
204 RARCH_WARN("[SDL]: Device #%u does not support rumble.\n", id);
205 }
206 }
207 #else
208 pad->num_axes = SDL_JoystickNumAxes(pad->joypad);
209 pad->num_buttons = SDL_JoystickNumButtons(pad->joypad);
210 pad->num_hats = SDL_JoystickNumHats(pad->joypad);
211 #endif
212 }
213
sdl_pad_disconnect(unsigned id)214 static void sdl_pad_disconnect(unsigned id)
215 {
216 #ifdef HAVE_SDL2
217 if (sdl_pads[id].haptic)
218 SDL_HapticClose(sdl_pads[id].haptic);
219
220 if (sdl_pads[id].controller)
221 {
222 SDL_GameControllerClose(sdl_pads[id].controller);
223 input_autoconfigure_disconnect(id, sdl_joypad.ident);
224 }
225 else
226 #endif
227 if (sdl_pads[id].joypad)
228 {
229 SDL_JoystickClose(sdl_pads[id].joypad);
230 input_autoconfigure_disconnect(id, sdl_joypad.ident);
231 }
232
233 memset(&sdl_pads[id], 0, sizeof(sdl_pads[id]));
234 }
235
sdl_joypad_destroy(void)236 static void sdl_joypad_destroy(void)
237 {
238 unsigned i;
239 for (i = 0; i < MAX_USERS; i++)
240 sdl_pad_disconnect(i);
241
242 memset(sdl_pads, 0, sizeof(sdl_pads));
243 }
244
sdl_joypad_init(void * data)245 static void *sdl_joypad_init(void *data)
246 {
247 unsigned i, num_sticks;
248 #ifdef HAVE_SDL2
249 uint32_t subsystem = SDL_INIT_GAMECONTROLLER;
250 #else
251 uint32_t subsystem = SDL_INIT_JOYSTICK;
252 #endif
253 uint32_t sdl_subsystem_flags = SDL_WasInit(0);
254
255 /* Initialise joystick/controller subsystem, if required */
256 if (sdl_subsystem_flags == 0)
257 {
258 if (SDL_Init(subsystem) < 0)
259 return NULL;
260 }
261 else if ((sdl_subsystem_flags & subsystem) == 0)
262 {
263 if (SDL_InitSubSystem(subsystem) < 0)
264 return NULL;
265 }
266
267 #if HAVE_SDL2
268 g_has_haptic = false;
269
270 /* Initialise haptic subsystem, if required */
271 if ((sdl_subsystem_flags & SDL_INIT_HAPTIC) == 0)
272 {
273 if (SDL_InitSubSystem(SDL_INIT_HAPTIC) < 0)
274 RARCH_WARN("[SDL]: Failed to initialize haptic device support: %s\n",
275 SDL_GetError());
276 else
277 g_has_haptic = true;
278 }
279 else
280 g_has_haptic = true;
281 #endif
282
283 memset(sdl_pads, 0, sizeof(sdl_pads));
284
285 num_sticks = SDL_NumJoysticks();
286 if (num_sticks > MAX_USERS)
287 num_sticks = MAX_USERS;
288
289 for (i = 0; i < num_sticks; i++)
290 sdl_pad_connect(i);
291
292 #ifndef HAVE_SDL2
293 /* quit if no joypad is detected. */
294 num_sticks = 0;
295 for (i = 0; i < MAX_USERS; i++)
296 if (sdl_pads[i].joypad)
297 num_sticks++;
298
299 if (num_sticks == 0)
300 goto error;
301 #endif
302
303 return (void*)-1;
304
305 #ifndef HAVE_SDL2
306 error:
307 sdl_joypad_destroy();
308
309 return NULL;
310 #endif
311 }
312
sdl_joypad_button_state(sdl_joypad_t * pad,unsigned port,uint16_t joykey)313 static int16_t sdl_joypad_button_state(
314 sdl_joypad_t *pad,
315 unsigned port, uint16_t joykey)
316 {
317 unsigned hat_dir = GET_HAT_DIR(joykey);
318 /* Check hat. */
319 if (hat_dir)
320 {
321 uint8_t dir;
322 uint16_t hat = GET_HAT(joykey);
323
324 if (hat >= pad->num_hats)
325 return 0;
326
327 dir = sdl_pad_get_hat(pad, hat);
328
329 switch (hat_dir)
330 {
331 case HAT_UP_MASK:
332 return (dir & SDL_HAT_UP);
333 case HAT_DOWN_MASK:
334 return (dir & SDL_HAT_DOWN);
335 case HAT_LEFT_MASK:
336 return (dir & SDL_HAT_LEFT);
337 case HAT_RIGHT_MASK:
338 return (dir & SDL_HAT_RIGHT);
339 default:
340 break;
341 }
342 /* hat requested and no hat button down */
343 }
344 else if (joykey < pad->num_buttons)
345 return sdl_pad_get_button(pad, joykey);
346 return 0;
347 }
348
sdl_joypad_button(unsigned port,uint16_t joykey)349 static int16_t sdl_joypad_button(unsigned port, uint16_t joykey)
350 {
351 sdl_joypad_t *pad = (sdl_joypad_t*)&sdl_pads[port];
352 if (!pad || !pad->joypad)
353 return 0;
354 if (port >= DEFAULT_MAX_PADS)
355 return 0;
356 return sdl_joypad_button_state(pad, port, joykey);
357 }
358
sdl_joypad_axis_state(sdl_joypad_t * pad,unsigned port,uint32_t joyaxis)359 static int16_t sdl_joypad_axis_state(
360 sdl_joypad_t *pad,
361 unsigned port, uint32_t joyaxis)
362 {
363 if (AXIS_NEG_GET(joyaxis) < pad->num_axes)
364 {
365 int16_t val = sdl_pad_get_axis(pad, AXIS_NEG_GET(joyaxis));
366 /* -0x8000 can cause trouble if we later abs() it. */
367 if (val < -0x7fff)
368 return -0x7fff;
369 else if (val < 0)
370 return val;
371 }
372 else if (AXIS_POS_GET(joyaxis) < pad->num_axes)
373 {
374 int16_t val = sdl_pad_get_axis(pad, AXIS_POS_GET(joyaxis));
375 if (val > 0)
376 return val;
377 }
378
379 return 0;
380 }
381
sdl_joypad_axis(unsigned port,uint32_t joyaxis)382 static int16_t sdl_joypad_axis(unsigned port, uint32_t joyaxis)
383 {
384 sdl_joypad_t *pad = (sdl_joypad_t*)&sdl_pads[port];
385 if (!pad || !pad->joypad)
386 return false;
387 return sdl_joypad_axis_state(pad, port, joyaxis);
388 }
389
sdl_joypad_state(rarch_joypad_info_t * joypad_info,const struct retro_keybind * binds,unsigned port)390 static int16_t sdl_joypad_state(
391 rarch_joypad_info_t *joypad_info,
392 const struct retro_keybind *binds,
393 unsigned port)
394 {
395 unsigned i;
396 int16_t ret = 0;
397 uint16_t port_idx = joypad_info->joy_idx;
398 sdl_joypad_t *pad = (sdl_joypad_t*)&sdl_pads[port_idx];
399
400 if (!pad || !pad->joypad)
401 return 0;
402 if (port_idx >= DEFAULT_MAX_PADS)
403 return 0;
404
405 for (i = 0; i < RARCH_FIRST_CUSTOM_BIND; i++)
406 {
407 /* Auto-binds are per joypad, not per user. */
408 const uint64_t joykey = (binds[i].joykey != NO_BTN)
409 ? binds[i].joykey : joypad_info->auto_binds[i].joykey;
410 const uint32_t joyaxis = (binds[i].joyaxis != AXIS_NONE)
411 ? binds[i].joyaxis : joypad_info->auto_binds[i].joyaxis;
412 if (
413 (uint16_t)joykey != NO_BTN
414 && sdl_joypad_button_state(pad, port_idx, (uint16_t)joykey)
415 )
416 ret |= ( 1 << i);
417 else if (joyaxis != AXIS_NONE &&
418 ((float)abs(sdl_joypad_axis_state(pad, port_idx, joyaxis))
419 / 0x8000) > joypad_info->axis_threshold)
420 ret |= (1 << i);
421 }
422
423 return ret;
424 }
425
sdl_joypad_poll(void)426 static void sdl_joypad_poll(void)
427 {
428 #ifdef HAVE_SDL2
429 SDL_Event event;
430
431 SDL_PumpEvents();
432
433 while (SDL_PeepEvents(&event, 1,
434 SDL_GETEVENT, SDL_JOYDEVICEADDED, SDL_JOYDEVICEREMOVED) > 0)
435 {
436 switch (event.type)
437 {
438 case SDL_JOYDEVICEADDED:
439 sdl_pad_connect(event.jdevice.which);
440 break;
441 case SDL_JOYDEVICEREMOVED:
442 sdl_pad_disconnect(event.jdevice.which);
443 break;
444 }
445 }
446
447 SDL_FlushEvents(SDL_JOYAXISMOTION, SDL_CONTROLLERDEVICEREMAPPED);
448 #else
449 SDL_JoystickUpdate();
450 #endif
451 }
452
453 #ifdef HAVE_SDL2
sdl_joypad_set_rumble(unsigned pad,enum retro_rumble_effect effect,uint16_t strength)454 static bool sdl_joypad_set_rumble(unsigned pad, enum retro_rumble_effect effect, uint16_t strength)
455 {
456 SDL_HapticEffect efx;
457 sdl_joypad_t *joypad = (sdl_joypad_t*)&sdl_pads[pad];
458
459 memset(&efx, 0, sizeof(efx));
460
461 if (!joypad->joypad || !joypad->haptic)
462 return false;
463
464 efx.type = SDL_HAPTIC_LEFTRIGHT;
465 efx.leftright.type = SDL_HAPTIC_LEFTRIGHT;
466 efx.leftright.length = 5000;
467
468 switch (effect)
469 {
470 case RETRO_RUMBLE_STRONG:
471 efx.leftright.large_magnitude = strength;
472 break;
473 case RETRO_RUMBLE_WEAK:
474 efx.leftright.small_magnitude = strength;
475 break;
476 default:
477 return false;
478 }
479
480 if (joypad->rumble_effect == -1)
481 {
482 joypad->rumble_effect = SDL_HapticNewEffect(sdl_pads[pad].haptic, &efx);
483 if (joypad->rumble_effect < 0)
484 {
485 RARCH_WARN("[SDL]: Failed to create rumble effect for joypad %u: %s\n",
486 pad, SDL_GetError());
487 joypad->rumble_effect = -2;
488 return false;
489 }
490 }
491 else if (joypad->rumble_effect >= 0)
492 SDL_HapticUpdateEffect(joypad->haptic, joypad->rumble_effect, &efx);
493
494 if (joypad->rumble_effect < 0)
495 return false;
496
497 if (SDL_HapticRunEffect(joypad->haptic, joypad->rumble_effect, 1) < 0)
498 {
499 RARCH_WARN("[SDL]: Failed to set rumble effect on joypad %u: %s\n",
500 pad, SDL_GetError());
501 return false;
502 }
503
504 return true;
505 }
506 #endif
507
sdl_joypad_query_pad(unsigned pad)508 static bool sdl_joypad_query_pad(unsigned pad)
509 {
510 return pad < MAX_USERS && sdl_pads[pad].joypad;
511 }
512
513 input_device_driver_t sdl_joypad = {
514 sdl_joypad_init,
515 sdl_joypad_query_pad,
516 sdl_joypad_destroy,
517 sdl_joypad_button,
518 sdl_joypad_state,
519 NULL,
520 sdl_joypad_axis,
521 sdl_joypad_poll,
522 #ifdef HAVE_SDL2
523 sdl_joypad_set_rumble,
524 #else
525 NULL,
526 #endif
527 sdl_joypad_name,
528 #ifdef HAVE_SDL2
529 "sdl2",
530 #else
531 "sdl"
532 #endif
533 };
534