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