Merge branch 'joypad-autoconfig' of github.com:Themaister/RetroArch into joypad-autoconfig

This commit is contained in:
Themaister 2013-04-26 22:16:38 +02:00
commit 85e5bbde3d
17 changed files with 728 additions and 464 deletions

View File

@ -39,13 +39,14 @@ JOYCONFIG_OBJ = tools/retroarch-joyconfig.o \
conf/config_file.o \ conf/config_file.o \
file_path.o \ file_path.o \
compat/compat.o \ compat/compat.o \
input/input_common.o tools/input_common_joyconfig.o
RETROLAUNCH_OBJ = tools/retrolaunch/main.o \ RETROLAUNCH_OBJ = tools/retrolaunch/main.o \
tools/retrolaunch/sha1.o \ tools/retrolaunch/sha1.o \
tools/retrolaunch/parser.o \ tools/retrolaunch/parser.o \
tools/retrolaunch/cd_detect.o \ tools/retrolaunch/cd_detect.o \
tools/retrolaunch/rl_fnmatch.o \ tools/retrolaunch/rl_fnmatch.o \
tools/input_common_launch.o \
file_path.o \ file_path.o \
compat/compat.o \ compat/compat.o \
conf/config_file.o \ conf/config_file.o \
@ -371,7 +372,15 @@ tools/retrolaunch/retrolaunch: $(RETROLAUNCH_OBJ)
tools/linuxraw_joypad.o: input/linuxraw_joypad.c tools/linuxraw_joypad.o: input/linuxraw_joypad.c
@$(if $(Q), $(shell echo echo CC $<),) @$(if $(Q), $(shell echo echo CC $<),)
$(Q)$(CC) $(CFLAGS) $(DEFINES) -DNO_MSG_QUEUE -c -o $@ $< $(Q)$(CC) $(CFLAGS) $(DEFINES) -DIS_JOYCONFIG -c -o $@ $<
tools/input_common_launch.o: input/input_common.c
@$(if $(Q), $(shell echo echo CC $<),)
$(Q)$(CC) $(CFLAGS) $(DEFINES) -DIS_RETROLAUNCH -c -o $@ $<
tools/input_common_joyconfig.o: input/input_common.c
@$(if $(Q), $(shell echo echo CC $<),)
$(Q)$(CC) $(CFLAGS) $(DEFINES) -DIS_JOYCONFIG -c -o $@ $<
%.o: %.S config.h config.mk $(HEADERS) %.o: %.S config.h config.mk $(HEADERS)
@$(if $(Q), $(shell echo echo AS $<),) @$(if $(Q), $(shell echo echo AS $<),)

View File

