mirror of
https://github.com/libretro/RetroArch
synced 2025-03-29 13:20:30 +00:00
464 lines
14 KiB
C
464 lines
14 KiB
C
/* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <SDL/SDL.h>
|
|
|
|
#include <libretro.h>
|
|
|
|
#include "../input_driver.h"
|
|
|
|
#include "../../tasks/tasks_internal.h"
|
|
#include "../../verbosity.h"
|
|
|
|
/* Simple joypad driver designed to rationalise
|
|
* the bizarre keyboard/gamepad hybrid setup
|
|
* of OpenDingux devices */
|
|
|
|
#define SDL_DINGUX_JOYPAD_NAME "Dingux Gamepad"
|
|
|
|
typedef struct
|
|
{
|
|
SDL_Joystick *device;
|
|
uint16_t pad_state;
|
|
int16_t analog_state[2][2];
|
|
unsigned num_axes;
|
|
bool connected;
|
|
bool menu_toggle;
|
|
} dingux_joypad_t;
|
|
|
|
/* TODO/FIXME - global referenced outside */
|
|
extern uint64_t lifecycle_state;
|
|
|
|
static dingux_joypad_t dingux_joypad;
|
|
|
|
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;
|
|
|
|
/* 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);
|
|
|
|
/* '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 (joypad->device)
|
|
SDL_JoystickClose(joypad->device);
|
|
|
|
if (joypad->connected)
|
|
input_autoconfigure_disconnect(0, sdl_dingux_joypad.ident);
|
|
|
|
memset(joypad, 0, sizeof(dingux_joypad_t));
|
|
}
|
|
|
|
static void sdl_dingux_joypad_destroy(void)
|
|
{
|
|
SDL_Event event;
|
|
|
|
/* Disconnect joypad */
|
|
sdl_dingux_joypad_disconnect();
|
|
|
|
/* De-initialise joystick subsystem */
|
|
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
|
|
|
|
/* Flush out all pending events */
|
|
while (SDL_PollEvent(&event));
|
|
|
|
BIT64_CLEAR(lifecycle_state, RARCH_MENU_TOGGLE);
|
|
}
|
|
|
|
static void *sdl_dingux_joypad_init(void *data)
|
|
{
|
|
dingux_joypad_t *joypad = (dingux_joypad_t*)&dingux_joypad;
|
|
|
|
memset(joypad, 0, sizeof(dingux_joypad_t));
|
|
BIT64_CLEAR(lifecycle_state, RARCH_MENU_TOGGLE);
|
|
|
|
/* Initialise joystick subsystem */
|
|
if (SDL_WasInit(0) == 0)
|
|
{
|
|
if (SDL_Init(SDL_INIT_JOYSTICK) < 0)
|
|
return NULL;
|
|
}
|
|
else if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0)
|
|
return NULL;
|
|
|
|
/* 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 int16_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)
|
|
{
|
|
dingux_joypad_t *joypad = (dingux_joypad_t*)&dingux_joypad;
|
|
int val = 0;
|
|
int axis = -1;
|
|
bool is_neg = false;
|
|
bool is_pos = false;
|
|
|
|
if (port != 0)
|
|
return 0;
|
|
|
|
if (AXIS_NEG_GET(joyaxis) < 4)
|
|
{
|
|
axis = AXIS_NEG_GET(joyaxis);
|
|
is_neg = true;
|
|
}
|
|
else if (AXIS_POS_GET(joyaxis) < 4)
|
|
{
|
|
axis = AXIS_POS_GET(joyaxis);
|
|
is_pos = true;
|
|
}
|
|
else
|
|
return 0;
|
|
|
|
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 (is_neg && val > 0)
|
|
return 0;
|
|
else if (is_pos && val < 0)
|
|
return 0;
|
|
|
|
return val;
|
|
}
|
|
|
|
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;
|
|
const uint32_t joyaxis = (binds[i].joyaxis != AXIS_NONE)
|
|
? binds[i].joyaxis : joypad_info->auto_binds[i].joyaxis;
|
|
|
|
if ((uint16_t)joykey != NO_BTN &&
|
|
(joypad->pad_state & (1 << (uint16_t)joykey)))
|
|
ret |= (1 << i);
|
|
else if (joyaxis != AXIS_NONE &&
|
|
((float)abs(sdl_dingux_joypad_axis_state(port_idx, joyaxis))
|
|
/ 0x8000) > joypad_info->axis_threshold)
|
|
ret |= (1 << i);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void sdl_dingux_joypad_poll(void)
|
|
{
|
|
dingux_joypad_t *joypad = (dingux_joypad_t*)&dingux_joypad;
|
|
SDL_Event event;
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* 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
|
|
*/
|
|
while (SDL_PollEvent(&event))
|
|
{
|
|
switch (event.type)
|
|
{
|
|
case SDL_KEYDOWN:
|
|
switch (event.key.keysym.sym)
|
|
{
|
|
case SDLK_SPACE:
|
|
BIT16_SET(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_X);
|
|
break;
|
|
case SDLK_LCTRL:
|
|
BIT16_SET(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_A);
|
|
break;
|
|
case SDLK_LALT:
|
|
BIT16_SET(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_B);
|
|
break;
|
|
case SDLK_LSHIFT:
|
|
BIT16_SET(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_Y);
|
|
break;
|
|
case SDLK_TAB:
|
|
BIT16_SET(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_L);
|
|
break;
|
|
case SDLK_BACKSPACE:
|
|
BIT16_SET(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_R);
|
|
break;
|
|
case SDLK_PAGEUP:
|
|
BIT16_SET(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_L2);
|
|
break;
|
|
case SDLK_PAGEDOWN:
|
|
BIT16_SET(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_R2);
|
|
break;
|
|
case SDLK_ESCAPE:
|
|
BIT16_SET(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_SELECT);
|
|
break;
|
|
case SDLK_RETURN:
|
|
BIT16_SET(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_START);
|
|
break;
|
|
case SDLK_KP_DIVIDE:
|
|
BIT16_SET(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_L3);
|
|
break;
|
|
case SDLK_KP_PERIOD:
|
|
BIT16_SET(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_R3);
|
|
break;
|
|
case SDLK_UP:
|
|
BIT16_SET(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_UP);
|
|
break;
|
|
case SDLK_RIGHT:
|
|
BIT16_SET(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_RIGHT);
|
|
break;
|
|
case SDLK_DOWN:
|
|
BIT16_SET(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_DOWN);
|
|
break;
|
|
case SDLK_LEFT:
|
|
BIT16_SET(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_LEFT);
|
|
break;
|
|
case SDLK_HOME:
|
|
BIT64_SET(lifecycle_state, RARCH_MENU_TOGGLE);
|
|
joypad->menu_toggle = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case SDL_KEYUP:
|
|
switch (event.key.keysym.sym)
|
|
{
|
|
case SDLK_SPACE:
|
|
BIT16_CLEAR(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_X);
|
|
break;
|
|
case SDLK_LCTRL:
|
|
BIT16_CLEAR(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_A);
|
|
break;
|
|
case SDLK_LALT:
|
|
BIT16_CLEAR(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_B);
|
|
break;
|
|
case SDLK_LSHIFT:
|
|
BIT16_CLEAR(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_Y);
|
|
break;
|
|
case SDLK_TAB:
|
|
BIT16_CLEAR(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_L);
|
|
break;
|
|
case SDLK_BACKSPACE:
|
|
BIT16_CLEAR(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_R);
|
|
break;
|
|
case SDLK_PAGEUP:
|
|
BIT16_CLEAR(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_L2);
|
|
break;
|
|
case SDLK_PAGEDOWN:
|
|
BIT16_CLEAR(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_R2);
|
|
break;
|
|
case SDLK_ESCAPE:
|
|
BIT16_CLEAR(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_SELECT);
|
|
break;
|
|
case SDLK_RETURN:
|
|
BIT16_CLEAR(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_START);
|
|
break;
|
|
case SDLK_KP_DIVIDE:
|
|
BIT16_CLEAR(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_L3);
|
|
break;
|
|
case SDLK_KP_PERIOD:
|
|
BIT16_CLEAR(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_R3);
|
|
break;
|
|
case SDLK_UP:
|
|
BIT16_CLEAR(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_UP);
|
|
break;
|
|
case SDLK_RIGHT:
|
|
BIT16_CLEAR(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_RIGHT);
|
|
break;
|
|
case SDLK_DOWN:
|
|
BIT16_CLEAR(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_DOWN);
|
|
break;
|
|
case SDLK_LEFT:
|
|
BIT16_CLEAR(joypad->pad_state, RETRO_DEVICE_ID_JOYPAD_LEFT);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
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,
|
|
NULL, /* set_rumble */
|
|
sdl_dingux_joypad_name,
|
|
"sdl_dingux",
|
|
};
|