Add support for RETRO_DEVICE_ANALOG.

This commit is contained in:
Themaister 2012-06-28 17:57:50 +02:00
parent 32383f168b
commit c440c7a50a
10 changed files with 331 additions and 151 deletions

View File

@ -329,6 +329,15 @@ static const struct snes_keybind snes_keybinds_1[] = {
{ true, RETRO_DEVICE_ID_JOYPAD_L3, SK_UNKNOWN, NO_BTN, AXIS_NONE },
{ true, RETRO_DEVICE_ID_JOYPAD_R3, SK_UNKNOWN, NO_BTN, AXIS_NONE },
{ true, RARCH_ANALOG_LEFT_X_PLUS, SK_UNKNOWN, NO_BTN, AXIS_NONE },
{ true, RARCH_ANALOG_LEFT_X_MINUS, SK_UNKNOWN, NO_BTN, AXIS_NONE },
{ true, RARCH_ANALOG_LEFT_Y_PLUS, SK_UNKNOWN, NO_BTN, AXIS_NONE },
{ true, RARCH_ANALOG_LEFT_Y_MINUS, SK_UNKNOWN, NO_BTN, AXIS_NONE },
{ true, RARCH_ANALOG_RIGHT_X_PLUS, SK_UNKNOWN, NO_BTN, AXIS_NONE },
{ true, RARCH_ANALOG_RIGHT_X_MINUS, SK_UNKNOWN, NO_BTN, AXIS_NONE },
{ true, RARCH_ANALOG_RIGHT_Y_PLUS, SK_UNKNOWN, NO_BTN, AXIS_NONE },
{ true, RARCH_ANALOG_RIGHT_Y_MINUS, SK_UNKNOWN, NO_BTN, AXIS_NONE },
{ true, RARCH_FAST_FORWARD_KEY, SK_SPACE, NO_BTN, AXIS_NONE },
{ true, RARCH_FAST_FORWARD_HOLD_KEY, SK_l, NO_BTN, AXIS_NONE },
{ true, RARCH_LOAD_STATE_KEY, SK_F4, NO_BTN, AXIS_NONE },
@ -374,6 +383,15 @@ static const struct snes_keybind snes_keybinds_rest[] = {
{ true, RETRO_DEVICE_ID_JOYPAD_R2, SK_UNKNOWN, NO_BTN, AXIS_NONE },
{ true, RETRO_DEVICE_ID_JOYPAD_L3, SK_UNKNOWN, NO_BTN, AXIS_NONE },
{ true, RETRO_DEVICE_ID_JOYPAD_R3, SK_UNKNOWN, NO_BTN, AXIS_NONE },
{ true, RARCH_ANALOG_LEFT_X_PLUS, SK_UNKNOWN, NO_BTN, AXIS_NONE },
{ true, RARCH_ANALOG_LEFT_X_MINUS, SK_UNKNOWN, NO_BTN, AXIS_NONE },
{ true, RARCH_ANALOG_LEFT_Y_PLUS, SK_UNKNOWN, NO_BTN, AXIS_NONE },
{ true, RARCH_ANALOG_LEFT_Y_MINUS, SK_UNKNOWN, NO_BTN, AXIS_NONE },
{ true, RARCH_ANALOG_RIGHT_X_PLUS, SK_UNKNOWN, NO_BTN, AXIS_NONE },
{ true, RARCH_ANALOG_RIGHT_X_MINUS, SK_UNKNOWN, NO_BTN, AXIS_NONE },
{ true, RARCH_ANALOG_RIGHT_Y_PLUS, SK_UNKNOWN, NO_BTN, AXIS_NONE },
{ true, RARCH_ANALOG_RIGHT_Y_MINUS, SK_UNKNOWN, NO_BTN, AXIS_NONE },
};
#endif

View File

@ -37,10 +37,27 @@
#define AUDIO_MAX_RATIO 16
// libretro has 16 buttons from 0-15 (libretro.h)
#define RARCH_FIRST_META_KEY 16
enum
// Analog binds use RETRO_DEVICE_ANALOG, but we follow the same scheme internally
// in RetroArch for simplicity,
// so they are mapped into [16, 23].
#define RARCH_FIRST_ANALOG_BIND 16
#define RARCH_FIRST_META_KEY RARCH_ANALOG_BIND_LIST_END
enum // RetroArch specific bind IDs.
{
RARCH_FAST_FORWARD_KEY = RARCH_FIRST_META_KEY,
// Analogs (RETRO_DEVICE_ANALOG)
RARCH_ANALOG_LEFT_X_PLUS = RARCH_FIRST_ANALOG_BIND,
RARCH_ANALOG_LEFT_X_MINUS,
RARCH_ANALOG_LEFT_Y_PLUS,
RARCH_ANALOG_LEFT_Y_MINUS,
RARCH_ANALOG_RIGHT_X_PLUS,
RARCH_ANALOG_RIGHT_X_MINUS,
RARCH_ANALOG_RIGHT_Y_PLUS,
RARCH_ANALOG_RIGHT_Y_MINUS,
RARCH_ANALOG_BIND_LIST_END,
// Command binds.
RARCH_FAST_FORWARD_KEY = RARCH_ANALOG_BIND_LIST_END,
RARCH_FAST_FORWARD_HOLD_KEY,
RARCH_LOAD_STATE_KEY,
RARCH_SAVE_STATE_KEY,

View File

@ -190,47 +190,60 @@ static bool dinput_joykey_pressed(sdl_dinput_t *di, unsigned port_num, uint16_t
return false;
}
static bool dinput_joyaxis_pressed(sdl_dinput_t *di, unsigned port_num, uint32_t joyaxis)
int16_t sdl_dinput_axis(sdl_dinput_t *di, unsigned port_num, const struct snes_keybind *key)
{
uint32_t joyaxis = key->joyaxis;
if (joyaxis == AXIS_NONE)
return 0;
int val = 0;
int axis = -1;
bool is_neg = false;
bool is_pos = false;
if (AXIS_NEG_GET(joyaxis) <= 5)
{
axis = AXIS_NEG_GET(joyaxis);
is_neg = true;
}
else if (AXIS_POS_GET(joyaxis) <= 5)
{
axis = AXIS_POS_GET(joyaxis);
is_pos = true;
}
switch (axis)
{
case 0: val = di->joy_state[port_num].lX; break;
case 1: val = di->joy_state[port_num].lY; break;
case 2: val = di->joy_state[port_num].lZ; break;
case 3: val = di->joy_state[port_num].lRx; break;
case 4: val = di->joy_state[port_num].lRy; break;
case 5: val = di->joy_state[port_num].lRz; break;
}
if (val < -0x7fff) // So abs() of -0x8000 can't mess us up.
val = -0x7fff;
if (is_neg && val > 0)
val = 0;
else if (is_pos && val < 0)
val = 0;
return val;
}
static bool dinput_joyaxis_pressed(sdl_dinput_t *di, unsigned port_num, const struct snes_keybind *key)
{
if (key->joyaxis == AXIS_NONE)
return false;
int min = -32678 * g_settings.input.axis_threshold;
int max = 32677 * g_settings.input.axis_threshold;
int min = 0x7fff * g_settings.input.axis_threshold;
switch (AXIS_NEG_GET(joyaxis))
{
case 0:
return di->joy_state[port_num].lX <= min;
case 1:
return di->joy_state[port_num].lY <= min;
case 2:
return di->joy_state[port_num].lZ <= min;
case 3:
return di->joy_state[port_num].lRx <= min;
case 4:
return di->joy_state[port_num].lRy <= min;
case 5:
return di->joy_state[port_num].lRz <= min;
}
switch (AXIS_POS_GET(joyaxis))
{
case 0:
return di->joy_state[port_num].lX >= max;
case 1:
return di->joy_state[port_num].lY >= max;
case 2:
return di->joy_state[port_num].lZ >= max;
case 3:
return di->joy_state[port_num].lRx >= max;
case 4:
return di->joy_state[port_num].lRy >= max;
case 5:
return di->joy_state[port_num].lRz >= max;
}
return false;
int16_t val = sdl_dinput_axis(di, port_num, key);
return abs(val) > min;
}
bool sdl_dinput_pressed(sdl_dinput_t *di, unsigned port_num, const struct snes_keybind *key)
@ -239,7 +252,7 @@ bool sdl_dinput_pressed(sdl_dinput_t *di, unsigned port_num, const struct snes_k
return false;
if (dinput_joykey_pressed(di, port_num, key->joykey))
return true;
if (dinput_joyaxis_pressed(di, port_num, key->joyaxis))
if (dinput_joyaxis_pressed(di, port_num, key))
return true;
return false;

View File

@ -40,6 +40,9 @@ void sdl_dinput_free(sdl_dinput_t *di);
bool sdl_dinput_pressed(sdl_dinput_t *di, unsigned port_num,
const struct snes_keybind *key);
int16_t sdl_dinput_axis(sdl_dinput_t *di, unsigned port_num,
const struct snes_keybind *key);
void sdl_dinput_poll(sdl_dinput_t *di);

View File

@ -223,27 +223,38 @@ static bool sdl_joykey_pressed(sdl_input_t *sdl, int port_num, uint16_t joykey)
}
}
static bool sdl_axis_pressed(sdl_input_t *sdl, int port_num, uint32_t joyaxis)
static int16_t sdl_axis_analog(sdl_input_t *sdl, unsigned port_num, uint32_t joyaxis)
{
if (joyaxis == AXIS_NONE)
return false;
return 0;
Sint16 val = 0;
if (AXIS_NEG_GET(joyaxis) < sdl->num_axes[port_num])
{
Sint16 val = SDL_JoystickGetAxis(sdl->joysticks[port_num], AXIS_NEG_GET(joyaxis));
float scaled = (float)val / 0x8000;
if (scaled < -g_settings.input.axis_threshold)
return true;
val = SDL_JoystickGetAxis(sdl->joysticks[port_num], AXIS_NEG_GET(joyaxis));
if (val > 0)
val = 0;
else if (val < -0x8000) // -0x8000 can cause trouble if we later abs() it.
val = -0x7fff;
}
if (AXIS_POS_GET(joyaxis) < sdl->num_axes[port_num])
else if (AXIS_POS_GET(joyaxis) < sdl->num_axes[port_num])
{
Sint16 val = SDL_JoystickGetAxis(sdl->joysticks[port_num], AXIS_POS_GET(joyaxis));
float scaled = (float)val / 0x8000;
if (scaled > g_settings.input.axis_threshold)
return true;
val = SDL_JoystickGetAxis(sdl->joysticks[port_num], AXIS_POS_GET(joyaxis));
if (val < 0)
val = 0;
}
return false;
return val;
}
static bool sdl_axis_pressed(sdl_input_t *sdl, unsigned port_num, uint32_t joyaxis)
{
int16_t val = sdl_axis_analog(sdl, port_num, joyaxis);
float scaled = (float)abs(val) / 0x8000;
return scaled > g_settings.input.axis_threshold;
}
#endif
@ -291,6 +302,69 @@ static int16_t sdl_joypad_device_state(sdl_input_t *sdl, const struct snes_keybi
return 0;
}
static void conv_analog_id_to_bind_id(unsigned index, unsigned id,
unsigned *id_minus, unsigned *id_plus)
{
switch ((index << 1) | id)
{
case (RETRO_DEVICE_INDEX_ANALOG_LEFT << 1) | RETRO_DEVICE_ID_ANALOG_X:
*id_minus = RARCH_ANALOG_LEFT_X_MINUS;
*id_plus = RARCH_ANALOG_LEFT_X_PLUS;
break;
case (RETRO_DEVICE_INDEX_ANALOG_LEFT << 1) | RETRO_DEVICE_ID_ANALOG_Y:
*id_minus = RARCH_ANALOG_LEFT_Y_MINUS;
*id_plus = RARCH_ANALOG_LEFT_Y_PLUS;
break;
case (RETRO_DEVICE_INDEX_ANALOG_RIGHT << 1) | RETRO_DEVICE_ID_ANALOG_X:
*id_minus = RARCH_ANALOG_RIGHT_X_MINUS;
*id_plus = RARCH_ANALOG_RIGHT_X_PLUS;
break;
case (RETRO_DEVICE_INDEX_ANALOG_RIGHT << 1) | RETRO_DEVICE_ID_ANALOG_Y:
*id_minus = RARCH_ANALOG_RIGHT_Y_MINUS;
*id_plus = RARCH_ANALOG_RIGHT_Y_PLUS;
break;
}
}
static int16_t sdl_analog_device_state(sdl_input_t *sdl, const struct snes_keybind **binds_,
unsigned port_num, unsigned index, unsigned id)
{
const struct snes_keybind *binds = binds_[port_num];
if (id >= RARCH_BIND_LIST_END)
return 0;
unsigned id_minus = 0;
unsigned id_plus = 0;
conv_analog_id_to_bind_id(index, id, &id_minus, &id_plus);
const struct snes_keybind *bind_minus = &binds[id_minus];
const struct snes_keybind *bind_plus = &binds[id_plus];
if (!bind_minus->valid || !bind_plus->valid)
return 0;
// A user might have bound minus axis to positive axis in SDL.
#ifdef HAVE_DINPUT
int16_t pressed_minus = abs(sdl_dinput_axis(sdl->di, port_num, bind_minus));
int16_t pressed_plus = abs(sdl_dinput_axis(sdl->di, port_num, bind_plus));
#else
int16_t pressed_minus = abs(sdl_axis_analog(sdl, port_num, bind_minus->joyaxis));
int16_t pressed_plus = abs(sdl_axis_analog(sdl, port_num, bind_plus->joyaxis));
#endif
int16_t res = pressed_plus - pressed_minus;
// TODO: Does it make sense to use axis thresholding here?
if (res != 0)
return res;
int16_t digital_left = sdl_is_pressed(sdl, port_num, bind_minus) ? -0x7fff : 0;
int16_t digital_right = sdl_is_pressed(sdl, port_num, bind_plus) ? 0x7fff : 0;
return digital_right + digital_left;
}
static int16_t sdl_mouse_device_state(sdl_input_t *sdl, unsigned id)
{
switch (id)
@ -338,6 +412,8 @@ static int16_t sdl_input_state(void *data_, const struct snes_keybind **binds, u
{
case RETRO_DEVICE_JOYPAD:
return sdl_joypad_device_state(data, binds, port, id);
case RETRO_DEVICE_ANALOG:
return sdl_analog_device_state(data, binds, port, index, id);
case RETRO_DEVICE_MOUSE:
return sdl_mouse_device_state(data, id);
case RETRO_DEVICE_LIGHTGUN:

View File

@ -197,6 +197,9 @@ static int16_t x_input_state(void *data, const struct snes_keybind **binds, unsi
return x_is_pressed(x11, binds[port], id) ||
input_sdl.input_state(x11->sdl, binds, port, device, index, id);
case RETRO_DEVICE_ANALOG:
return input_sdl.input_state(x11->sdl, binds, port, device, index, id);
default:
return 0;
}

View File

@ -115,6 +115,11 @@ static void update_input(void)
if (input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT))
dir_x++;
dir_x += input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X) / 2000;
dir_y += input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y) / 2000;
//dir_x += input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X) / 2000;
//dir_y += input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y) / 2000;
x_coord = (x_coord + dir_x) & 31;
y_coord = (y_coord + dir_y) & 31;
}

