/* RetroArch - A frontend for libretro.
* Copyright (C) 2011-2020 - Daniel De Matteis
* Copyright (C) 2019-2020 - James Leaver
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see .
*/
#include
#include
#include
#include
#include "../input_driver.h"
#include "../../tasks/tasks_internal.h"
#include "../../verbosity.h"
#if defined(HAVE_LIBSHAKE)
#include
#include "../../configuration.h"
#include "../../config.def.h"
#endif
/* RS-90 devices:
* - Analog input: No
* - Menu button: No
* RetroFW devices:
* - Analog input: No
* - Menu button: Yes
* Miyoo devices:
* - Analog input: No
* - Menu button: Yes
* All other OpenDingux devices:
* - Analog input: Yes
* - Menu button: Yes
*/
#if !defined(RS90)
#if !(defined(MIYOO) || defined(RETROFW))
#define SDL_DINGUX_HAS_ANALOG 1
#endif
#define SDL_DINGUX_HAS_MENU_TOGGLE 1
#endif
/* Simple joypad driver designed to rationalise
* the bizarre keyboard/gamepad hybrid setup
* of OpenDingux devices */
#define SDL_DINGUX_JOYPAD_NAME "Dingux Gamepad"
/* All digital inputs map to keyboard keys:
* - X: SDLK_SPACE
* - A: SDLK_LCTRL
* - B: SDLK_LALT
* - Y: SDLK_LSHIFT
* - L: SDLK_TAB
* - R: SDLK_BACKSPACE
* - L2: SDLK_PAGEUP
* - R2: SDLK_PAGEDOWN
* - Select: SDLK_ESCAPE
* - Start: SDLK_RETURN
* - L3: SDLK_KP_DIVIDE
* - R3: SDLK_KP_PERIOD
* - Up: SDLK_UP
* - Right: SDLK_RIGHT
* - Down: SDLK_DOWN
* - Left: SDLK_LEFT
* - Menu: SDLK_HOME
*
* Miyoo devices (Pocketgo, Powkiddy V90 & Q90)
* have the following alternate mappings:
* - X: SDLK_LSHIFT
* - A: SDLK_LALT
* - B: SDLK_LCTRL
* - Y: SDLK_SPACE
* - Menu: SDLK_RCTRL
* - L3: SDLK_RALT
* - R3: SDLK_RSHIFT
*/
#if defined(MIYOO)
#define SDL_DINGUX_SDLK_X SDLK_LSHIFT
#define SDL_DINGUX_SDLK_A SDLK_LALT
#define SDL_DINGUX_SDLK_B SDLK_LCTRL
#define SDL_DINGUX_SDLK_Y SDLK_SPACE
#else
#define SDL_DINGUX_SDLK_X SDLK_SPACE
#define SDL_DINGUX_SDLK_A SDLK_LCTRL
#define SDL_DINGUX_SDLK_B SDLK_LALT
#define SDL_DINGUX_SDLK_Y SDLK_LSHIFT
#endif
#define SDL_DINGUX_SDLK_L SDLK_TAB
#define SDL_DINGUX_SDLK_R SDLK_BACKSPACE
#define SDL_DINGUX_SDLK_L2 SDLK_PAGEUP
#define SDL_DINGUX_SDLK_R2 SDLK_PAGEDOWN
#define SDL_DINGUX_SDLK_SELECT SDLK_ESCAPE
#define SDL_DINGUX_SDLK_START SDLK_RETURN
#if defined(MIYOO)
#define SDL_DINGUX_SDLK_L3 SDLK_RALT
#define SDL_DINGUX_SDLK_R3 SDLK_RSHIFT
#else
#define SDL_DINGUX_SDLK_L3 SDLK_KP_DIVIDE
#define SDL_DINGUX_SDLK_R3 SDLK_KP_PERIOD
#endif
#define SDL_DINGUX_SDLK_UP SDLK_UP
#define SDL_DINGUX_SDLK_RIGHT SDLK_RIGHT
#define SDL_DINGUX_SDLK_DOWN SDLK_DOWN
#define SDL_DINGUX_SDLK_LEFT SDLK_LEFT
#if defined(RETROFW)
#define SDL_DINGUX_SDLK_MENU SDLK_END
#elif defined(MIYOO)
#define SDL_DINGUX_SDLK_MENU SDLK_RCTRL
#else
#define SDL_DINGUX_SDLK_MENU SDLK_HOME
#endif
#if defined(HAVE_LIBSHAKE)
/* 5 ms period == 200 Hz
* > Meissner's Corpuscle registers this
* as 'fast' motion */
#define SDL_DINGUX_RUMBLE_WEAK_PERIOD 5
/* 142 ms period ~= 7 Hz
* > Merkel's Cells and Ruffini Ending register
* this as 'slow' motion */
#define SDL_DINGUX_RUMBLE_STRONG_PERIOD 142
typedef struct
{
int id;
uint16_t strength;
Shake_Effect effect;
bool active;
} dingux_joypad_rumble_effect_t;
typedef struct
{
Shake_Device *device;
dingux_joypad_rumble_effect_t weak;
dingux_joypad_rumble_effect_t strong;
} dingux_joypad_rumble_t;
#endif
typedef struct
{
#if defined(SDL_DINGUX_HAS_ANALOG)
SDL_Joystick *device;
#endif
#if defined(HAVE_LIBSHAKE)
dingux_joypad_rumble_t rumble;
#endif
unsigned num_axes;
uint16_t pad_state;
int16_t analog_state[2][2];
bool connected;
#if defined(SDL_DINGUX_HAS_MENU_TOGGLE)
bool menu_toggle;
#endif
} dingux_joypad_t;
#if defined(SDL_DINGUX_HAS_MENU_TOGGLE)
/* TODO/FIXME - global referenced outside */
extern uint64_t lifecycle_state;
#endif
static dingux_joypad_t dingux_joypad;
#if defined(HAVE_LIBSHAKE)
static bool sdl_dingux_rumble_init(dingux_joypad_rumble_t *rumble)
{
settings_t *settings = config_get_ptr();
unsigned rumble_gain = settings ? settings->uints.input_rumble_gain
: DEFAULT_RUMBLE_GAIN;
bool weak_uploaded = false;
bool strong_uploaded = false;
if (Shake_NumOfDevices() < 1)
goto error;
/* Open shake device */
rumble->device = Shake_Open(0);
if (!rumble->device)
goto error;
/* Check whether shake device has the required
* feature set */
if (!Shake_QueryEffectSupport(rumble->device, SHAKE_EFFECT_PERIODIC) ||
!Shake_QueryWaveformSupport(rumble->device, SHAKE_PERIODIC_SINE))
goto error;
/* In most cases it is recommended to use SHAKE_EFFECT_PERIODIC
* instead of SHAKE_EFFECT_RUMBLE. All devices that support
* SHAKE_EFFECT_RUMBLE support SHAKE_EFFECT_PERIODIC (square,
* triangle, sine) and vice versa */
/* Initialise weak rumble effect */
if (Shake_InitEffect(&rumble->weak.effect, SHAKE_EFFECT_PERIODIC) != SHAKE_OK)
goto error;
rumble->weak.effect.u.periodic.waveform = SHAKE_PERIODIC_SINE;
rumble->weak.effect.u.periodic.period = SDL_DINGUX_RUMBLE_WEAK_PERIOD;
rumble->weak.effect.u.periodic.magnitude = 0;
rumble->weak.id = Shake_UploadEffect(rumble->device, &rumble->weak.effect);
if (rumble->weak.id == SHAKE_ERROR)
goto error;
weak_uploaded = true;
/* Initialise strong rumble effect */
if (Shake_InitEffect(&rumble->strong.effect, SHAKE_EFFECT_PERIODIC) != SHAKE_OK)
goto error;
rumble->strong.effect.u.periodic.waveform = SHAKE_PERIODIC_SINE;
rumble->strong.effect.u.periodic.period = SDL_DINGUX_RUMBLE_STRONG_PERIOD;
rumble->strong.effect.u.periodic.magnitude = 0;
rumble->strong.id = Shake_UploadEffect(rumble->device, &rumble->strong.effect);
if (rumble->strong.id == SHAKE_ERROR)
goto error;
strong_uploaded = true;
/* Set gain, if supported */
if (Shake_QueryGainSupport(rumble->device))
if (Shake_SetGain(rumble->device, (int)rumble_gain) != SHAKE_OK)
goto error;
return true;
error:
RARCH_WARN("[libShake]: Input device does not support rumble effects.\n");
if (rumble->device)
{
if (weak_uploaded)
Shake_EraseEffect(rumble->device, rumble->weak.id);
if (strong_uploaded)
Shake_EraseEffect(rumble->device, rumble->strong.id);
Shake_Close(rumble->device);
rumble->device = NULL;
}
return false;
}
static bool sdl_dingux_rumble_update(Shake_Device *device,
dingux_joypad_rumble_effect_t *effect,
uint16_t strength, uint16_t max_strength)
{
/* If strength is zero, halt rumble effect */
if (strength == 0)
{
if (effect->active)
{
if (Shake_Stop(device, effect->id) == SHAKE_OK)
{
effect->active = false;
return true;
}
else
return false;
}
return true;
}
/* If strength has changed, update effect */
if (strength != effect->strength)
{
int id;
effect->effect.id = effect->id;
effect->effect.u.periodic.magnitude = (max_strength * strength) / 0xFFFF;
id = Shake_UploadEffect(device, &effect->effect);
if (id == SHAKE_ERROR)
return false;
effect->id = id;
effect->strength = strength;
}
/* If effect is currently idle, activate it */
if (!effect->active)
{
if (Shake_Play(device, effect->id) == SHAKE_OK)
{
effect->active = true;
return true;
}
else
return false;
}
return true;
}
static bool sdl_dingux_joypad_set_rumble(unsigned pad,
enum retro_rumble_effect effect, uint16_t strength)
{
dingux_joypad_t *joypad = (dingux_joypad_t*)&dingux_joypad;
if ((pad != 0) ||
!joypad->rumble.device)
return false;
switch (effect)
{
case RETRO_RUMBLE_STRONG:
return sdl_dingux_rumble_update(joypad->rumble.device,
&joypad->rumble.strong, strength,
SHAKE_RUMBLE_STRONG_MAGNITUDE_MAX);
case RETRO_RUMBLE_WEAK:
return sdl_dingux_rumble_update(joypad->rumble.device,
&joypad->rumble.weak, strength,
SHAKE_RUMBLE_WEAK_MAGNITUDE_MAX);
default:
break;
}
return false;
}
static bool sdl_dingux_joypad_set_rumble_gain(unsigned pad, unsigned gain)
{
dingux_joypad_t *joypad = (dingux_joypad_t*)&dingux_joypad;
if ((pad != 0) ||
!joypad->rumble.device)
return false;
/* Gain is automatically capped by Shake_SetGain(),
* but do it explicitly here for clarity */
if (gain > 100)
gain = 100;
/* Set gain */
if (Shake_QueryGainSupport(joypad->rumble.device))
if (Shake_SetGain(joypad->rumble.device, (int)gain) == SHAKE_OK)
return true;
return false;
}
#endif
static const char *sdl_dingux_joypad_name(unsigned port)
{
const char *joypad_name = NULL;
if (port != 0)
return NULL;
return SDL_DINGUX_JOYPAD_NAME;
}
static void sdl_dingux_joypad_connect(void)
{
dingux_joypad_t *joypad = (dingux_joypad_t*)&dingux_joypad;
#if defined(SDL_DINGUX_HAS_ANALOG)
/* Open joypad device */
if (SDL_NumJoysticks() > 0)
joypad->device = SDL_JoystickOpen(0);
/* If joypad exists, get number of axes */
if (joypad->device)
joypad->num_axes = SDL_JoystickNumAxes(joypad->device);
#endif
#if defined(HAVE_LIBSHAKE)
/* Configure rumble interface */
sdl_dingux_rumble_init(&joypad->rumble);
#endif
/* 'Register' joypad connection via
* autoconfig task */
input_autoconfigure_connect(
sdl_dingux_joypad_name(0), /* name */
NULL, /* display_name */
sdl_dingux_joypad.ident, /* driver */
0, /* port */
0, /* vid */
0); /* pid */
joypad->connected = true;
}
static void sdl_dingux_joypad_disconnect(void)
{
dingux_joypad_t *joypad = (dingux_joypad_t*)&dingux_joypad;
#if defined(SDL_DINGUX_HAS_ANALOG)
if (joypad->device)
SDL_JoystickClose(joypad->device);
#endif
if (joypad->connected)
input_autoconfigure_disconnect(0, sdl_dingux_joypad.ident);
#if defined(HAVE_LIBSHAKE)
if (joypad->rumble.device)
{
if (joypad->rumble.weak.active)
Shake_Stop(joypad->rumble.device, joypad->rumble.weak.id);
if (joypad->rumble.strong.active)
Shake_Stop(joypad->rumble.device, joypad->rumble.strong.id);
Shake_EraseEffect(joypad->rumble.device, joypad->rumble.weak.id);
Shake_EraseEffect(joypad->rumble.device, joypad->rumble.strong.id);
Shake_Close(joypad->rumble.device);
}
#endif
memset(joypad, 0, sizeof(dingux_joypad_t));
}
static void sdl_dingux_joypad_destroy(void)
{
SDL_Event event;
/* Disconnect joypad */
sdl_dingux_joypad_disconnect();
/* Flush out all pending events */
while (SDL_PollEvent(&event));
#if defined(HAVE_LIBSHAKE)
/* De-initialise rumble interface */
Shake_Quit();
#endif
#if defined(SDL_DINGUX_HAS_MENU_TOGGLE)
BIT64_CLEAR(lifecycle_state, RARCH_MENU_TOGGLE);
#endif
}
static void *sdl_dingux_joypad_init(void *data)
{
dingux_joypad_t *joypad = (dingux_joypad_t*)&dingux_joypad;
uint32_t sdl_subsystem_flags = SDL_WasInit(0);
memset(joypad, 0, sizeof(dingux_joypad_t));
#if defined(SDL_DINGUX_HAS_MENU_TOGGLE)
BIT64_CLEAR(lifecycle_state, RARCH_MENU_TOGGLE);
#endif
#if defined(SDL_DINGUX_HAS_ANALOG)
/* Initialise joystick subsystem, if required */
if (sdl_subsystem_flags == 0)
{
if (SDL_Init(SDL_INIT_JOYSTICK) < 0)
return NULL;
}
else if ((sdl_subsystem_flags & SDL_INIT_JOYSTICK) == 0)
{
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0)
return NULL;
}
#endif
#if defined(HAVE_LIBSHAKE)
/* Initialise rumble interface */
Shake_Init();
#endif
/* Connect joypad */
sdl_dingux_joypad_connect();
return (void*)-1;
}
static bool sdl_dingux_joypad_query_pad(unsigned port)
{
dingux_joypad_t *joypad = (dingux_joypad_t*)&dingux_joypad;
return (port == 0) && joypad->connected;
}
static int32_t sdl_dingux_joypad_button(unsigned port, uint16_t joykey)
{
dingux_joypad_t *joypad = (dingux_joypad_t*)&dingux_joypad;
if (port != 0)
return 0;
return (joypad->pad_state & (1 << joykey));
}
static void sdl_dingux_joypad_get_buttons(unsigned port, input_bits_t *state)
{
dingux_joypad_t *joypad = (dingux_joypad_t*)&dingux_joypad;
/* Macros require braces here... */
if (port == 0)
{
BITS_COPY16_PTR(state, joypad->pad_state);
}
else
{
BIT256_CLEAR_ALL_PTR(state);
}
}
static int16_t sdl_dingux_joypad_axis_state(unsigned port, uint32_t joyaxis)
{
#if defined(SDL_DINGUX_HAS_ANALOG)
if (port == 0)
{
dingux_joypad_t *joypad = (dingux_joypad_t*)&dingux_joypad;
if (AXIS_NEG_GET(joyaxis) < 4)
{
int16_t val = 0;
int16_t axis = AXIS_NEG_GET(joyaxis);
switch (axis)
{
case 0:
case 1:
val = joypad->analog_state[0][axis];
break;
case 2:
case 3:
val = joypad->analog_state[1][axis - 2];
break;
}
if (val < 0)
return val;
}
else if (AXIS_POS_GET(joyaxis) < 4)
{
int16_t val = 0;
int16_t axis = AXIS_POS_GET(joyaxis);
switch (axis)
{
case 0:
case 1:
val = joypad->analog_state[0][axis];
break;
case 2:
case 3:
val = joypad->analog_state[1][axis - 2];
break;
}
if (val > 0)
return val;
}
}
#endif
return 0;
}
static int16_t sdl_dingux_joypad_axis(unsigned port, uint32_t joyaxis)
{
if (port != 0)
return 0;
return sdl_dingux_joypad_axis_state(port, joyaxis);
}
static int16_t sdl_dingux_joypad_state(
rarch_joypad_info_t *joypad_info,
const struct retro_keybind *binds,
unsigned port)
{
dingux_joypad_t *joypad = (dingux_joypad_t*)&dingux_joypad;
uint16_t port_idx = joypad_info->joy_idx;
int16_t ret = 0;
size_t i;
if (port_idx != 0)
return 0;
for (i = 0; i < RARCH_FIRST_CUSTOM_BIND; i++)
{
/* Auto-binds are per joypad, not per user. */
const uint64_t joykey = (binds[i].joykey != NO_BTN)
? binds[i].joykey : joypad_info->auto_binds[i].joykey;
#if defined(SDL_DINGUX_HAS_ANALOG)
const uint32_t joyaxis = (binds[i].joyaxis != AXIS_NONE)
? binds[i].joyaxis : joypad_info->auto_binds[i].joyaxis;
#endif
if ((uint16_t)joykey != NO_BTN &&
(joypad->pad_state & (1 << (uint16_t)joykey)))
ret |= (1 << i);
#if defined(SDL_DINGUX_HAS_ANALOG)
else if (joyaxis != AXIS_NONE &&
((float)abs(sdl_dingux_joypad_axis_state(port_idx, joyaxis))
/ 0x8000) > joypad_info->axis_threshold)
ret |= (1 << i);
#endif
}
return ret;
}
static void sdl_dingux_joypad_poll(void)
{
dingux_joypad_t *joypad = (dingux_joypad_t*)&dingux_joypad;
SDL_Event event;
#if defined(SDL_DINGUX_HAS_MENU_TOGGLE)
/* Note: The menu toggle key is an awkward special
* case - the press/release events happen almost
* instantaneously, and since we only sample once
* per frame the input is often 'missed'.
* If the toggle key gets pressed, we therefore have
* to wait until the *next* frame to release it */
if (joypad->menu_toggle)
{
BIT64_CLEAR(lifecycle_state, RARCH_MENU_TOGGLE);
joypad->menu_toggle = false;
}
#endif
/* All digital inputs map to keyboard keys */
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_KEYDOWN:
switch (event.key.keysym.sym)
{
case SDL_DINGUX_SDLK_X:
BIT16_SET(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_X);
break;
case SDL_DINGUX_SDLK_A:
BIT16_SET(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_A);
break;
case SDL_DINGUX_SDLK_B:
BIT16_SET(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_B);
break;
case SDL_DINGUX_SDLK_Y:
BIT16_SET(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_Y);
break;
case SDL_DINGUX_SDLK_L:
BIT16_SET(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_L);
break;
case SDL_DINGUX_SDLK_R:
BIT16_SET(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_R);
break;
case SDL_DINGUX_SDLK_L2:
BIT16_SET(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_L2);
break;
case SDL_DINGUX_SDLK_R2:
BIT16_SET(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_R2);
break;
case SDL_DINGUX_SDLK_SELECT:
BIT16_SET(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_SELECT);
break;
case SDL_DINGUX_SDLK_START:
BIT16_SET(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_START);
break;
case SDL_DINGUX_SDLK_L3:
BIT16_SET(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_L3);
break;
case SDL_DINGUX_SDLK_R3:
BIT16_SET(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_R3);
break;
case SDL_DINGUX_SDLK_UP:
BIT16_SET(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_UP);
break;
case SDL_DINGUX_SDLK_RIGHT:
BIT16_SET(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_RIGHT);
break;
case SDL_DINGUX_SDLK_DOWN:
BIT16_SET(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_DOWN);
break;
case SDL_DINGUX_SDLK_LEFT:
BIT16_SET(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_LEFT);
break;
#if defined(SDL_DINGUX_HAS_MENU_TOGGLE)
case SDL_DINGUX_SDLK_MENU:
BIT64_SET(lifecycle_state, RARCH_MENU_TOGGLE);
joypad->menu_toggle = true;
break;
#endif
default:
break;
}
break;
case SDL_KEYUP:
switch (event.key.keysym.sym)
{
case SDL_DINGUX_SDLK_X:
BIT16_CLEAR(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_X);
break;
case SDL_DINGUX_SDLK_A:
BIT16_CLEAR(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_A);
break;
case SDL_DINGUX_SDLK_B:
BIT16_CLEAR(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_B);
break;
case SDL_DINGUX_SDLK_Y:
BIT16_CLEAR(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_Y);
break;
case SDL_DINGUX_SDLK_L:
BIT16_CLEAR(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_L);
break;
case SDL_DINGUX_SDLK_R:
BIT16_CLEAR(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_R);
break;
case SDL_DINGUX_SDLK_L2:
BIT16_CLEAR(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_L2);
break;
case SDL_DINGUX_SDLK_R2:
BIT16_CLEAR(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_R2);
break;
case SDL_DINGUX_SDLK_SELECT:
BIT16_CLEAR(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_SELECT);
break;
case SDL_DINGUX_SDLK_START:
BIT16_CLEAR(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_START);
break;
case SDL_DINGUX_SDLK_L3:
BIT16_CLEAR(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_L3);
break;
case SDL_DINGUX_SDLK_R3:
BIT16_CLEAR(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_R3);
break;
case SDL_DINGUX_SDLK_UP:
BIT16_CLEAR(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_UP);
break;
case SDL_DINGUX_SDLK_RIGHT:
BIT16_CLEAR(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_RIGHT);
break;
case SDL_DINGUX_SDLK_DOWN:
BIT16_CLEAR(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_DOWN);
break;
case SDL_DINGUX_SDLK_LEFT:
BIT16_CLEAR(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_LEFT);
break;
default:
break;
}
break;
default:
break;
}
}
#if defined(SDL_DINGUX_HAS_ANALOG)
/* Analog inputs come from the joypad device,
* if connected */
if (joypad->device)
{
int16_t axis_value;
SDL_JoystickUpdate();
if (joypad->num_axes > 0)
{
axis_value = SDL_JoystickGetAxis(joypad->device, 0);
/* -0x8000 can cause trouble if we later abs() it */
joypad->analog_state[RETRO_DEVICE_INDEX_ANALOG_LEFT][RETRO_DEVICE_ID_ANALOG_X] =
(axis_value < -0x7FFF) ? -0x7FFF : axis_value;
}
if (joypad->num_axes > 1)
{
axis_value = SDL_JoystickGetAxis(joypad->device, 1);
joypad->analog_state[RETRO_DEVICE_INDEX_ANALOG_LEFT][RETRO_DEVICE_ID_ANALOG_Y] =
(axis_value < -0x7FFF) ? -0x7FFF : axis_value;
}
if (joypad->num_axes > 2)
{
axis_value = SDL_JoystickGetAxis(joypad->device, 2);
joypad->analog_state[RETRO_DEVICE_INDEX_ANALOG_RIGHT][RETRO_DEVICE_ID_ANALOG_X] =
(axis_value < -0x7FFF) ? -0x7FFF : axis_value;
}
if (joypad->num_axes > 3)
{
axis_value = SDL_JoystickGetAxis(joypad->device, 3);
joypad->analog_state[RETRO_DEVICE_INDEX_ANALOG_RIGHT][RETRO_DEVICE_ID_ANALOG_Y] =
(axis_value < -0x7FFF) ? -0x7FFF : axis_value;
}
}
#endif
}
input_device_driver_t sdl_dingux_joypad = {
sdl_dingux_joypad_init,
sdl_dingux_joypad_query_pad,
sdl_dingux_joypad_destroy,
sdl_dingux_joypad_button,
sdl_dingux_joypad_state,
sdl_dingux_joypad_get_buttons,
sdl_dingux_joypad_axis,
sdl_dingux_joypad_poll,
#if defined(HAVE_LIBSHAKE)
sdl_dingux_joypad_set_rumble,
sdl_dingux_joypad_set_rumble_gain,
#else
NULL,
NULL,
#endif
NULL, /* set_sensor_state */
NULL, /* get_sensor_input */
sdl_dingux_joypad_name,
"sdl_dingux",
};