@ -42,7 +42,7 @@ JOBJ := conf/config_file.o \
tools/retroarch-joyconfig.o \ tools/retroarch-joyconfig.o \
compat/compat.o \ compat/compat.o \
file_path.o \ file_path.o \
input/input_common.o \ tools/input_common_joyconfig.o \
input/dinput.o input/dinput.o
CC = gcc CC = gcc
@ -254,6 +254,10 @@ $(TARGET): $(OBJ)
@$(if $(Q), $(shell echo echo CC $<),) @$(if $(Q), $(shell echo echo CC $<),)
$(Q)$(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< $(Q)$(CC) $(CFLAGS) $(DEFINES) -c -o $@ $<
tools/input_common_joyconfig.o: input/input_common.c
@$(if $(Q), $(shell echo echo CC $<),)
$(Q)$(CC) $(CFLAGS) $(DEFINES) -DIS_JOYCONFIG -c -o $@ $<
%.o: %.cpp %.o: %.cpp
@$(if $(Q), $(shell echo echo CXX $<),) @$(if $(Q), $(shell echo echo CXX $<),)
$(Q)$(CXX) $(CXXFLAGS) $(DEFINES) -c -o $@ $< $(Q)$(CXX) $(CXXFLAGS) $(DEFINES) -c -o $@ $<

View File

@ -413,11 +413,9 @@ static const unsigned turbo_duty_cycle = 3;
// Enable input debugging output. // Enable input debugging output.
static const bool input_debug_enable = false; static const bool input_debug_enable = false;
#ifdef ANDROID
// Enable input auto-detection. Will attempt to autoconfigure // Enable input auto-detection. Will attempt to autoconfigure
// gamepads, plug-and-play style. // gamepads, plug-and-play style.
static const bool input_autodetect_enable = true; static const bool input_autodetect_enable = true;
#endif
#ifndef IS_SALAMANDER #ifndef IS_SALAMANDER

View File

@ -34,6 +34,10 @@ Use config file found in PATH as a base. Old configuration values can be overwri
\fB--output PATH, -o PATH\fR \fB--output PATH, -o PATH\fR
Writes the final config to a file, rather than \fBstdout\fR. If -i and -o point to the same file, the file found in PATH will simply be updated with the new configs. Writes the final config to a file, rather than \fBstdout\fR. If -i and -o point to the same file, the file found in PATH will simply be updated with the new configs.
.TP
\fB-a PATH, --autoconfig PATH\fR
Also writes an autoconfigure file for the joypad which was configured. It can be used by RetroArch to automatically configure a joypad when it's plugged in.
.TP .TP
\fB--misc, -m\fR \fB--misc, -m\fR
Also configure some binds that aren't directly related to RetroPad, such as save states, fullscreen toggling, etc. Also configure some binds that aren't directly related to RetroPad, such as save states, fullscreen toggling, etc.

View File

@ -217,6 +217,11 @@ struct settings
{ {
char driver[32]; char driver[32];
struct retro_keybind binds[MAX_PLAYERS][RARCH_BIND_LIST_END]; struct retro_keybind binds[MAX_PLAYERS][RARCH_BIND_LIST_END];
// Set by autoconfiguration in joypad_autoconfig_dir. Does not override main binds.
struct retro_keybind autoconf_binds[MAX_PLAYERS][RARCH_BIND_LIST_END];
bool autoconfigured[MAX_PLAYERS];
float axis_threshold; float axis_threshold;
int joypad_map[MAX_PLAYERS]; int joypad_map[MAX_PLAYERS];
unsigned device[MAX_PLAYERS]; unsigned device[MAX_PLAYERS];
@ -236,6 +241,8 @@ struct settings
char overlay[PATH_MAX]; char overlay[PATH_MAX];
float overlay_opacity; float overlay_opacity;
char autoconfig_dir[PATH_MAX];
} input; } input;
char core_options_path[PATH_MAX]; char core_options_path[PATH_MAX];

View File

@ -189,7 +189,7 @@ static bool dinput_is_pressed(struct dinput_input *di, const struct retro_keybin
return false; return false;
const struct retro_keybind *bind = &binds[id]; const struct retro_keybind *bind = &binds[id];
return dinput_keyboard_pressed(di, bind->key) || input_joypad_pressed(di->joypad, port, bind); return dinput_keyboard_pressed(di, bind->key) || input_joypad_pressed(di->joypad, port, binds, id);
} }
static bool dinput_key_pressed(void *data, int key) static bool dinput_key_pressed(void *data, int key)
@ -555,6 +555,13 @@ static bool dinput_joypad_query_pad(unsigned pad)
return pad < MAX_PLAYERS && g_pads[pad].joypad; return pad < MAX_PLAYERS && g_pads[pad].joypad;
} }
static const char *dinput_joypad_name(unsigned pad)
{
(void)pad;
// FIXME
return NULL;
}
const rarch_joypad_driver_t dinput_joypad = { const rarch_joypad_driver_t dinput_joypad = {
dinput_joypad_init, dinput_joypad_init,
dinput_joypad_query_pad, dinput_joypad_query_pad,
@ -562,6 +569,7 @@ const rarch_joypad_driver_t dinput_joypad = {
dinput_joypad_button, dinput_joypad_button,
dinput_joypad_axis, dinput_joypad_axis,
dinput_joypad_poll, dinput_joypad_poll,
dinput_joypad_name,
"dinput", "dinput",
}; };

View File

@ -18,6 +18,7 @@
#include <stdlib.h> #include <stdlib.h>
#include "../general.h" #include "../general.h"
#include "../driver.h"
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include "../config.h" #include "../config.h"
#endif #endif
@ -36,7 +37,10 @@
#include <X11/keysym.h> #include <X11/keysym.h>
#endif #endif
#include "../file.h"
static const rarch_joypad_driver_t *joypad_drivers[] = { static const rarch_joypad_driver_t *joypad_drivers[] = {
#ifndef IS_RETROLAUNCH
#ifdef HAVE_DINPUT #ifdef HAVE_DINPUT
&dinput_joypad, &dinput_joypad,
#endif #endif
@ -46,11 +50,13 @@ static const rarch_joypad_driver_t *joypad_drivers[] = {
#ifdef HAVE_SDL #ifdef HAVE_SDL
&sdl_joypad, &sdl_joypad,
#endif #endif
#endif
NULL,
}; };
const rarch_joypad_driver_t *input_joypad_find_driver(const char *ident) const rarch_joypad_driver_t *input_joypad_find_driver(const char *ident)
{ {
for (unsigned i = 0; i < ARRAY_SIZE(joypad_drivers); i++) for (unsigned i = 0; joypad_drivers[i]; i++)
{ {
if (strcmp(ident, joypad_drivers[i]->ident) == 0) if (strcmp(ident, joypad_drivers[i]->ident) == 0)
{ {
@ -64,7 +70,7 @@ const rarch_joypad_driver_t *input_joypad_find_driver(const char *ident)
const rarch_joypad_driver_t *input_joypad_init_first(void) const rarch_joypad_driver_t *input_joypad_init_first(void)
{ {
for (unsigned i = 0; i < ARRAY_SIZE(joypad_drivers); i++) for (unsigned i = 0; joypad_drivers[i]; i++)
{ {
if (joypad_drivers[i]->init()) if (joypad_drivers[i]->init())
{ {
@ -82,8 +88,16 @@ void input_joypad_poll(const rarch_joypad_driver_t *driver)
driver->poll(); driver->poll();
} }
const char *input_joypad_name(const rarch_joypad_driver_t *driver, unsigned joypad)
{
if (!driver)
return NULL;
return driver->name(joypad);
}
bool input_joypad_pressed(const rarch_joypad_driver_t *driver, bool input_joypad_pressed(const rarch_joypad_driver_t *driver,
unsigned port, const struct retro_keybind *key) unsigned port, const struct retro_keybind *binds, unsigned key)
{ {
if (!driver) if (!driver)
return false; return false;
@ -92,13 +106,24 @@ bool input_joypad_pressed(const rarch_joypad_driver_t *driver,
if (joy_index < 0 || joy_index >= MAX_PLAYERS) if (joy_index < 0 || joy_index >= MAX_PLAYERS)
return false; return false;
if (!key->valid) // Auto-binds are per joypad, not per player.
const struct retro_keybind *auto_binds = g_settings.input.autoconf_binds[joy_index];
if (!binds[key].valid)
return false; return false;
if (driver->button(joy_index, (uint16_t)key->joykey)) uint64_t joykey = binds[key].joykey;
if (joykey == NO_BTN)
joykey = auto_binds[key].joykey;
if (driver->button(joy_index, (uint16_t)joykey))
return true; return true;
int16_t axis = driver->axis(joy_index, key->joyaxis); uint32_t joyaxis = binds[key].joyaxis;
if (joyaxis == AXIS_NONE)
joyaxis = auto_binds[key].joyaxis;
int16_t axis = driver->axis(joy_index, joyaxis);
float scaled_axis = (float)abs(axis) / 0x8000; float scaled_axis = (float)abs(axis) / 0x8000;
return scaled_axis > g_settings.input.axis_threshold; return scaled_axis > g_settings.input.axis_threshold;
} }
@ -110,9 +135,12 @@ int16_t input_joypad_analog(const rarch_joypad_driver_t *driver,
return 0; return 0;
int joy_index = g_settings.input.joypad_map[port]; int joy_index = g_settings.input.joypad_map[port];
if (joy_index < 0) if (joy_index < 0 || joy_index >= MAX_PLAYERS)
return 0; return 0;
// Auto-binds are per joypad, not per player.
const struct retro_keybind *auto_binds = g_settings.input.autoconf_binds[joy_index];
unsigned id_minus = 0; unsigned id_minus = 0;
unsigned id_plus = 0; unsigned id_plus = 0;
input_conv_analog_id_to_bind_id(index, id, &id_minus, &id_plus); input_conv_analog_id_to_bind_id(index, id, &id_minus, &id_plus);
@ -122,16 +150,30 @@ int16_t input_joypad_analog(const rarch_joypad_driver_t *driver,
if (!bind_minus->valid || !bind_plus->valid) if (!bind_minus->valid || !bind_plus->valid)
return 0; return 0;
int16_t pressed_minus = abs(driver->axis(joy_index, bind_minus->joyaxis)); uint32_t axis_minus = bind_minus->joyaxis;
int16_t pressed_plus = abs(driver->axis(joy_index, bind_plus->joyaxis)); uint32_t axis_plus = bind_plus->joyaxis;
if (axis_minus == AXIS_NONE)
axis_minus = auto_binds[id_minus].joyaxis;
if (axis_plus == AXIS_NONE)
axis_plus = auto_binds[id_plus].joyaxis;
int16_t pressed_minus = abs(driver->axis(joy_index, axis_minus));
int16_t pressed_plus = abs(driver->axis(joy_index, axis_plus));
int16_t res = pressed_plus - pressed_minus; int16_t res = pressed_plus - pressed_minus;
if (res != 0) if (res != 0)
return res; return res;
int16_t digital_left = driver->button(joy_index, (uint16_t)bind_minus->joykey) ? -0x7fff : 0; uint64_t key_minus = bind_minus->joykey;
int16_t digital_right = driver->button(joy_index, (uint16_t)bind_plus->joykey) ? 0x7fff : 0; uint64_t key_plus = bind_plus->joykey;
if (key_minus == NO_BTN)
key_minus = auto_binds[id_minus].joykey;
if (key_plus == NO_BTN)
key_plus = auto_binds[id_plus].joykey;
int16_t digital_left = driver->button(joy_index, (uint16_t)key_minus) ? -0x7fff : 0;
int16_t digital_right = driver->button(joy_index, (uint16_t)key_plus) ? 0x7fff : 0;
return digital_right + digital_left; return digital_right + digital_left;
} }
@ -162,6 +204,7 @@ bool input_joypad_hat_raw(const rarch_joypad_driver_t *driver,
return driver->button(joypad, HAT_MAP(hat, hat_dir)); return driver->button(joypad, HAT_MAP(hat, hat_dir));
} }
#ifndef IS_RETROLAUNCH
bool input_translate_coord_viewport(int mouse_x, int mouse_y, bool input_translate_coord_viewport(int mouse_x, int mouse_y,
int16_t *res_x, int16_t *res_y, int16_t *res_screen_x, int16_t *res_screen_y) int16_t *res_x, int16_t *res_y, int16_t *res_screen_x, int16_t *res_screen_y)
{ {
@ -194,6 +237,7 @@ bool input_translate_coord_viewport(int mouse_x, int mouse_y,
*res_screen_y = scaled_screen_y; *res_screen_y = scaled_screen_y;
return true; return true;
} }
#endif
#ifdef HAVE_X11 #ifdef HAVE_X11
const struct rarch_key_map rarch_key_map_x11[] = { const struct rarch_key_map rarch_key_map_x11[] = {
@ -489,3 +533,337 @@ unsigned input_translate_rk_to_keysym(enum retro_key key)
{ {
return rarch_keysym_lut[key]; return rarch_keysym_lut[key];
} }
static const char *bind_player_prefix[MAX_PLAYERS] = {
"input_player1",
"input_player2",
"input_player3",
"input_player4",
"input_player5",
"input_player6",
"input_player7",
"input_player8",
};
#define DECLARE_BIND(x, bind, desc) { true, false, #x, desc, bind }
#define DECLARE_META_BIND(x, bind, desc) { true, true, #x, desc, bind }
const struct input_bind_map input_config_bind_map[RARCH_BIND_LIST_END_NULL] = {
DECLARE_BIND(b, RETRO_DEVICE_ID_JOYPAD_B, "B button (down)"),
DECLARE_BIND(y, RETRO_DEVICE_ID_JOYPAD_Y, "Y button (left)"),
DECLARE_BIND(select, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select button"),
DECLARE_BIND(start, RETRO_DEVICE_ID_JOYPAD_START, "Start button"),
DECLARE_BIND(up, RETRO_DEVICE_ID_JOYPAD_UP, "Up D-pad"),
DECLARE_BIND(down, RETRO_DEVICE_ID_JOYPAD_DOWN, "Down D-pad"),
DECLARE_BIND(left, RETRO_DEVICE_ID_JOYPAD_LEFT, "Left D-pad"),
DECLARE_BIND(right, RETRO_DEVICE_ID_JOYPAD_RIGHT, "Right D-pad"),
DECLARE_BIND(a, RETRO_DEVICE_ID_JOYPAD_A, "A button (right)"),
DECLARE_BIND(x, RETRO_DEVICE_ID_JOYPAD_X, "X button (top)"),
DECLARE_BIND(l, RETRO_DEVICE_ID_JOYPAD_L, "L button (left shoulder)"),
DECLARE_BIND(r, RETRO_DEVICE_ID_JOYPAD_R, "R button (right shoulder)"),
DECLARE_BIND(l2, RETRO_DEVICE_ID_JOYPAD_L2, "L2 button (left shoulder #2)"),
DECLARE_BIND(r2, RETRO_DEVICE_ID_JOYPAD_R2, "R2 button (right shoulder #2)"),
DECLARE_BIND(l3, RETRO_DEVICE_ID_JOYPAD_L3, "L3 button (left analog button)"),
DECLARE_BIND(r3, RETRO_DEVICE_ID_JOYPAD_R3, "R3 button (right analog button)"),
DECLARE_BIND(turbo, RARCH_TURBO_ENABLE, "Turbo enable"),
DECLARE_BIND(l_x_plus, RARCH_ANALOG_LEFT_X_PLUS, "Left analog X+ (right)"),
DECLARE_BIND(l_x_minus, RARCH_ANALOG_LEFT_X_MINUS, "Left analog X- (left)"),
DECLARE_BIND(l_y_plus, RARCH_ANALOG_LEFT_Y_PLUS, "Left analog Y+ (down)"),
DECLARE_BIND(l_y_minus, RARCH_ANALOG_LEFT_Y_MINUS, "Left analog Y- (up)"),
DECLARE_BIND(r_x_plus, RARCH_ANALOG_RIGHT_X_PLUS, "Right analog X+ (right)"),
DECLARE_BIND(r_x_minus, RARCH_ANALOG_RIGHT_X_MINUS, "Right analog X- (left)"),
DECLARE_BIND(r_y_plus, RARCH_ANALOG_RIGHT_Y_PLUS, "Right analog Y+ (down)"),
DECLARE_BIND(r_y_minus, RARCH_ANALOG_RIGHT_Y_MINUS, "Right analog Y- (up)"),
DECLARE_META_BIND(toggle_fast_forward, RARCH_FAST_FORWARD_KEY, "Fast forward toggle"),
DECLARE_META_BIND(hold_fast_forward, RARCH_FAST_FORWARD_HOLD_KEY, "Fast forward hold"),
DECLARE_META_BIND(load_state, RARCH_LOAD_STATE_KEY, "Load state"),
DECLARE_META_BIND(save_state, RARCH_SAVE_STATE_KEY, "Save state"),
DECLARE_META_BIND(toggle_fullscreen, RARCH_FULLSCREEN_TOGGLE_KEY, "Fullscreen toggle"),
DECLARE_META_BIND(exit_emulator, RARCH_QUIT_KEY, "Quit RetroArch"),
DECLARE_META_BIND(state_slot_increase, RARCH_STATE_SLOT_PLUS, "Savestate slot +"),
DECLARE_META_BIND(state_slot_decrease, RARCH_STATE_SLOT_MINUS, "Savestate slot -"),
DECLARE_META_BIND(rewind, RARCH_REWIND, "Rewind"),
DECLARE_META_BIND(movie_record_toggle, RARCH_MOVIE_RECORD_TOGGLE, "Movie record toggle"),
DECLARE_META_BIND(pause_toggle, RARCH_PAUSE_TOGGLE, "Pause toggle"),
DECLARE_META_BIND(frame_advance, RARCH_FRAMEADVANCE, "Frameadvance"),
DECLARE_META_BIND(reset, RARCH_RESET, "Reset game"),
DECLARE_META_BIND(shader_next, RARCH_SHADER_NEXT, "Next shader"),
DECLARE_META_BIND(shader_prev, RARCH_SHADER_PREV, "Previous shader"),
DECLARE_META_BIND(cheat_index_plus, RARCH_CHEAT_INDEX_PLUS, "Cheat index +"),
DECLARE_META_BIND(cheat_index_minus, RARCH_CHEAT_INDEX_MINUS, "Cheat index -"),
DECLARE_META_BIND(cheat_toggle, RARCH_CHEAT_TOGGLE, "Cheat toggle"),
DECLARE_META_BIND(screenshot, RARCH_SCREENSHOT, "Take screenshot"),
DECLARE_META_BIND(dsp_config, RARCH_DSP_CONFIG, "DSP config"),
DECLARE_META_BIND(audio_mute, RARCH_MUTE, "Audio mute toggle"),
DECLARE_META_BIND(netplay_flip_players, RARCH_NETPLAY_FLIP, "Netplay flip players"),
DECLARE_META_BIND(slowmotion, RARCH_SLOWMOTION, "Slow motion"),
DECLARE_META_BIND(enable_hotkey, RARCH_ENABLE_HOTKEY, "Enable hotkeys"),
DECLARE_META_BIND(volume_up, RARCH_VOLUME_UP, "Volume +"),
DECLARE_META_BIND(volume_down, RARCH_VOLUME_DOWN, "Volume -"),
DECLARE_META_BIND(overlay_next, RARCH_OVERLAY_NEXT, "Overlay next"),
DECLARE_META_BIND(disk_eject_toggle, RARCH_DISK_EJECT_TOGGLE, "Disk eject toggle"),
DECLARE_META_BIND(disk_next, RARCH_DISK_NEXT, "Disk next"),
DECLARE_META_BIND(grab_mouse_toggle, RARCH_GRAB_MOUSE_TOGGLE, "Grab mouse toggle"),
#ifdef HAVE_RGUI
DECLARE_META_BIND(menu_toggle, RARCH_MENU_TOGGLE, "RGUI menu toggle"),
#endif
};
const struct input_key_map input_config_key_map[] = {
{ "left", RETROK_LEFT },
{ "right", RETROK_RIGHT },
{ "up", RETROK_UP },
{ "down", RETROK_DOWN },
{ "enter", RETROK_RETURN },
{ "kp_enter", RETROK_KP_ENTER },
{ "tab", RETROK_TAB },
{ "insert", RETROK_INSERT },
{ "del", RETROK_DELETE },
{ "end", RETROK_END },
{ "home", RETROK_HOME },
{ "rshift", RETROK_RSHIFT },
{ "shift", RETROK_LSHIFT },
{ "ctrl", RETROK_LCTRL },
{ "alt", RETROK_LALT },
{ "space", RETROK_SPACE },
{ "escape", RETROK_ESCAPE },
{ "add", RETROK_KP_PLUS },
{ "subtract", RETROK_KP_MINUS },
{ "kp_plus", RETROK_KP_PLUS },
{ "kp_minus", RETROK_KP_MINUS },
{ "f1", RETROK_F1 },
{ "f2", RETROK_F2 },
{ "f3", RETROK_F3 },
{ "f4", RETROK_F4 },
{ "f5", RETROK_F5 },
{ "f6", RETROK_F6 },
{ "f7", RETROK_F7 },
{ "f8", RETROK_F8 },
{ "f9", RETROK_F9 },
{ "f10", RETROK_F10 },
{ "f11", RETROK_F11 },
{ "f12", RETROK_F12 },
{ "num0", RETROK_0 },
{ "num1", RETROK_1 },
{ "num2", RETROK_2 },
{ "num3", RETROK_3 },
{ "num4", RETROK_4 },
{ "num5", RETROK_5 },
{ "num6", RETROK_6 },
{ "num7", RETROK_7 },
{ "num8", RETROK_8 },
{ "num9", RETROK_9 },
{ "pageup", RETROK_PAGEUP },
{ "pagedown", RETROK_PAGEDOWN },
{ "keypad0", RETROK_KP0 },
{ "keypad1", RETROK_KP1 },
{ "keypad2", RETROK_KP2 },
{ "keypad3", RETROK_KP3 },
{ "keypad4", RETROK_KP4 },
{ "keypad5", RETROK_KP5 },
{ "keypad6", RETROK_KP6 },
{ "keypad7", RETROK_KP7 },
{ "keypad8", RETROK_KP8 },
{ "keypad9", RETROK_KP9 },
{ "period", RETROK_PERIOD },
{ "capslock", RETROK_CAPSLOCK },
{ "numlock", RETROK_NUMLOCK },
{ "backspace", RETROK_BACKSPACE },
{ "multiply", RETROK_KP_MULTIPLY },
{ "divide", RETROK_KP_DIVIDE },
{ "print_screen", RETROK_PRINT },
{ "scroll_lock", RETROK_SCROLLOCK },
{ "tilde", RETROK_BACKQUOTE },
{ "backquote", RETROK_BACKQUOTE },
{ "pause", RETROK_PAUSE },
{ "nul", RETROK_UNKNOWN },
{ NULL, RETROK_UNKNOWN },
};
static enum retro_key find_sk_bind(const char *str)
{
for (size_t i = 0; input_config_key_map[i].str; i++)
{
if (strcasecmp(input_config_key_map[i].str, str) == 0)
return input_config_key_map[i].key;
}
return RETROK_UNKNOWN;
}
static enum retro_key find_sk_key(const char *str)
{
if (strlen(str) == 1 && isalpha(*str))
return (enum retro_key)(RETROK_a + (tolower(*str) - (int)'a'));
else
return find_sk_bind(str);
}
void input_config_parse_key(config_file_t *conf, const char *prefix, const char *btn,
struct retro_keybind *bind)
{
char tmp[64];
char key[64];
snprintf(key, sizeof(key), "%s_%s", prefix, btn);
if (config_get_array(conf, key, tmp, sizeof(tmp)))
bind->key = find_sk_key(tmp);
}
const char *input_config_get_prefix(unsigned player, bool meta)
{
if (player == 0)
return meta ? "input" : bind_player_prefix[player];
else if (player != 0 && !meta)
return bind_player_prefix[player];
else
return NULL; // Don't bother with meta bind for anyone else than first player.
}
static void parse_hat(struct retro_keybind *bind, const char *str)
{
if (!isdigit(*str))
return;
char *dir = NULL;
uint16_t hat = strtoul(str, &dir, 0);
uint16_t hat_dir = 0;
if (!dir)
{
RARCH_WARN("Found invalid hat in config!\n");
return;
}
if (strcasecmp(dir, "up") == 0)
hat_dir = HAT_UP_MASK;
else if (strcasecmp(dir, "down") == 0)
hat_dir = HAT_DOWN_MASK;
else if (strcasecmp(dir, "left") == 0)
hat_dir = HAT_LEFT_MASK;
else if (strcasecmp(dir, "right") == 0)
hat_dir = HAT_RIGHT_MASK;
if (hat_dir)
bind->joykey = HAT_MAP(hat, hat_dir);
}
void input_config_parse_joy_button(config_file_t *conf, const char *prefix,
const char *btn, struct retro_keybind *bind)
{
char tmp[64];
char key[64];
snprintf(key, sizeof(key), "%s_%s_btn", prefix, btn);
if (config_get_array(conf, key, tmp, sizeof(tmp)))
{
const char *btn = tmp;
if (strcmp(btn, "nul") == 0)
bind->joykey = NO_BTN;
else
{
if (*btn == 'h')
parse_hat(bind, btn + 1);
else
bind->joykey = strtoull(tmp, NULL, 0);
}
}
}
void input_config_parse_joy_axis(config_file_t *conf, const char *prefix,
const char *axis, struct retro_keybind *bind)
{
char tmp[64];
char key[64];
snprintf(key, sizeof(key), "%s_%s_axis", prefix, axis);
if (config_get_array(conf, key, tmp, sizeof(tmp)))
{
if (strcmp(tmp, "nul") == 0)
bind->joyaxis = AXIS_NONE;
else if (strlen(tmp) >= 2 && (*tmp == '+' || *tmp == '-'))
{
int axis = strtol(tmp + 1, NULL, 0);
if (*tmp == '+')
bind->joyaxis = AXIS_POS(axis);
else
bind->joyaxis = AXIS_NEG(axis);
}
}
}
#if !defined(IS_JOYCONFIG) && !defined(IS_RETROLAUNCH)
static void input_autoconfigure_joypad_conf(config_file_t *conf, struct retro_keybind *binds)
{
for (unsigned i = 0; i < RARCH_BIND_LIST_END; i++)
{
input_config_parse_joy_button(conf, "input", input_config_bind_map[i].base, &binds[i]);
input_config_parse_joy_axis(conf, "input", input_config_bind_map[i].base, &binds[i]);
}
}
void input_config_autoconfigure_joypad(unsigned index, const char *name, const char *driver)
{
if (!g_settings.input.autodetect_enable)
return;
// This will be the case if input driver is reinit. No reason to spam autoconfigure messages
// every time (fine in log).
bool block_osd_spam = g_settings.input.autoconfigured[index] && name;
for (unsigned i = 0; i < RARCH_BIND_LIST_END; i++)
{
g_settings.input.autoconf_binds[index][i].joykey = NO_BTN;
g_settings.input.autoconf_binds[index][i].joyaxis = AXIS_NONE;
}
g_settings.input.autoconfigured[index] = false;
if (!name)
return;
if (!*g_settings.input.autoconfig_dir)
return;
struct string_list *list = dir_list_new(g_settings.input.autoconfig_dir, "cfg", false);
if (!list)
return;
char ident[1024];
char input_driver[1024];
for (size_t i = 0; i < list->size; i++)
{
*ident = *input_driver = '\0';
config_file_t *conf = config_file_new(list->elems[i].data);
if (!conf)
continue;
config_get_array(conf, "input_device", ident, sizeof(ident));
config_get_array(conf, "input_driver", input_driver, sizeof(input_driver));
if (!strcmp(ident, name) && !strcmp(driver, input_driver))
{
g_settings.input.autoconfigured[index] = true;
input_autoconfigure_joypad_conf(conf, g_settings.input.autoconf_binds[index]);
char msg[512];
snprintf(msg, sizeof(msg), "Joypad port #%u (%s) configured.",
index, name);
if (!block_osd_spam)
msg_queue_push(g_extern.msg_queue, msg, 0, 60);
RARCH_LOG("%s\n", msg);
config_file_free(conf);
break;
}
else
config_file_free(conf);
}
string_list_free(list);
}
#endif

View File

@ -17,6 +17,7 @@
#define INPUT_COMMON_H__ #define INPUT_COMMON_H__
#include "../driver.h" #include "../driver.h"
#include "../conf/config_file.h"
#include <stdint.h> #include <stdint.h>
static inline void input_conv_analog_id_to_bind_id(unsigned index, unsigned id, static inline void input_conv_analog_id_to_bind_id(unsigned index, unsigned id,
@ -65,6 +66,7 @@ typedef struct rarch_joypad_driver
bool (*button)(unsigned, uint16_t); bool (*button)(unsigned, uint16_t);
int16_t (*axis)(unsigned, uint32_t); int16_t (*axis)(unsigned, uint32_t);
void (*poll)(void); void (*poll)(void);
const char *(*name)(unsigned);
const char *ident; const char *ident;
} rarch_joypad_driver_t; } rarch_joypad_driver_t;
@ -73,7 +75,7 @@ const rarch_joypad_driver_t *input_joypad_find_driver(const char *ident);
const rarch_joypad_driver_t *input_joypad_init_first(void); const rarch_joypad_driver_t *input_joypad_init_first(void);
bool input_joypad_pressed(const rarch_joypad_driver_t *driver, bool input_joypad_pressed(const rarch_joypad_driver_t *driver,
unsigned port, const struct retro_keybind *key); unsigned port, const struct retro_keybind *binds, unsigned key);
int16_t input_joypad_analog(const rarch_joypad_driver_t *driver, int16_t input_joypad_analog(const rarch_joypad_driver_t *driver,
unsigned port, unsigned index, unsigned id, const struct retro_keybind *binds); unsigned port, unsigned index, unsigned id, const struct retro_keybind *binds);
@ -86,6 +88,7 @@ bool input_joypad_hat_raw(const rarch_joypad_driver_t *driver,
unsigned joypad, unsigned hat_dir, unsigned hat); unsigned joypad, unsigned hat_dir, unsigned hat);
void input_joypad_poll(const rarch_joypad_driver_t *driver); void input_joypad_poll(const rarch_joypad_driver_t *driver);
const char *input_joypad_name(const rarch_joypad_driver_t *driver, unsigned joypad);
extern const rarch_joypad_driver_t dinput_joypad; extern const rarch_joypad_driver_t dinput_joypad;
extern const rarch_joypad_driver_t linuxraw_joypad; extern const rarch_joypad_driver_t linuxraw_joypad;
@ -106,5 +109,34 @@ void input_init_keyboard_lut(const struct rarch_key_map *map);
enum retro_key input_translate_keysym_to_rk(unsigned sym); enum retro_key input_translate_keysym_to_rk(unsigned sym);
unsigned input_translate_rk_to_keysym(enum retro_key key); unsigned input_translate_rk_to_keysym(enum retro_key key);
// Input config.
struct input_bind_map
{
bool valid;
bool meta; // Meta binds get input as prefix, not input_playerN"
const char *base;
const char *desc;
unsigned retro_key;
};
extern const struct input_bind_map input_config_bind_map[];
struct input_key_map
{
const char *str;
enum retro_key key;
};
extern const struct input_key_map input_config_key_map[];
const char *input_config_get_prefix(unsigned player, bool meta);
void input_config_parse_key(config_file_t *conf, const char *prefix, const char *btn,
struct retro_keybind *bind);
void input_config_parse_joy_button(config_file_t *conf, const char *prefix,
const char *btn, struct retro_keybind *bind);
void input_config_parse_joy_axis(config_file_t *conf, const char *prefix,
const char *axis, struct retro_keybind *bind);
void input_config_autoconfigure_joypad(unsigned index, const char *name, const char *driver);
#endif #endif

View File

@ -253,7 +253,7 @@ static bool linuxraw_bind_button_pressed(void *data, int key)
{ {
linuxraw_input_t *linuxraw = (linuxraw_input_t*)data; linuxraw_input_t *linuxraw = (linuxraw_input_t*)data;
return linuxraw_is_pressed(linuxraw, g_settings.input.binds[0], key) || return linuxraw_is_pressed(linuxraw, g_settings.input.binds[0], key) ||
input_joypad_pressed(linuxraw->joypad, 0, &g_settings.input.binds[0][key]); input_joypad_pressed(linuxraw->joypad, 0, g_settings.input.binds[0], key);
} }
static int16_t linuxraw_input_state(void *data, const struct retro_keybind **binds, unsigned port, unsigned device, unsigned index, unsigned id) static int16_t linuxraw_input_state(void *data, const struct retro_keybind **binds, unsigned port, unsigned device, unsigned index, unsigned id)
@ -264,7 +264,7 @@ static int16_t linuxraw_input_state(void *data, const struct retro_keybind **bin
{ {
case RETRO_DEVICE_JOYPAD: case RETRO_DEVICE_JOYPAD:
return linuxraw_is_pressed(linuxraw, binds[port], id) || return linuxraw_is_pressed(linuxraw, binds[port], id) ||
input_joypad_pressed(linuxraw->joypad, port, &binds[port][id]); input_joypad_pressed(linuxraw->joypad, port, binds[port], id);
case RETRO_DEVICE_ANALOG: case RETRO_DEVICE_ANALOG:
return input_joypad_analog(linuxraw->joypad, port, index, id, binds[port]); return input_joypad_analog(linuxraw->joypad, port, index, id, binds[port]);

View File

@ -42,7 +42,7 @@ static struct linuxraw_joypad g_pads[MAX_PLAYERS];
static int g_notify; static int g_notify;
static int g_epoll; static int g_epoll;
#ifndef NO_MSG_QUEUE #ifndef IS_JOYCONFIG
static bool g_hotplug; static bool g_hotplug;
#endif #endif
@ -68,15 +68,15 @@ static void poll_pad(struct linuxraw_joypad *pad)
} }
} }
static void linuxraw_joypad_init_pad(const char *path, struct linuxraw_joypad *pad) static bool linuxraw_joypad_init_pad(const char *path, struct linuxraw_joypad *pad)
{ {
if (pad->fd >= 0) if (pad->fd >= 0)
return; return false;
// Device can have just been created, but not made accessible (yet). // Device can have just been created, but not made accessible (yet).
// IN_ATTRIB will signal when permissions change. // IN_ATTRIB will signal when permissions change.
if (access(path, R_OK) < 0) if (access(path, R_OK) < 0)
return; return false;
pad->fd = open(path, O_RDONLY | O_NONBLOCK); pad->fd = open(path, O_RDONLY | O_NONBLOCK);
@ -87,7 +87,7 @@ static void linuxraw_joypad_init_pad(const char *path, struct linuxraw_joypad *p
{ {
RARCH_LOG("[Joypad]: Found pad: %s on %s.\n", pad->ident, path); RARCH_LOG("[Joypad]: Found pad: %s on %s.\n", pad->ident, path);
#ifndef NO_MSG_QUEUE #ifndef IS_JOYCONFIG
if (g_hotplug) if (g_hotplug)
{ {
char msg[512]; char msg[512];
@ -104,9 +104,13 @@ static void linuxraw_joypad_init_pad(const char *path, struct linuxraw_joypad *p
event.events = EPOLLIN; event.events = EPOLLIN;
event.data.ptr = pad; event.data.ptr = pad;
epoll_ctl(g_epoll, EPOLL_CTL_ADD, pad->fd, &event); epoll_ctl(g_epoll, EPOLL_CTL_ADD, pad->fd, &event);
return true;
} }
else else
{
RARCH_ERR("[Joypad]: Failed to open pad %s (error: %s).\n", path, strerror(errno)); RARCH_ERR("[Joypad]: Failed to open pad %s (error: %s).\n", path, strerror(errno));
return false;
}
} }
static void handle_plugged_pad(void) static void handle_plugged_pad(void)
@ -136,7 +140,7 @@ static void handle_plugged_pad(void)
{ {
if (g_pads[index].fd >= 0) if (g_pads[index].fd >= 0)
{ {
#ifndef NO_MSG_QUEUE #ifndef IS_JOYCONFIG
if (g_hotplug) if (g_hotplug)
{ {
char msg[512]; char msg[512];
@ -151,6 +155,10 @@ static void handle_plugged_pad(void)
memset(g_pads[index].axes, 0, sizeof(g_pads[index].axes)); memset(g_pads[index].axes, 0, sizeof(g_pads[index].axes));
g_pads[index].fd = -1; g_pads[index].fd = -1;
*g_pads[index].ident = '\0'; *g_pads[index].ident = '\0';
#ifndef IS_JOYCONFIG
input_config_autoconfigure_joypad(index, NULL, NULL);
#endif
} }
} }
// Sometimes, device will be created before acess to it is established. // Sometimes, device will be created before acess to it is established.
@ -158,7 +166,12 @@ static void handle_plugged_pad(void)
{ {
char path[PATH_MAX]; char path[PATH_MAX];
snprintf(path, sizeof(path), "/dev/input/%s", event->name); snprintf(path, sizeof(path), "/dev/input/%s", event->name);
linuxraw_joypad_init_pad(path, &g_pads[index]); bool ret = linuxraw_joypad_init_pad(path, &g_pads[index]);
#ifndef IS_JOYCONFIG
if (*g_pads[index].ident && ret)
input_config_autoconfigure_joypad(index, g_pads[index].ident, "linuxraw");
#endif
} }
} }
} }
@ -206,10 +219,17 @@ static bool linuxraw_joypad_init(void)
char path[PATH_MAX]; char path[PATH_MAX];
snprintf(path, sizeof(path), "/dev/input/js%u", i); snprintf(path, sizeof(path), "/dev/input/js%u", i);
linuxraw_joypad_init_pad(path, pad); if (linuxraw_joypad_init_pad(path, pad))
if (pad->fd >= 0) {
#ifndef IS_JOYCONFIG
input_config_autoconfigure_joypad(i, pad->ident, "linuxraw");
#endif
poll_pad(pad); poll_pad(pad);
}
#ifndef IS_JOYCONFIG
else
input_config_autoconfigure_joypad(i, NULL, NULL);
#endif
} }
g_notify = inotify_init(); g_notify = inotify_init();
@ -223,7 +243,7 @@ static bool linuxraw_joypad_init(void)
epoll_ctl(g_epoll, EPOLL_CTL_ADD, g_notify, &event); epoll_ctl(g_epoll, EPOLL_CTL_ADD, g_notify, &event);
} }
#ifndef NO_MSG_QUEUE #ifndef IS_JOYCONFIG
g_hotplug = true; g_hotplug = true;
#endif #endif
@ -250,7 +270,7 @@ static void linuxraw_joypad_destroy(void)
close(g_epoll); close(g_epoll);
g_epoll = -1; g_epoll = -1;
#ifndef NO_MSG_QUEUE #ifndef IS_JOYCONFIG
g_hotplug = false; g_hotplug = false;
#endif #endif
} }
@ -292,6 +312,14 @@ static bool linuxraw_joypad_query_pad(unsigned pad)
return pad < MAX_PLAYERS && g_pads[pad].fd >= 0; return pad < MAX_PLAYERS && g_pads[pad].fd >= 0;
} }
static const char *linuxraw_joypad_name(unsigned pad)
{
if (pad >= MAX_PLAYERS)
return NULL;
return *g_pads[pad].ident ? g_pads[pad].ident : NULL;
}
const rarch_joypad_driver_t linuxraw_joypad = { const rarch_joypad_driver_t linuxraw_joypad = {
linuxraw_joypad_init, linuxraw_joypad_init,
linuxraw_joypad_query_pad, linuxraw_joypad_query_pad,
@ -299,6 +327,7 @@ const rarch_joypad_driver_t linuxraw_joypad = {
linuxraw_joypad_button, linuxraw_joypad_button,
linuxraw_joypad_axis, linuxraw_joypad_axis,
linuxraw_joypad_poll, linuxraw_joypad_poll,
linuxraw_joypad_name,
"linuxraw", "linuxraw",
}; };

View File

@ -59,22 +59,19 @@ static bool sdl_key_pressed(int key)
return keymap[sym]; return keymap[sym];
} }
static bool sdl_is_pressed(sdl_input_t *sdl, unsigned port_num, const struct retro_keybind *key) static bool sdl_is_pressed(sdl_input_t *sdl, unsigned port_num, const struct retro_keybind *binds, unsigned key)
{ {
if (sdl_key_pressed(key->key)) if (sdl_key_pressed(binds[key].key))
return true; return true;
return input_joypad_pressed(sdl->joypad, port_num, key); return input_joypad_pressed(sdl->joypad, port_num, binds, key);
} }
static bool sdl_bind_button_pressed(void *data, int key) static bool sdl_bind_button_pressed(void *data, int key)
{ {
const struct retro_keybind *binds = g_settings.input.binds[0]; const struct retro_keybind *binds = g_settings.input.binds[0];
if (key >= 0 && key < RARCH_BIND_LIST_END) if (key >= 0 && key < RARCH_BIND_LIST_END)
{ return sdl_is_pressed((sdl_input_t*)data, 0, binds, key);
const struct retro_keybind *bind = &binds[key];
return sdl_is_pressed((sdl_input_t*)data, 0, bind);
}
else else
return false; return false;
} }
@ -84,10 +81,7 @@ static int16_t sdl_joypad_device_state(sdl_input_t *sdl, const struct retro_keyb
{ {
const struct retro_keybind *binds = binds_[port_num]; const struct retro_keybind *binds = binds_[port_num];
if (id < RARCH_BIND_LIST_END) if (id < RARCH_BIND_LIST_END)
{ return binds[id].valid && sdl_is_pressed(sdl, port_num, binds, id);
const struct retro_keybind *bind = &binds[id];
return bind->valid && sdl_is_pressed(sdl, port_num, bind);
}
else else
return 0; return 0;
} }

View File

@ -155,6 +155,14 @@ static bool sdl_joypad_query_pad(unsigned pad)
return pad < MAX_PLAYERS && g_pads[pad].joypad; return pad < MAX_PLAYERS && g_pads[pad].joypad;
} }
static const char *sdl_joypad_name(unsigned pad)
{
if (pad >= MAX_PLAYERS)
return NULL;
return SDL_JoystickName(pad);
}
const rarch_joypad_driver_t sdl_joypad = { const rarch_joypad_driver_t sdl_joypad = {
sdl_joypad_init, sdl_joypad_init,
sdl_joypad_query_pad, sdl_joypad_query_pad,
@ -162,6 +170,7 @@ const rarch_joypad_driver_t sdl_joypad = {
sdl_joypad_button, sdl_joypad_button,
sdl_joypad_axis, sdl_joypad_axis,
sdl_joypad_poll, sdl_joypad_poll,
sdl_joypad_name,
"sdl", "sdl",
}; };

View File

@ -89,7 +89,7 @@ static bool x_bind_button_pressed(void *data, int key)
{ {
x11_input_t *x11 = (x11_input_t*)data; x11_input_t *x11 = (x11_input_t*)data;
return x_is_pressed(x11, g_settings.input.binds[0], key) || return x_is_pressed(x11, g_settings.input.binds[0], key) ||
input_joypad_pressed(x11->joypad, 0, &g_settings.input.binds[0][key]); input_joypad_pressed(x11->joypad, 0, g_settings.input.binds[0], key);
} }
static int16_t x_mouse_state(x11_input_t *x11, unsigned id) static int16_t x_mouse_state(x11_input_t *x11, unsigned id)
@ -176,7 +176,7 @@ static int16_t x_input_state(void *data, const struct retro_keybind **binds, uns
{ {
case RETRO_DEVICE_JOYPAD: case RETRO_DEVICE_JOYPAD:
return x_is_pressed(x11, binds[port], id) || return x_is_pressed(x11, binds[port], id) ||
input_joypad_pressed(x11->joypad, port, &binds[port][id]); input_joypad_pressed(x11->joypad, port, binds[port], id);
case RETRO_DEVICE_KEYBOARD: case RETRO_DEVICE_KEYBOARD:
return x_key_pressed(x11, id); return x_key_pressed(x11, id);

View File

@ -65,9 +65,9 @@ static bool ios_key_pressed(enum retro_key key)
return false; return false;
} }
static bool ios_is_pressed(unsigned port_num, const struct retro_keybind *key) static bool ios_is_pressed(unsigned port_num, const struct retro_keybind *binds, unsigned key)
{ {
return ios_key_pressed(key->key) || input_joypad_pressed(g_joydriver, port_num, key); return ios_key_pressed(binds[key].key) || input_joypad_pressed(g_joydriver, port_num, binds, key);
} }
// Exported input driver // Exported input driver
@ -107,7 +107,7 @@ static int16_t ios_input_state(void *data, const struct retro_keybind **binds, u
switch (device) switch (device)
{ {
case RETRO_DEVICE_JOYPAD: case RETRO_DEVICE_JOYPAD:
return (id < RARCH_BIND_LIST_END) ? ios_is_pressed(port, &binds[port][id]) : false; return (id < RARCH_BIND_LIST_END) ? ios_is_pressed(port, binds[port], id) : false;
case RETRO_DEVICE_ANALOG: case RETRO_DEVICE_ANALOG:
return input_joypad_analog(g_joydriver, port, index, id, binds[port]); return input_joypad_analog(g_joydriver, port, index, id, binds[port]);
@ -143,7 +143,7 @@ static int16_t ios_input_state(void *data, const struct retro_keybind **binds, u
static bool ios_bind_button_pressed(void *data, int key) static bool ios_bind_button_pressed(void *data, int key)
{ {
const struct retro_keybind *binds = g_settings.input.binds[0]; const struct retro_keybind *binds = g_settings.input.binds[0];
return (key >= 0 && key < RARCH_BIND_LIST_END) ? ios_is_pressed(0, &binds[key]) : false; return (key >= 0 && key < RARCH_BIND_LIST_END) ? ios_is_pressed(0, binds, key) : false;
} }
static void ios_input_free_input(void *data) static void ios_input_free_input(void *data)

View File

@ -187,10 +187,18 @@
# Path to input overlay # Path to input overlay
# input_overlay = # input_overlay =
# Enable input auto-detection (used on Android). Will attempt to autoconfigure # Enable input auto-detection. Will attempt to autoconfigure
# gamepads, Plug-and-Play style. # joypads, Plug-and-Play style.
# input_autodetect_enable = true # input_autodetect_enable = true
# Directory for joypad autoconfigs (PC).
# If a joypad is plugged in, that joypad will be autoconfigured if a config file
# corresponding to that joypad is present in joypad_autoconfig_dir.
# Input binds which are made explicit (input_playerN_*_btn/axis) will take priority over autoconfigs.
# Autoconfigs can be created with retroarch-joyconfig, manually, or with a frontend.
# Requires input_autodetect_enable to be enabled.
# joypad_autoconfig_dir =
# Enable debug input key reporting on-screen. # Enable debug input key reporting on-screen.
# input_debug_enable = false # input_debug_enable = false
@ -227,13 +235,13 @@
# input_player1_r_y_plus = # input_player1_r_y_plus =
# input_player1_r_y_minus = # 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. # If desired, it is possible to override which joypads are being used for player 1 through 8.
# First joypad available is 0.
# input_player1_joypad_index = 0 # input_player1_joypad_index = 0
# input_player2_joypad_index = 1 # input_player2_joypad_index = 1
# input_player3_joypad_index = 2 # input_player3_joypad_index = 2
# input_player4_joypad_index = 3 # input_player4_joypad_index = 3
# input_player5_joypad_index = 4 # input_player5_joypad_index = 4
# Player 6-8 is not directly expected by libretro API, but we'll futureproof it.
# input_player6_joypad_index = 5 # input_player6_joypad_index = 5
# input_player7_joypad_index = 6 # input_player7_joypad_index = 6
# input_player8_joypad_index = 7 # input_player8_joypad_index = 7

View File

@ -20,6 +20,7 @@
#include "config.def.h" #include "config.def.h"
#include "file.h" #include "file.h"
#include "compat/posix_string.h" #include "compat/posix_string.h"
#include "input/input_common.h"
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include "config.h" #include "config.h"
@ -221,6 +222,8 @@ void config_set_defaults(void)
for (unsigned i = 1; i < MAX_PLAYERS; i++) for (unsigned i = 1; i < MAX_PLAYERS; i++)
memcpy(g_settings.input.binds[i], retro_keybinds_rest, sizeof(retro_keybinds_rest)); memcpy(g_settings.input.binds[i], retro_keybinds_rest, sizeof(retro_keybinds_rest));
memcpy(g_settings.input.autoconf_binds, g_settings.input.binds, sizeof(g_settings.input.binds));
// Verify that binds are in proper order. // Verify that binds are in proper order.
for (int i = 0; i < MAX_PLAYERS; i++) for (int i = 0; i < MAX_PLAYERS; i++)
for (int j = 0; j < RARCH_BIND_LIST_END; j++) for (int j = 0; j < RARCH_BIND_LIST_END; j++)
@ -233,8 +236,8 @@ void config_set_defaults(void)
g_settings.input.turbo_duty_cycle = turbo_duty_cycle; g_settings.input.turbo_duty_cycle = turbo_duty_cycle;
g_settings.input.overlay_opacity = 1.0f; g_settings.input.overlay_opacity = 1.0f;
g_settings.input.debug_enable = input_debug_enable; g_settings.input.debug_enable = input_debug_enable;
#ifdef ANDROID
g_settings.input.autodetect_enable = input_autodetect_enable; g_settings.input.autodetect_enable = input_autodetect_enable;
#ifdef ANDROID
g_settings.input.back_behavior = BACK_BUTTON_QUIT; g_settings.input.back_behavior = BACK_BUTTON_QUIT;
#endif #endif
@ -650,8 +653,10 @@ bool config_load_file(const char *path)
CONFIG_GET_FLOAT(input.overlay_opacity, "input_overlay_opacity"); CONFIG_GET_FLOAT(input.overlay_opacity, "input_overlay_opacity");
CONFIG_GET_BOOL(input.debug_enable, "input_debug_enable"); CONFIG_GET_BOOL(input.debug_enable, "input_debug_enable");
#ifdef ANDROID
CONFIG_GET_BOOL(input.autodetect_enable, "input_autodetect_enable"); CONFIG_GET_BOOL(input.autodetect_enable, "input_autodetect_enable");
CONFIG_GET_PATH(input.autoconfig_dir, "joypad_autoconfig_dir");
#ifdef ANDROID
CONFIG_GET_INT(input.back_behavior, "input_back_behavior"); CONFIG_GET_INT(input.back_behavior, "input_back_behavior");
CONFIG_GET_INT(input.icade_profile[0], "input_autodetect_icade_profile_pad1"); CONFIG_GET_INT(input.icade_profile[0], "input_autodetect_icade_profile_pad1");
CONFIG_GET_INT(input.icade_profile[1], "input_autodetect_icade_profile_pad2"); CONFIG_GET_INT(input.icade_profile[1], "input_autodetect_icade_profile_pad2");
@ -695,280 +700,46 @@ bool config_load_file(const char *path)
return true; return true;
} }
struct bind_map static void read_keybinds_keyboard(config_file_t *conf, unsigned player, unsigned index,
struct retro_keybind *bind)
{ {
bool valid; if (input_config_bind_map[index].valid && input_config_bind_map[index].base)
const char *key;
const char *btn;
const char *axis;
int retro_key;
};
#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##_turbo, RARCH_TURBO_ENABLE), \
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] = {
{ {
DECL_PLAYER(1), const char *prefix = input_config_get_prefix(player, input_config_bind_map[index].meta);
if (prefix)
DECLARE_BIND(toggle_fast_forward, RARCH_FAST_FORWARD_KEY), input_config_parse_key(conf, prefix, input_config_bind_map[index].base, bind);
DECLARE_BIND(hold_fast_forward, RARCH_FAST_FORWARD_HOLD_KEY),
DECLARE_BIND(load_state, RARCH_LOAD_STATE_KEY),
DECLARE_BIND(save_state, RARCH_SAVE_STATE_KEY),
DECLARE_BIND(toggle_fullscreen, RARCH_FULLSCREEN_TOGGLE_KEY),
DECLARE_BIND(exit_emulator, RARCH_QUIT_KEY),
DECLARE_BIND(state_slot_increase, RARCH_STATE_SLOT_PLUS),
DECLARE_BIND(state_slot_decrease, RARCH_STATE_SLOT_MINUS),
DECLARE_BIND(rewind, RARCH_REWIND),
DECLARE_BIND(movie_record_toggle, RARCH_MOVIE_RECORD_TOGGLE),
DECLARE_BIND(pause_toggle, RARCH_PAUSE_TOGGLE),
DECLARE_BIND(frame_advance, RARCH_FRAMEADVANCE),
DECLARE_BIND(reset, RARCH_RESET),
DECLARE_BIND(shader_next, RARCH_SHADER_NEXT),
DECLARE_BIND(shader_prev, RARCH_SHADER_PREV),
DECLARE_BIND(cheat_index_plus, RARCH_CHEAT_INDEX_PLUS),
DECLARE_BIND(cheat_index_minus, RARCH_CHEAT_INDEX_MINUS),
DECLARE_BIND(cheat_toggle, RARCH_CHEAT_TOGGLE),
DECLARE_BIND(screenshot, RARCH_SCREENSHOT),
DECLARE_BIND(dsp_config, RARCH_DSP_CONFIG),
DECLARE_BIND(audio_mute, RARCH_MUTE),
DECLARE_BIND(netplay_flip_players, RARCH_NETPLAY_FLIP),
DECLARE_BIND(slowmotion, RARCH_SLOWMOTION),
DECLARE_BIND(enable_hotkey, RARCH_ENABLE_HOTKEY),
DECLARE_BIND(volume_up, RARCH_VOLUME_UP),
DECLARE_BIND(volume_down, RARCH_VOLUME_DOWN),
DECLARE_BIND(overlay_next, RARCH_OVERLAY_NEXT),
DECLARE_BIND(disk_eject_toggle, RARCH_DISK_EJECT_TOGGLE),
DECLARE_BIND(disk_next, RARCH_DISK_NEXT),
DECLARE_BIND(grab_mouse_toggle, RARCH_GRAB_MOUSE_TOGGLE),
#ifdef HAVE_RGUI
DECLARE_BIND(menu_toggle, RARCH_MENU_TOGGLE),
#endif
},
{ DECL_PLAYER(2) },
{ DECL_PLAYER(3) },
{ DECL_PLAYER(4) },
{ DECL_PLAYER(5) },
{ DECL_PLAYER(6) },
{ DECL_PLAYER(7) },
{ DECL_PLAYER(8) },
};
struct key_map
{
const char *str;
int key;
};
static const struct key_map sk_map[] = {
{ "left", RETROK_LEFT },
{ "right", RETROK_RIGHT },
{ "up", RETROK_UP },
{ "down", RETROK_DOWN },
{ "enter", RETROK_RETURN },
{ "kp_enter", RETROK_KP_ENTER },
{ "tab", RETROK_TAB },
{ "insert", RETROK_INSERT },
{ "del", RETROK_DELETE },
{ "end", RETROK_END },
{ "home", RETROK_HOME },
{ "rshift", RETROK_RSHIFT },
{ "shift", RETROK_LSHIFT },
{ "ctrl", RETROK_LCTRL },
{ "alt", RETROK_LALT },
{ "space", RETROK_SPACE },
{ "escape", RETROK_ESCAPE },
{ "add", RETROK_KP_PLUS },
{ "subtract", RETROK_KP_MINUS },
{ "kp_plus", RETROK_KP_PLUS },
{ "kp_minus", RETROK_KP_MINUS },
{ "f1", RETROK_F1 },
{ "f2", RETROK_F2 },
{ "f3", RETROK_F3 },
{ "f4", RETROK_F4 },
{ "f5", RETROK_F5 },
{ "f6", RETROK_F6 },
{ "f7", RETROK_F7 },
{ "f8", RETROK_F8 },
{ "f9", RETROK_F9 },
{ "f10", RETROK_F10 },
{ "f11", RETROK_F11 },
{ "f12", RETROK_F12 },
{ "num0", RETROK_0 },
{ "num1", RETROK_1 },
{ "num2", RETROK_2 },
{ "num3", RETROK_3 },
{ "num4", RETROK_4 },
{ "num5", RETROK_5 },
{ "num6", RETROK_6 },
{ "num7", RETROK_7 },
{ "num8", RETROK_8 },
{ "num9", RETROK_9 },
{ "pageup", RETROK_PAGEUP },
{ "pagedown", RETROK_PAGEDOWN },
{ "keypad0", RETROK_KP0 },
{ "keypad1", RETROK_KP1 },
{ "keypad2", RETROK_KP2 },
{ "keypad3", RETROK_KP3 },
{ "keypad4", RETROK_KP4 },
{ "keypad5", RETROK_KP5 },
{ "keypad6", RETROK_KP6 },
{ "keypad7", RETROK_KP7 },
{ "keypad8", RETROK_KP8 },
{ "keypad9", RETROK_KP9 },
{ "period", RETROK_PERIOD },
{ "capslock", RETROK_CAPSLOCK },
{ "numlock", RETROK_NUMLOCK },
{ "backspace", RETROK_BACKSPACE },
{ "multiply", RETROK_KP_MULTIPLY },
{ "divide", RETROK_KP_DIVIDE },
{ "print_screen", RETROK_PRINT },
{ "scroll_lock", RETROK_SCROLLOCK },
{ "tilde", RETROK_BACKQUOTE },
{ "backquote", RETROK_BACKQUOTE },
{ "pause", RETROK_PAUSE },
{ "nul", RETROK_UNKNOWN },
};
static struct retro_keybind *find_retro_bind(unsigned port, int id)
{
struct retro_keybind *binds = g_settings.input.binds[port];
return binds[id].valid ? &binds[id] : NULL;
}
static int find_sk_bind(const char *str)
{
for (size_t i = 0; i < ARRAY_SIZE(sk_map); i++)
{
if (strcasecmp(sk_map[i].str, str) == 0)
return sk_map[i].key;
}
return -1;
}
static int find_sk_key(const char *str)
{
if (strlen(str) == 1 && isalpha(*str))
return (int)RETROK_a + (tolower(*str) - (int)'a');
else
return find_sk_bind(str);
}
static void read_keybinds_keyboard(config_file_t *conf, unsigned player, unsigned index, struct retro_keybind *bind)
{
char tmp[64];
if (bind_maps[player][index].key &&
config_get_array(conf, bind_maps[player][index].key, tmp, sizeof(tmp)))
{
int key = find_sk_key(tmp);
if (key >= 0)
bind->key = (enum retro_key)key;
} }
} }
static void parse_hat(struct retro_keybind *bind, const char *str) static void read_keybinds_button(config_file_t *conf, unsigned player, unsigned index,
struct retro_keybind *bind)
{ {
if (!isdigit(*str)) if (input_config_bind_map[index].valid && input_config_bind_map[index].base)
return;
char *dir = NULL;
uint16_t hat = strtoul(str, &dir, 0);
uint16_t hat_dir = 0;
if (!dir)
{ {
RARCH_WARN("Found invalid hat in config!\n"); const char *prefix = input_config_get_prefix(player, input_config_bind_map[index].meta);
return; if (prefix)
} input_config_parse_joy_button(conf, prefix, input_config_bind_map[index].base, bind);
if (strcasecmp(dir, "up") == 0)
hat_dir = HAT_UP_MASK;
else if (strcasecmp(dir, "down") == 0)
hat_dir = HAT_DOWN_MASK;
else if (strcasecmp(dir, "left") == 0)
hat_dir = HAT_LEFT_MASK;
else if (strcasecmp(dir, "right") == 0)
hat_dir = HAT_RIGHT_MASK;
if (hat_dir)
bind->joykey = HAT_MAP(hat, hat_dir);
}
static void read_keybinds_button(config_file_t *conf, unsigned player, unsigned index, struct retro_keybind *bind)
{
char tmp[64];
if (bind_maps[player][index].btn &&
config_get_array(conf, bind_maps[player][index].btn, tmp, sizeof(tmp)))
{
const char *btn = tmp;
if (strcmp(btn, "nul") == 0)
bind->joykey = NO_BTN;
else
{
if (*btn == 'h')
parse_hat(bind, btn + 1);
else
bind->joykey = strtoull(tmp, NULL, 0);
}
} }
} }
static void read_keybinds_axis(config_file_t *conf, unsigned player, unsigned index, struct retro_keybind *bind) static void read_keybinds_axis(config_file_t *conf, unsigned player, unsigned index,
struct retro_keybind *bind)
{ {
char tmp[64]; if (input_config_bind_map[index].valid && input_config_bind_map[index].base)
if (bind_maps[player][index].axis &&
config_get_array(conf, bind_maps[player][index].axis, tmp, sizeof(tmp)))
{ {
if (strcmp(tmp, "nul") == 0) const char *prefix = input_config_get_prefix(player, input_config_bind_map[index].meta);
bind->joyaxis = AXIS_NONE; if (prefix)
else if (strlen(tmp) >= 2 && (*tmp == '+' || *tmp == '-')) input_config_parse_joy_axis(conf, prefix, input_config_bind_map[index].base, bind);
{
int axis = strtol(tmp + 1, NULL, 0);
if (*tmp == '+')
bind->joyaxis = AXIS_POS(axis);
else
bind->joyaxis = AXIS_NEG(axis);
}
} }
} }
static void read_keybinds_player(config_file_t *conf, unsigned player) static void read_keybinds_player(config_file_t *conf, unsigned player)
{ {
for (unsigned i = 0; bind_maps[player][i].valid; i++) for (unsigned i = 0; input_config_bind_map[i].valid; i++)
{ {
struct retro_keybind *bind = find_retro_bind(player, bind_maps[player][i].retro_key); struct retro_keybind *bind = &g_settings.input.binds[player][i];
rarch_assert(bind); if (!bind->valid)
continue;
read_keybinds_keyboard(conf, player, i, bind); read_keybinds_keyboard(conf, player, i, bind);
read_keybinds_button(conf, player, i, bind); read_keybinds_button(conf, player, i, bind);
@ -992,9 +763,12 @@ bool config_read_keybinds(const char *path)
return true; return true;
} }
static void save_keybind_key(config_file_t *conf, static void save_keybind_key(config_file_t *conf, const char *prefix, const char *base,
const struct bind_map *map, const struct retro_keybind *bind) const struct retro_keybind *bind)
{ {
char key[64];
snprintf(key, sizeof(key), "%s_%s", prefix, base);
char ascii[2] = {0}; char ascii[2] = {0};
const char *btn = ascii; const char *btn = ascii;
@ -1002,22 +776,21 @@ static void save_keybind_key(config_file_t *conf,
ascii[0] = 'a' + (bind->key - RETROK_a); ascii[0] = 'a' + (bind->key - RETROK_a);
else else
{ {
for (unsigned i = 0; i < ARRAY_SIZE(sk_map); i++) for (unsigned i = 0; input_config_key_map[i].str; i++)
{ {
if (sk_map[i].key == bind->key) if (input_config_key_map[i].key == bind->key)
{ {
btn = sk_map[i].str; btn = input_config_key_map[i].str;
break; break;
} }
} }
} }
config_set_string(conf, map->key, btn); config_set_string(conf, key, btn);
} }
#ifndef RARCH_CONSOLE #ifndef RARCH_CONSOLE
static void save_keybind_hat(config_file_t *conf, static void save_keybind_hat(config_file_t *conf, const char *key, const struct retro_keybind *bind)
const struct bind_map *map, const struct retro_keybind *bind)
{ {
unsigned hat = GET_HAT(bind->joykey); unsigned hat = GET_HAT(bind->joykey);
const char *dir = NULL; const char *dir = NULL;
@ -1046,31 +819,37 @@ static void save_keybind_hat(config_file_t *conf,
char config[16]; char config[16];
snprintf(config, sizeof(config), "h%u%s", hat, dir); snprintf(config, sizeof(config), "h%u%s", hat, dir);
config_set_string(conf, map->btn, config); config_set_string(conf, key, config);
} }
#endif #endif
static void save_keybind_joykey(config_file_t *conf, static void save_keybind_joykey(config_file_t *conf, const char *prefix, const char *base,
const struct bind_map *map, const struct retro_keybind *bind) const struct retro_keybind *bind)
{ {
char key[64];
snprintf(key, sizeof(key), "%s_%s_btn", prefix, base);
if (bind->joykey == NO_BTN) if (bind->joykey == NO_BTN)
config_set_string(conf, map->btn, "nul"); config_set_string(conf, key, "nul");
#ifndef RARCH_CONSOLE // Consoles don't understand hats. #ifndef RARCH_CONSOLE // Consoles don't understand hats.
else if (GET_HAT_DIR(bind->joykey)) else if (GET_HAT_DIR(bind->joykey))
save_keybind_hat(conf, map, bind); save_keybind_hat(conf, key, bind);
#endif #endif
else else
config_set_uint64(conf, map->btn, bind->joykey); config_set_uint64(conf, key, bind->joykey);
} }
static void save_keybind_axis(config_file_t *conf, static void save_keybind_axis(config_file_t *conf, const char *prefix, const char *base,
const struct bind_map *map, const struct retro_keybind *bind) const struct retro_keybind *bind)
{ {
char key[64];
snprintf(key, sizeof(key), "%s_%s_axis", prefix, base);
unsigned axis = 0; unsigned axis = 0;
char dir = '\0'; char dir = '\0';
if (bind->joyaxis == AXIS_NONE) if (bind->joyaxis == AXIS_NONE)
config_set_string(conf, map->axis, "nul"); config_set_string(conf, key, "nul");
else if (AXIS_NEG_GET(bind->joyaxis) != AXIS_DIR_NONE) else if (AXIS_NEG_GET(bind->joyaxis) != AXIS_DIR_NONE)
{ {
dir = '-'; dir = '-';
@ -1082,28 +861,33 @@ static void save_keybind_axis(config_file_t *conf,
axis = AXIS_POS_GET(bind->joyaxis); axis = AXIS_POS_GET(bind->joyaxis);
} }
char config[16]; if (dir)
snprintf(config, sizeof(config), "%c%u", dir, axis); {
config_set_string(conf, map->axis, config); char config[16];
snprintf(config, sizeof(config), "%c%u", dir, axis);
config_set_string(conf, key, config);
}
} }
static void save_keybind(config_file_t *conf, static void save_keybind(config_file_t *conf, const char *prefix, const char *base,
const struct bind_map *map, const struct retro_keybind *bind) const struct retro_keybind *bind)
{ {
if (!map->valid) if (!bind->valid)
return; return;
save_keybind_key(conf, map, bind); save_keybind_key(conf, prefix, base, bind);
save_keybind_joykey(conf, map, bind); save_keybind_joykey(conf, prefix, base, bind);
save_keybind_axis(conf, map, bind); save_keybind_axis(conf, prefix, base, bind);
#ifdef RARCH_CONSOLE
#endif
} }
static void save_keybinds_player(config_file_t *conf, unsigned i) static void save_keybinds_player(config_file_t *conf, unsigned player)
{ {
for (unsigned j = 0; j < RARCH_BIND_LIST_END; j++) for (unsigned i = 0; input_config_bind_map[i].valid; i++)
save_keybind(conf, &bind_maps[i][j], &g_settings.input.binds[i][j]); {
const char *prefix = input_config_get_prefix(player, input_config_bind_map[i].meta);
if (prefix)
save_keybind(conf, prefix, input_config_bind_map[i].base, &g_settings.input.binds[player][i]);
}
} }
#ifdef RARCH_CONSOLE #ifdef RARCH_CONSOLE

View File

@ -37,6 +37,7 @@ static int g_player = 1;
static int g_joypad = 0; static int g_joypad = 0;
static char *g_in_path = NULL; static char *g_in_path = NULL;
static char *g_out_path = NULL; static char *g_out_path = NULL;
static char *g_auto_path = NULL;
static bool g_use_misc = false; static bool g_use_misc = false;
static void print_help(void) static void print_help(void)
@ -44,91 +45,18 @@ static void print_help(void)
puts("=================="); puts("==================");
puts("retroarch-joyconfig"); puts("retroarch-joyconfig");
puts("=================="); puts("==================");
puts("Usage: retroarch-joyconfig [ -p/--player <1-5> | -j/--joypad <num> | -i/--input <file> | -o/--output <file> | -h/--help ]"); puts("Usage: retroarch-joyconfig [ -p/--player <1-8> | -j/--joypad <num> | -i/--input <file> | -o/--output <file> | -h/--help ]");
puts(""); puts("");
puts("-p/--player: Which player to configure for (1 up to and including 5)."); puts("-p/--player: Which player to configure for (1 up to and including 8).");
puts("-j/--joypad: Which joypad to use when configuring (first joypad is 0)."); puts("-j/--joypad: Which joypad to use when configuring (first joypad is 0).");
puts("-i/--input: Input file to configure with. Binds will be added on or overwritten."); puts("-i/--input: Input file to configure with. Binds will be added on or overwritten.");
puts("\tIf not selected, an empty config will be used as a base."); puts("\tIf not selected, an empty config will be used as a base.");
puts("-o/--output: Output file to write to. If not selected, config file will be dumped to stdout."); puts("-o/--output: Output file to write to. If not selected, config file will be dumped to stdout.");
puts("-a/--autoconfig: Outputs an autoconfig file for joypad which was configured.");
puts("-m/--misc: Also configure various keybinds that are not directly libretro related. These configurations are for player 1 only."); puts("-m/--misc: Also configure various keybinds that are not directly libretro related. These configurations are for player 1 only.");
puts("-h/--help: This help."); puts("-h/--help: This help.");
} }
struct bind
{
const char *keystr;
const char *confbtn[MAX_PLAYERS];
const char *confaxis[MAX_PLAYERS];
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 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("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),
MISC_BIND("Hotkey enable", enable_hotkey),
MISC_BIND("Volume up", volume_up),
MISC_BIND("Volume down", volume_down),
MISC_BIND("Next overlay", overlay_next),
MISC_BIND("Disk eject toggle", disk_eject_toggle),
MISC_BIND("Disk next cycle", disk_next),
MISC_BIND("Grab mouse toggle", grab_mouse_toggle),
MISC_BIND("Menu toggle", menu_toggle),
};
#define MAX_BUTTONS 32 #define MAX_BUTTONS 32
#define MAX_AXES 32 #define MAX_AXES 32
#define MAX_HATS 32 #define MAX_HATS 32
@ -163,7 +91,7 @@ static void poll_joypad(const rarch_joypad_driver_t *driver,
} }
} }
static void get_binds(config_file_t *conf, int player, int joypad) static void get_binds(config_file_t *conf, config_file_t *auto_conf, int player, int joypad)
{ {
const rarch_joypad_driver_t *driver = input_joypad_init_first(); const rarch_joypad_driver_t *driver = input_joypad_init_first();
if (!driver) if (!driver)
@ -179,6 +107,14 @@ static void get_binds(config_file_t *conf, int player, int joypad)
} }
fprintf(stderr, "Found joypad driver: %s\n", driver->ident); fprintf(stderr, "Found joypad driver: %s\n", driver->ident);
const char *joypad_name = input_joypad_name(driver, joypad);
fprintf(stderr, "Using joypad: %s\n", joypad_name ? joypad_name : "Unknown");
if (joypad_name && auto_conf)
{
config_set_string(auto_conf, "input_device", joypad_name);
config_set_string(auto_conf, "input_driver", driver->ident);
}
int16_t initial_axes[MAX_AXES] = {0}; int16_t initial_axes[MAX_AXES] = {0};
struct poll_data old_poll = {{0}}; struct poll_data old_poll = {{0}};
@ -187,6 +123,9 @@ static void get_binds(config_file_t *conf, int player, int joypad)
int last_axis = -1; int last_axis = -1;
bool block_axis = false; bool block_axis = false;
poll_joypad(driver, joypad, &old_poll);
fprintf(stderr, "\nJoypads tend to have stale state after opened.\nPress some buttons and move some axes around to make sure joypad state is completely neutral before proceeding.\nWhen done, press Enter ... ");
getchar();
poll_joypad(driver, joypad, &old_poll); poll_joypad(driver, joypad, &old_poll);
for (int i = 0; i < MAX_AXES; i++) for (int i = 0; i < MAX_AXES; i++)
@ -200,19 +139,29 @@ static void get_binds(config_file_t *conf, int player, int joypad)
// If default negative, we can't trigger on the negative axis, and similar with defaulted positive axes. // If default negative, we can't trigger on the negative axis, and similar with defaulted positive axes.
if (initial) if (initial)
fprintf(stderr, "Axis %d is defaulted to %s axis value of %d\n", i, initial > 0 ? "positive" : "negative", initial); fprintf(stderr, "Axis %d is defaulted to %s axis value of %d.\n", i, initial > 0 ? "positive" : "negative", initial);
initial_axes[i] = initial; initial_axes[i] = initial;
} }
for (int i = 0; i < MAX_BUTTONS; i++)
{
if (old_poll.buttons[i])
fprintf(stderr, "Button %d was initially pressed. This indicates broken initial state.\n", i);
}
fprintf(stderr, "Configuring binds for player #%d on joypad #%d.\n\n", fprintf(stderr, "Configuring binds for player #%d on joypad #%d.\n\n",
player + 1, joypad); player + 1, joypad);
for (unsigned i = 0; i < sizeof(binds) / sizeof(binds[0]) && (g_use_misc || !binds[i].is_misc) ; i++) for (unsigned i = 0; input_config_bind_map[i].valid &&
(g_use_misc || !input_config_bind_map[i].meta); i++)
{ {
fprintf(stderr, "%s\n", binds[i].keystr); if (i == RARCH_TURBO_ENABLE)
continue;
unsigned player_index = binds[i].is_misc ? 0 : player; fprintf(stderr, "%s\n", input_config_bind_map[i].desc);
unsigned player_index = input_config_bind_map[i].meta ? 0 : player;
for (;;) for (;;)
{ {
@ -229,51 +178,74 @@ static void get_binds(config_file_t *conf, int player, int joypad)
if (new_poll.buttons[j] && !old_poll.buttons[j]) if (new_poll.buttons[j] && !old_poll.buttons[j])
{ {
fprintf(stderr, "\tJoybutton pressed: %u\n", j); fprintf(stderr, "\tJoybutton pressed: %u\n", j);
config_set_int(conf, binds[i].confbtn[player_index], j); char key[64];
snprintf(key, sizeof(key), "%s_%s_btn",
input_config_get_prefix(player_index, input_config_bind_map[i].meta), input_config_bind_map[i].base);
config_set_int(conf, key, j);
if (auto_conf)
{
snprintf(key, sizeof(key), "input_%s_btn",
input_config_bind_map[i].base);
config_set_int(auto_conf, key, j);
}
goto out; goto out;
} }
} }
for (int j = 0; j < MAX_AXES; j++) for (int j = 0; j < MAX_AXES; j++)
{ {
if (new_poll.axes[j] != old_poll.axes[j]) if (new_poll.axes[j] == old_poll.axes[j])
continue;
int16_t value = new_poll.axes[j];
bool same_axis = last_axis == j;
bool require_negative = initial_axes[j] > 0;
bool require_positive = initial_axes[j] < 0;
// Block the axis config until we're sure axes have returned to their neutral state.
if (same_axis)
{ {
int16_t value = new_poll.axes[j]; if (abs(value) < 10000 ||
bool same_axis = last_axis == j; (require_positive && value < 0) ||
bool require_negative = initial_axes[j] > 0; (require_negative && value > 0))
bool require_positive = initial_axes[j] < 0; block_axis = false;
}
// Block the axis config until we're sure axes have returned to their neutral state. // If axes are in their neutral state, we can't allow it.
if (same_axis) if (require_negative && value >= 0)
continue;
if (require_positive && value <= 0)
continue;
if (block_axis)
continue;
if (abs(value) > 20000)
{
last_axis = j;
fprintf(stderr, "\tJoyaxis moved: Axis %d, Value %d\n", j, value);
char buf[8];
snprintf(buf, sizeof(buf),
value > 0 ? "+%d" : "-%d", j);
char key[64];
snprintf(key, sizeof(key), "%s_%s_axis",
input_config_get_prefix(player_index, input_config_bind_map[i].meta), input_config_bind_map[i].base);
config_set_string(conf, key, buf);
if (auto_conf)
{ {
if (abs(value) < 10000 || snprintf(key, sizeof(key), "input_%s_axis",
(require_positive && value < 0) || input_config_bind_map[i].base);
(require_negative && value > 0)) config_set_string(auto_conf, key, buf);
block_axis = false;
} }
// If axes are in their neutral state, we can't allow it. block_axis = true;
if (require_negative && value >= 0) goto out;
continue;
if (require_positive && value <= 0)
continue;
if (block_axis)
continue;
if (abs(value) > 20000)
{
last_axis = j;
fprintf(stderr, "\tJoyaxis moved: Axis %d, Value %d\n", j, value);
char buf[8];
snprintf(buf, sizeof(buf),
value > 0 ? "+%d" : "-%d", j);
config_set_string(conf, binds[i].confaxis[player_index], buf);
block_axis = true;
goto out;
}
} }
} }
@ -297,7 +269,20 @@ static void get_binds(config_file_t *conf, int player, int joypad)
fprintf(stderr, "\tJoyhat moved: Hat %d, direction %s\n", j, quark); fprintf(stderr, "\tJoyhat moved: Hat %d, direction %s\n", j, quark);
char buf[16]; char buf[16];
snprintf(buf, sizeof(buf), "h%d%s", j, quark); snprintf(buf, sizeof(buf), "h%d%s", j, quark);
config_set_string(conf, binds[i].confbtn[player_index], buf);
char key[64];
snprintf(key, sizeof(key), "%s_%s_btn",
input_config_get_prefix(player_index, input_config_bind_map[i].meta), input_config_bind_map[i].base);
config_set_string(conf, key, buf);
if (auto_conf)
{
snprintf(key, sizeof(key), "input_%s_btn",
input_config_bind_map[i].base);
config_set_string(auto_conf, key, buf);
}
goto out; goto out;
} }
} }
@ -309,10 +294,11 @@ out:
static void parse_input(int argc, char *argv[]) static void parse_input(int argc, char *argv[])
{ {
char optstring[] = "i:o:p:j:hm"; char optstring[] = "i:o:a:p:j:hm";
struct option opts[] = { struct option opts[] = {
{ "input", 1, NULL, 'i' }, { "input", 1, NULL, 'i' },
{ "output", 1, NULL, 'o' }, { "output", 1, NULL, 'o' },
{ "autoconfig", 1, NULL, 'a' },
{ "player", 1, NULL, 'p' }, { "player", 1, NULL, 'p' },
{ "joypad", 1, NULL, 'j' }, { "joypad", 1, NULL, 'j' },
{ "help", 0, NULL, 'h' }, { "help", 0, NULL, 'h' },
@ -341,6 +327,10 @@ static void parse_input(int argc, char *argv[])
g_out_path = strdup(optarg); g_out_path = strdup(optarg);
break; break;
case 'a':
g_auto_path = strdup(optarg);
break;
case 'm': case 'm':
g_use_misc = true; g_use_misc = true;
break; break;
@ -410,12 +400,22 @@ int main(int argc, char *argv[])
config_set_int(conf, index_list[g_player - 1], g_joypad); config_set_int(conf, index_list[g_player - 1], g_joypad);
get_binds(conf, g_player - 1, g_joypad); config_file_t *auto_conf = NULL;
if (g_auto_path)
auto_conf = config_file_new(NULL);
get_binds(conf, auto_conf, g_player - 1, g_joypad);
config_file_write(conf, g_out_path); config_file_write(conf, g_out_path);
config_file_free(conf); config_file_free(conf);
if (g_in_path) if (auto_conf)
free(g_in_path); {
if (g_out_path) fprintf(stderr, "Writing autoconfig profile to: %s.\n", g_auto_path);
free(g_out_path); config_file_write(auto_conf, g_auto_path);
config_file_free(auto_conf);
}
free(g_in_path);
free(g_out_path);
free(g_auto_path);
return 0; return 0;
} }