View File

@ -203,6 +203,19 @@
# input_player1_l3 =
# input_player1_r3 =
# Two analog sticks (DualShock-esque).
# Bound as usual, however, if a real analog axis is bound,
# it can be read as a true analog.
# Positive X axis is right, Positive Y axis is down.
# input_player1_l_x_plus =
# input_player1_l_x_minus =
# input_player1_l_y_plus =
# input_player1_l_y_minus =
# input_player1_r_x_plus =
# input_player1_r_x_minus =
# input_player1_r_y_plus =
# input_player1_r_y_minus =
# If desired, it is possible to override which joypads are being used for player 1 through 5. First joypad available is 0.
# input_player1_joypad_index = 0
# input_player2_joypad_index = 1

View File

@ -527,44 +527,35 @@ struct bind_map
#define DECLARE_BIND(x, bind) { true, "input_" #x, "input_" #x "_btn", "input_" #x "_axis", bind }
#define DECL_PLAYER(P) \
{ \
DECLARE_BIND(player##P##_b, RETRO_DEVICE_ID_JOYPAD_B), \
DECLARE_BIND(player##P##_y, RETRO_DEVICE_ID_JOYPAD_Y), \
DECLARE_BIND(player##P##_select, RETRO_DEVICE_ID_JOYPAD_SELECT), \
DECLARE_BIND(player##P##_start, RETRO_DEVICE_ID_JOYPAD_START), \
DECLARE_BIND(player##P##_up, RETRO_DEVICE_ID_JOYPAD_UP), \
DECLARE_BIND(player##P##_down, RETRO_DEVICE_ID_JOYPAD_DOWN), \
DECLARE_BIND(player##P##_left, RETRO_DEVICE_ID_JOYPAD_LEFT), \
DECLARE_BIND(player##P##_right, RETRO_DEVICE_ID_JOYPAD_RIGHT), \
DECLARE_BIND(player##P##_a, RETRO_DEVICE_ID_JOYPAD_A), \
DECLARE_BIND(player##P##_x, RETRO_DEVICE_ID_JOYPAD_X), \
DECLARE_BIND(player##P##_l, RETRO_DEVICE_ID_JOYPAD_L), \
DECLARE_BIND(player##P##_r, RETRO_DEVICE_ID_JOYPAD_R), \
DECLARE_BIND(player##P##_l2, RETRO_DEVICE_ID_JOYPAD_L2), \
DECLARE_BIND(player##P##_r2, RETRO_DEVICE_ID_JOYPAD_R2), \
DECLARE_BIND(player##P##_l3, RETRO_DEVICE_ID_JOYPAD_L3), \
DECLARE_BIND(player##P##_r3, RETRO_DEVICE_ID_JOYPAD_R3), \
}
DECLARE_BIND(player##P##_b, RETRO_DEVICE_ID_JOYPAD_B), \
DECLARE_BIND(player##P##_y, RETRO_DEVICE_ID_JOYPAD_Y), \
DECLARE_BIND(player##P##_select, RETRO_DEVICE_ID_JOYPAD_SELECT), \
DECLARE_BIND(player##P##_start, RETRO_DEVICE_ID_JOYPAD_START), \
DECLARE_BIND(player##P##_up, RETRO_DEVICE_ID_JOYPAD_UP), \
DECLARE_BIND(player##P##_down, RETRO_DEVICE_ID_JOYPAD_DOWN), \
DECLARE_BIND(player##P##_left, RETRO_DEVICE_ID_JOYPAD_LEFT), \
DECLARE_BIND(player##P##_right, RETRO_DEVICE_ID_JOYPAD_RIGHT), \
DECLARE_BIND(player##P##_a, RETRO_DEVICE_ID_JOYPAD_A), \
DECLARE_BIND(player##P##_x, RETRO_DEVICE_ID_JOYPAD_X), \
DECLARE_BIND(player##P##_l, RETRO_DEVICE_ID_JOYPAD_L), \
DECLARE_BIND(player##P##_r, RETRO_DEVICE_ID_JOYPAD_R), \
DECLARE_BIND(player##P##_l2, RETRO_DEVICE_ID_JOYPAD_L2), \
DECLARE_BIND(player##P##_r2, RETRO_DEVICE_ID_JOYPAD_R2), \
DECLARE_BIND(player##P##_l3, RETRO_DEVICE_ID_JOYPAD_L3), \
DECLARE_BIND(player##P##_r3, RETRO_DEVICE_ID_JOYPAD_R3), \
DECLARE_BIND(player##P##_l_x_plus, RARCH_ANALOG_LEFT_X_PLUS), \
DECLARE_BIND(player##P##_l_x_minus, RARCH_ANALOG_LEFT_X_MINUS), \
DECLARE_BIND(player##P##_l_y_plus, RARCH_ANALOG_LEFT_Y_PLUS), \
DECLARE_BIND(player##P##_l_y_minus, RARCH_ANALOG_LEFT_Y_MINUS), \
DECLARE_BIND(player##P##_r_x_plus, RARCH_ANALOG_RIGHT_X_PLUS), \
DECLARE_BIND(player##P##_r_x_minus, RARCH_ANALOG_RIGHT_X_MINUS), \
DECLARE_BIND(player##P##_r_y_plus, RARCH_ANALOG_RIGHT_Y_PLUS), \
DECLARE_BIND(player##P##_r_y_minus, RARCH_ANALOG_RIGHT_Y_MINUS)
// Big and nasty bind map... :)
static const struct bind_map bind_maps[MAX_PLAYERS][RARCH_BIND_LIST_END_NULL] = {
{
DECLARE_BIND(player1_b, RETRO_DEVICE_ID_JOYPAD_B),
DECLARE_BIND(player1_y, RETRO_DEVICE_ID_JOYPAD_Y),
DECLARE_BIND(player1_select, RETRO_DEVICE_ID_JOYPAD_SELECT),
DECLARE_BIND(player1_start, RETRO_DEVICE_ID_JOYPAD_START),
DECLARE_BIND(player1_up, RETRO_DEVICE_ID_JOYPAD_UP),
DECLARE_BIND(player1_down, RETRO_DEVICE_ID_JOYPAD_DOWN),
DECLARE_BIND(player1_left, RETRO_DEVICE_ID_JOYPAD_LEFT),
DECLARE_BIND(player1_right, RETRO_DEVICE_ID_JOYPAD_RIGHT),
DECLARE_BIND(player1_a, RETRO_DEVICE_ID_JOYPAD_A),
DECLARE_BIND(player1_x, RETRO_DEVICE_ID_JOYPAD_X),
DECLARE_BIND(player1_l, RETRO_DEVICE_ID_JOYPAD_L),
DECLARE_BIND(player1_r, RETRO_DEVICE_ID_JOYPAD_R),
DECLARE_BIND(player1_l2, RETRO_DEVICE_ID_JOYPAD_L2),
DECLARE_BIND(player1_r2, RETRO_DEVICE_ID_JOYPAD_R2),
DECLARE_BIND(player1_l3, RETRO_DEVICE_ID_JOYPAD_L3),
DECLARE_BIND(player1_r3, RETRO_DEVICE_ID_JOYPAD_R3),
DECL_PLAYER(1),
DECLARE_BIND(toggle_fast_forward, RARCH_FAST_FORWARD_KEY),
DECLARE_BIND(hold_fast_forward, RARCH_FAST_FORWARD_HOLD_KEY),
@ -593,13 +584,13 @@ static const struct bind_map bind_maps[MAX_PLAYERS][RARCH_BIND_LIST_END_NULL] =
DECLARE_BIND(slowmotion, RARCH_SLOWMOTION),
},
DECL_PLAYER(2),
DECL_PLAYER(3),
DECL_PLAYER(4),
DECL_PLAYER(5),
DECL_PLAYER(6),
DECL_PLAYER(7),
DECL_PLAYER(8),
{ DECL_PLAYER(2) },
{ DECL_PLAYER(3) },
{ DECL_PLAYER(4) },
{ DECL_PLAYER(5) },
{ DECL_PLAYER(6) },
{ DECL_PLAYER(7) },
{ DECL_PLAYER(8) },
};
struct key_map

View File

@ -60,53 +60,62 @@ struct bind
bool is_misc;
};
#define BIND(x, k) { x, { "input_player1_" #k "_btn", "input_player2_" #k "_btn", "input_player3_" #k "_btn", "input_player4_" #k "_btn", "input_player5_" #k "_btn" }, {"input_player1_" #k "_axis", "input_player2_" #k "_axis", "input_player3_" #k "_axis", "input_player4_" #k "_axis", "input_player5_" #k "_axis"}, false},
#define BIND(x, k) { x, { "input_player1_" #k "_btn", "input_player2_" #k "_btn", "input_player3_" #k "_btn", "input_player4_" #k "_btn", "input_player5_" #k "_btn" }, {"input_player1_" #k "_axis", "input_player2_" #k "_axis", "input_player3_" #k "_axis", "input_player4_" #k "_axis", "input_player5_" #k "_axis"}, false}
#define MISC_BIND(x, k) { x, { "input_" #k "_btn" }, { "input_" #k "_axis" }, true},
#define MISC_BIND(x, k) { x, { "input_" #k "_btn" }, { "input_" #k "_axis" }, true}
static struct bind binds[] = {
BIND("A button (right)", a)
BIND("B button (down)", b)
BIND("X button (top)", x)
BIND("Y button (left)", y)
BIND("L button (left shoulder)", l)
BIND("R button (right shoulder)", r)
BIND("L2 button (left shoulder #2)", l2)
BIND("R2 button (right shoulder #2)", r2)
BIND("L3 button (left analog button)", l3)
BIND("R3 button (right analog button)", r3)
BIND("Start button", start)
BIND("Select button", select)
BIND("Left D-pad", left)
BIND("Up D-pad", up)
BIND("Right D-pad", right)
BIND("Down D-pad", down)
BIND("A button (right)", a),
BIND("B button (down)", b),
BIND("X button (top)", x),
BIND("Y button (left)", y),
BIND("L button (left shoulder)", l),
BIND("R button (right shoulder)", r),
BIND("L2 button (left shoulder #2)", l2),
BIND("R2 button (right shoulder #2)", r2),
BIND("L3 button (left analog button)", l3),
BIND("R3 button (right analog button)", r3),
BIND("Start button", start),
BIND("Select button", select),
BIND("Left D-pad", left),
BIND("Up D-pad", up),
BIND("Right D-pad", right),
BIND("Down D-pad", down),
MISC_BIND("Save state", save_state)
MISC_BIND("Load state", load_state)
MISC_BIND("Exit emulator", exit_emulator)
MISC_BIND("Toggle fullscreen", toggle_fullscreen)
MISC_BIND("Save state slot increase", state_slot_increase)
MISC_BIND("Save state slot decrease", state_slot_decrease)
MISC_BIND("Toggle fast forward", toggle_fast_forward)
MISC_BIND("Hold fast forward", hold_fast_forward)
MISC_BIND("Audio input rate step up", rate_step_up)
MISC_BIND("Audio input rate step down", rate_step_down)
MISC_BIND("Rewind", rewind)
MISC_BIND("Movie recording toggle", movie_record_toggle)
MISC_BIND("Pause", pause_toggle)
MISC_BIND("Frame advance", frame_advance)
MISC_BIND("Reset", reset)
MISC_BIND("Next shader", shader_next)
MISC_BIND("Previous shader", shader_prev)
MISC_BIND("Toggle cheat on/off", cheat_toggle)
MISC_BIND("Cheat index plus", cheat_index_plus)
MISC_BIND("Cheat index minus", cheat_index_minus)
MISC_BIND("Screenshot", screenshot)
MISC_BIND("DSP config", dsp_config)
MISC_BIND("Audio mute/unmute", audio_mute)
MISC_BIND("Netplay player flip", netplay_flip_players)
MISC_BIND("Slow motion", slowmotion)
BIND("Left analog X+ (right)", l_x_plus),
BIND("Left analog Y+ (down)", l_y_plus),
BIND("Left analog X- (left)", l_x_minus),
BIND("Left analog Y- (up)", l_y_minus),
BIND("Right analog X+ (right)", r_x_plus),
BIND("Right analog Y+ (down)", r_y_plus),
BIND("Right analog X- (left)", r_x_minus),
BIND("Right analog Y- (up)", r_y_minus),
MISC_BIND("Save state", save_state),
MISC_BIND("Load state", load_state),
MISC_BIND("Exit emulator", exit_emulator),
MISC_BIND("Toggle fullscreen", toggle_fullscreen),
MISC_BIND("Save state slot increase", state_slot_increase),
MISC_BIND("Save state slot decrease", state_slot_decrease),
MISC_BIND("Toggle fast forward", toggle_fast_forward),
MISC_BIND("Hold fast forward", hold_fast_forward),
MISC_BIND("Audio input rate step up", rate_step_up),
MISC_BIND("Audio input rate step down", rate_step_down),
MISC_BIND("Rewind", rewind),
MISC_BIND("Movie recording toggle", movie_record_toggle),
MISC_BIND("Pause", pause_toggle),
MISC_BIND("Frame advance", frame_advance),
MISC_BIND("Reset", reset),
MISC_BIND("Next shader", shader_next),
MISC_BIND("Previous shader", shader_prev),
MISC_BIND("Toggle cheat on/off", cheat_toggle),
MISC_BIND("Cheat index plus", cheat_index_plus),
MISC_BIND("Cheat index minus", cheat_index_minus),
MISC_BIND("Screenshot", screenshot),
MISC_BIND("DSP config", dsp_config),
MISC_BIND("Audio mute/unmute", audio_mute),
MISC_BIND("Netplay player flip", netplay_flip_players),
MISC_BIND("Slow motion", slowmotion),
};
static void get_binds(config_file_t *conf, int player, int joypad)
@ -116,6 +125,7 @@ static void get_binds(config_file_t *conf, int player, int joypad)
fprintf(stderr, "Failed to init joystick subsystem.\n");
exit(1);
}
SDL_Joystick *joystick;
int num = SDL_NumJoysticks();
if (joypad >= num)
@ -131,8 +141,9 @@ static void get_binds(config_file_t *conf, int player, int joypad)
exit(1);
}
int last_axis = 0xFF;
int last_pos = 0;
int last_axis = -1;
bool block_axis = false;
int num_axes = SDL_JoystickNumAxes(joystick);
int *initial_axes = (int*)calloc(num_axes, sizeof(int));
assert(initial_axes);
@ -140,7 +151,20 @@ static void get_binds(config_file_t *conf, int player, int joypad)
SDL_PumpEvents();
SDL_JoystickUpdate();
for (int i = 0; i < num_axes; i++)
initial_axes[i] = SDL_JoystickGetAxis(joystick, i);
{
Sint16 initial = SDL_JoystickGetAxis(joystick, i);
if (abs(initial) < 20000)
initial = 0;
// Certain joypads (such as XBox360 controller on Linux) has a default negative axis for shoulder triggers,
// which makes configuration very awkward.
// If default negative, we can't trigger on the negative axis, and similar with defaulted positive axes.
if (initial)
fprintf(stderr, "Axis %d is defaulted to %s axis value of %d\n", i, initial > 0 ? "positive" : "negative", (int)initial);
initial_axes[i] = initial;
}
fprintf(stderr, "Configuring binds for player #%d on joypad #%d (%s)\n",
player + 1, joypad, SDL_JoystickName(joypad));
@ -168,27 +192,44 @@ static void get_binds(config_file_t *conf, int player, int joypad)
break;
case SDL_JOYAXISMOTION:
if ( // This is starting to look like Lisp. :D
(abs((int)event.jaxis.value - initial_axes[event.jaxis.axis]) > 20000) &&
(
(event.jaxis.axis != last_axis) ||
(
(abs(event.jaxis.value) > 20000) &&
(abs((int)event.jaxis.value - last_pos) > 20000)
)
)
)
{
bool same_axis = last_axis == event.jaxis.axis;
bool require_negative = initial_axes[event.jaxis.axis] > 0;
bool require_positive = initial_axes[event.jaxis.axis] < 0;
// Block the axis config until we're sure axes have returned to their neutral state.
if (same_axis)
{
if (abs(event.jaxis.value) < 10000 ||
(require_positive && event.jaxis.value < 0) ||
(require_negative && event.jaxis.value > 0))
block_axis = false;
}
// If axes are in their neutral state, we can't allow it.
if (require_negative && event.jaxis.value >= 0)
break;
if (require_positive && event.jaxis.value <= 0)
break;
if (block_axis)
break;
if (abs(event.jaxis.value) > 20000)
{
last_axis = event.jaxis.axis;
last_pos = event.jaxis.value;
fprintf(stderr, "\tJoyaxis moved: Axis %d, Value %d\n", (int)event.jaxis.axis, (int)event.jaxis.value);
done = true;
done = true;
block_axis = true;
char buf[8];
snprintf(buf, sizeof(buf), event.jaxis.value > 0 ? "+%d" : "-%d", event.jaxis.axis);
config_set_string(conf, binds[i].confaxis[player_index], buf);
}
break;
}
case SDL_KEYDOWN:
fprintf(stderr, ":V\n");