diff --git a/Makefile b/Makefile index 2faa52b83e..d9e3548e13 100644 --- a/Makefile +++ b/Makefile @@ -39,13 +39,14 @@ JOYCONFIG_OBJ = tools/retroarch-joyconfig.o \ conf/config_file.o \ file_path.o \ compat/compat.o \ - input/input_common.o + tools/input_common_joyconfig.o RETROLAUNCH_OBJ = tools/retrolaunch/main.o \ tools/retrolaunch/sha1.o \ tools/retrolaunch/parser.o \ tools/retrolaunch/cd_detect.o \ tools/retrolaunch/rl_fnmatch.o \ + tools/input_common_launch.o \ file_path.o \ compat/compat.o \ conf/config_file.o \ @@ -371,7 +372,15 @@ tools/retrolaunch/retrolaunch: $(RETROLAUNCH_OBJ) tools/linuxraw_joypad.o: input/linuxraw_joypad.c @$(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) @$(if $(Q), $(shell echo echo AS $<),) diff --git a/Makefile.win b/Makefile.win index 61f096c903..35a3e76472 100644 --- a/Makefile.win +++ b/Makefile.win @@ -42,7 +42,7 @@ JOBJ := conf/config_file.o \ tools/retroarch-joyconfig.o \ compat/compat.o \ file_path.o \ - input/input_common.o \ + tools/input_common_joyconfig.o \ input/dinput.o CC = gcc @@ -254,6 +254,10 @@ $(TARGET): $(OBJ) @$(if $(Q), $(shell echo echo CC $<),) $(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 @$(if $(Q), $(shell echo echo CXX $<),) $(Q)$(CXX) $(CXXFLAGS) $(DEFINES) -c -o $@ $< diff --git a/config.def.h b/config.def.h index 20d57fa6cc..e561ab5173 100644 --- a/config.def.h +++ b/config.def.h @@ -413,11 +413,9 @@ static const unsigned turbo_duty_cycle = 3; // Enable input debugging output. static const bool input_debug_enable = false; -#ifdef ANDROID // Enable input auto-detection. Will attempt to autoconfigure // gamepads, plug-and-play style. static const bool input_autodetect_enable = true; -#endif #ifndef IS_SALAMANDER diff --git a/docs/retroarch-joyconfig.1 b/docs/retroarch-joyconfig.1 index f36df4d243..c3117fe61b 100644 --- a/docs/retroarch-joyconfig.1 +++ b/docs/retroarch-joyconfig.1 @@ -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 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 \fB--misc, -m\fR Also configure some binds that aren't directly related to RetroPad, such as save states, fullscreen toggling, etc. diff --git a/general.h b/general.h index 1585c55cf1..82aac61198 100644 --- a/general.h +++ b/general.h @@ -217,6 +217,11 @@ struct settings { char driver[32]; 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; int joypad_map[MAX_PLAYERS]; unsigned device[MAX_PLAYERS]; @@ -236,6 +241,8 @@ struct settings char overlay[PATH_MAX]; float overlay_opacity; + + char autoconfig_dir[PATH_MAX]; } input; char core_options_path[PATH_MAX]; diff --git a/input/dinput.c b/input/dinput.c index c7e66f23c9..8865395e23 100644 --- a/input/dinput.c +++ b/input/dinput.c @@ -189,7 +189,7 @@ static bool dinput_is_pressed(struct dinput_input *di, const struct retro_keybin return false; 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) @@ -555,6 +555,13 @@ static bool dinput_joypad_query_pad(unsigned pad) 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 = { dinput_joypad_init, dinput_joypad_query_pad, @@ -562,6 +569,7 @@ const rarch_joypad_driver_t dinput_joypad = { dinput_joypad_button, dinput_joypad_axis, dinput_joypad_poll, + dinput_joypad_name, "dinput", }; diff --git a/input/input_common.c b/input/input_common.c index 02c564f247..1b060e6819 100644 --- a/input/input_common.c +++ b/input/input_common.c @@ -18,6 +18,7 @@ #include #include "../general.h" +#include "../driver.h" #ifdef HAVE_CONFIG_H #include "../config.h" #endif @@ -36,7 +37,10 @@ #include #endif +#include "../file.h" + static const rarch_joypad_driver_t *joypad_drivers[] = { +#ifndef IS_RETROLAUNCH #ifdef HAVE_DINPUT &dinput_joypad, #endif @@ -46,11 +50,13 @@ static const rarch_joypad_driver_t *joypad_drivers[] = { #ifdef HAVE_SDL &sdl_joypad, #endif +#endif + NULL, }; 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) { @@ -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) { - for (unsigned i = 0; i < ARRAY_SIZE(joypad_drivers); i++) + for (unsigned i = 0; joypad_drivers[i]; i++) { if (joypad_drivers[i]->init()) { @@ -82,8 +88,16 @@ void input_joypad_poll(const rarch_joypad_driver_t *driver) 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, - unsigned port, const struct retro_keybind *key) + unsigned port, const struct retro_keybind *binds, unsigned key) { if (!driver) return false; @@ -92,13 +106,24 @@ bool input_joypad_pressed(const rarch_joypad_driver_t *driver, if (joy_index < 0 || joy_index >= MAX_PLAYERS) 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; - 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; - 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; 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; int joy_index = g_settings.input.joypad_map[port]; - if (joy_index < 0) + if (joy_index < 0 || joy_index >= MAX_PLAYERS) 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_plus = 0; 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) return 0; - int16_t pressed_minus = abs(driver->axis(joy_index, bind_minus->joyaxis)); - int16_t pressed_plus = abs(driver->axis(joy_index, bind_plus->joyaxis)); + uint32_t axis_minus = bind_minus->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; if (res != 0) return res; - int16_t digital_left = driver->button(joy_index, (uint16_t)bind_minus->joykey) ? -0x7fff : 0; - int16_t digital_right = driver->button(joy_index, (uint16_t)bind_plus->joykey) ? 0x7fff : 0; + uint64_t key_minus = bind_minus->joykey; + 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; } @@ -162,6 +204,7 @@ bool input_joypad_hat_raw(const rarch_joypad_driver_t *driver, return driver->button(joypad, HAT_MAP(hat, hat_dir)); } +#ifndef IS_RETROLAUNCH 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) { @@ -194,6 +237,7 @@ bool input_translate_coord_viewport(int mouse_x, int mouse_y, *res_screen_y = scaled_screen_y; return true; } +#endif #ifdef HAVE_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]; } + +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 + diff --git a/input/input_common.h b/input/input_common.h index 68ac10c561..4061be10bc 100644 --- a/input/input_common.h +++ b/input/input_common.h @@ -17,6 +17,7 @@ #define INPUT_COMMON_H__ #include "../driver.h" +#include "../conf/config_file.h" #include 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); int16_t (*axis)(unsigned, uint32_t); void (*poll)(void); + const char *(*name)(unsigned); const char *ident; } 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); 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, 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); 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 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); 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 diff --git a/input/linuxraw_input.c b/input/linuxraw_input.c index 9ba370213d..bf1d764a0b 100644 --- a/input/linuxraw_input.c +++ b/input/linuxraw_input.c @@ -253,7 +253,7 @@ static bool linuxraw_bind_button_pressed(void *data, int key) { linuxraw_input_t *linuxraw = (linuxraw_input_t*)data; 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) @@ -264,7 +264,7 @@ static int16_t linuxraw_input_state(void *data, const struct retro_keybind **bin { case RETRO_DEVICE_JOYPAD: 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: return input_joypad_analog(linuxraw->joypad, port, index, id, binds[port]); diff --git a/input/linuxraw_joypad.c b/input/linuxraw_joypad.c index 388a5a4485..68700706ac 100644 --- a/input/linuxraw_joypad.c +++ b/input/linuxraw_joypad.c @@ -42,7 +42,7 @@ static struct linuxraw_joypad g_pads[MAX_PLAYERS]; static int g_notify; static int g_epoll; -#ifndef NO_MSG_QUEUE +#ifndef IS_JOYCONFIG static bool g_hotplug; #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) - return; + return false; // Device can have just been created, but not made accessible (yet). // IN_ATTRIB will signal when permissions change. if (access(path, R_OK) < 0) - return; + return false; 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); -#ifndef NO_MSG_QUEUE +#ifndef IS_JOYCONFIG if (g_hotplug) { char msg[512]; @@ -104,9 +104,13 @@ static void linuxraw_joypad_init_pad(const char *path, struct linuxraw_joypad *p event.events = EPOLLIN; event.data.ptr = pad; epoll_ctl(g_epoll, EPOLL_CTL_ADD, pad->fd, &event); + return true; } else + { RARCH_ERR("[Joypad]: Failed to open pad %s (error: %s).\n", path, strerror(errno)); + return false; + } } static void handle_plugged_pad(void) @@ -136,7 +140,7 @@ static void handle_plugged_pad(void) { if (g_pads[index].fd >= 0) { -#ifndef NO_MSG_QUEUE +#ifndef IS_JOYCONFIG if (g_hotplug) { char msg[512]; @@ -151,6 +155,10 @@ static void handle_plugged_pad(void) memset(g_pads[index].axes, 0, sizeof(g_pads[index].axes)); g_pads[index].fd = -1; *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. @@ -158,7 +166,12 @@ static void handle_plugged_pad(void) { char path[PATH_MAX]; 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]; snprintf(path, sizeof(path), "/dev/input/js%u", i); - linuxraw_joypad_init_pad(path, pad); - if (pad->fd >= 0) + if (linuxraw_joypad_init_pad(path, pad)) + { +#ifndef IS_JOYCONFIG + input_config_autoconfigure_joypad(i, pad->ident, "linuxraw"); +#endif poll_pad(pad); - + } +#ifndef IS_JOYCONFIG + else + input_config_autoconfigure_joypad(i, NULL, NULL); +#endif } g_notify = inotify_init(); @@ -223,7 +243,7 @@ static bool linuxraw_joypad_init(void) epoll_ctl(g_epoll, EPOLL_CTL_ADD, g_notify, &event); } -#ifndef NO_MSG_QUEUE +#ifndef IS_JOYCONFIG g_hotplug = true; #endif @@ -250,7 +270,7 @@ static void linuxraw_joypad_destroy(void) close(g_epoll); g_epoll = -1; -#ifndef NO_MSG_QUEUE +#ifndef IS_JOYCONFIG g_hotplug = false; #endif } @@ -292,6 +312,14 @@ static bool linuxraw_joypad_query_pad(unsigned pad) 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 = { linuxraw_joypad_init, linuxraw_joypad_query_pad, @@ -299,6 +327,7 @@ const rarch_joypad_driver_t linuxraw_joypad = { linuxraw_joypad_button, linuxraw_joypad_axis, linuxraw_joypad_poll, + linuxraw_joypad_name, "linuxraw", }; diff --git a/input/sdl_input.c b/input/sdl_input.c index e15ae5be2d..7941ace247 100644 --- a/input/sdl_input.c +++ b/input/sdl_input.c @@ -59,22 +59,19 @@ static bool sdl_key_pressed(int key) 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 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) { const struct retro_keybind *binds = g_settings.input.binds[0]; if (key >= 0 && key < RARCH_BIND_LIST_END) - { - const struct retro_keybind *bind = &binds[key]; - return sdl_is_pressed((sdl_input_t*)data, 0, bind); - } + return sdl_is_pressed((sdl_input_t*)data, 0, binds, key); else 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]; if (id < RARCH_BIND_LIST_END) - { - const struct retro_keybind *bind = &binds[id]; - return bind->valid && sdl_is_pressed(sdl, port_num, bind); - } + return binds[id].valid && sdl_is_pressed(sdl, port_num, binds, id); else return 0; } diff --git a/input/sdl_joypad.c b/input/sdl_joypad.c index 13bcd8cf34..812936be56 100644 --- a/input/sdl_joypad.c +++ b/input/sdl_joypad.c @@ -155,6 +155,14 @@ static bool sdl_joypad_query_pad(unsigned pad) 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 = { sdl_joypad_init, sdl_joypad_query_pad, @@ -162,6 +170,7 @@ const rarch_joypad_driver_t sdl_joypad = { sdl_joypad_button, sdl_joypad_axis, sdl_joypad_poll, + sdl_joypad_name, "sdl", }; diff --git a/input/x11_input.c b/input/x11_input.c index 26e252d717..a9ad4a36e7 100644 --- a/input/x11_input.c +++ b/input/x11_input.c @@ -89,7 +89,7 @@ static bool x_bind_button_pressed(void *data, int key) { x11_input_t *x11 = (x11_input_t*)data; 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) @@ -176,7 +176,7 @@ static int16_t x_input_state(void *data, const struct retro_keybind **binds, uns { case RETRO_DEVICE_JOYPAD: 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: return x_key_pressed(x11, id); diff --git a/ios/RetroArch/input/ios_input.c b/ios/RetroArch/input/ios_input.c index aba13ce1de..ae2a6cdf05 100644 --- a/ios/RetroArch/input/ios_input.c +++ b/ios/RetroArch/input/ios_input.c @@ -65,9 +65,9 @@ static bool ios_key_pressed(enum retro_key key) 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 @@ -107,7 +107,7 @@ static int16_t ios_input_state(void *data, const struct retro_keybind **binds, u switch (device) { 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: 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) { 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) diff --git a/retroarch.cfg b/retroarch.cfg index 36c4c7f54a..40f527d563 100644 --- a/retroarch.cfg +++ b/retroarch.cfg @@ -187,10 +187,18 @@ # Path to input overlay # input_overlay = -# Enable input auto-detection (used on Android). Will attempt to autoconfigure -# gamepads, Plug-and-Play style. +# Enable input auto-detection. Will attempt to autoconfigure +# joypads, Plug-and-Play style. # 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. # input_debug_enable = false @@ -227,13 +235,13 @@ # 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. +# 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_player2_joypad_index = 1 # input_player3_joypad_index = 2 # input_player4_joypad_index = 3 # 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_player7_joypad_index = 6 # input_player8_joypad_index = 7 diff --git a/settings.c b/settings.c index e3bdfdc6e7..5a1cd5a8d9 100644 --- a/settings.c +++ b/settings.c @@ -20,6 +20,7 @@ #include "config.def.h" #include "file.h" #include "compat/posix_string.h" +#include "input/input_common.h" #ifdef HAVE_CONFIG_H #include "config.h" @@ -221,6 +222,8 @@ void config_set_defaults(void) 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.autoconf_binds, g_settings.input.binds, sizeof(g_settings.input.binds)); + // Verify that binds are in proper order. for (int i = 0; i < MAX_PLAYERS; i++) 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.overlay_opacity = 1.0f; g_settings.input.debug_enable = input_debug_enable; -#ifdef ANDROID g_settings.input.autodetect_enable = input_autodetect_enable; +#ifdef ANDROID g_settings.input.back_behavior = BACK_BUTTON_QUIT; #endif @@ -650,8 +653,10 @@ bool config_load_file(const char *path) CONFIG_GET_FLOAT(input.overlay_opacity, "input_overlay_opacity"); CONFIG_GET_BOOL(input.debug_enable, "input_debug_enable"); -#ifdef ANDROID 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.icade_profile[0], "input_autodetect_icade_profile_pad1"); 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; } -struct bind_map +static void read_keybinds_keyboard(config_file_t *conf, unsigned player, unsigned index, + struct retro_keybind *bind) { - bool valid; - 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] = { + if (input_config_bind_map[index].valid && input_config_bind_map[index].base) { - DECL_PLAYER(1), - - DECLARE_BIND(toggle_fast_forward, RARCH_FAST_FORWARD_KEY), - 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; + const char *prefix = input_config_get_prefix(player, input_config_bind_map[index].meta); + if (prefix) + input_config_parse_key(conf, prefix, input_config_bind_map[index].base, bind); } } -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)) - return; - - char *dir = NULL; - uint16_t hat = strtoul(str, &dir, 0); - uint16_t hat_dir = 0; - - if (!dir) + if (input_config_bind_map[index].valid && input_config_bind_map[index].base) { - 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); -} - -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); - } + const char *prefix = input_config_get_prefix(player, input_config_bind_map[index].meta); + if (prefix) + input_config_parse_joy_button(conf, prefix, input_config_bind_map[index].base, bind); } } -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 (bind_maps[player][index].axis && - config_get_array(conf, bind_maps[player][index].axis, tmp, sizeof(tmp))) + if (input_config_bind_map[index].valid && input_config_bind_map[index].base) { - 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); - - } + const char *prefix = input_config_get_prefix(player, input_config_bind_map[index].meta); + if (prefix) + input_config_parse_joy_axis(conf, prefix, input_config_bind_map[index].base, bind); } } 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); - rarch_assert(bind); + struct retro_keybind *bind = &g_settings.input.binds[player][i]; + if (!bind->valid) + continue; read_keybinds_keyboard(conf, player, i, bind); read_keybinds_button(conf, player, i, bind); @@ -992,9 +763,12 @@ bool config_read_keybinds(const char *path) return true; } -static void save_keybind_key(config_file_t *conf, - const struct bind_map *map, const struct retro_keybind *bind) +static void save_keybind_key(config_file_t *conf, const char *prefix, const char *base, + const struct retro_keybind *bind) { + char key[64]; + snprintf(key, sizeof(key), "%s_%s", prefix, base); + char ascii[2] = {0}; const char *btn = ascii; @@ -1002,22 +776,21 @@ static void save_keybind_key(config_file_t *conf, ascii[0] = 'a' + (bind->key - RETROK_a); 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; } } } - config_set_string(conf, map->key, btn); + config_set_string(conf, key, btn); } #ifndef RARCH_CONSOLE -static void save_keybind_hat(config_file_t *conf, - const struct bind_map *map, const struct retro_keybind *bind) +static void save_keybind_hat(config_file_t *conf, const char *key, const struct retro_keybind *bind) { unsigned hat = GET_HAT(bind->joykey); const char *dir = NULL; @@ -1046,31 +819,37 @@ static void save_keybind_hat(config_file_t *conf, char config[16]; snprintf(config, sizeof(config), "h%u%s", hat, dir); - config_set_string(conf, map->btn, config); + config_set_string(conf, key, config); } #endif -static void save_keybind_joykey(config_file_t *conf, - const struct bind_map *map, const struct retro_keybind *bind) +static void save_keybind_joykey(config_file_t *conf, const char *prefix, const char *base, + const struct retro_keybind *bind) { + char key[64]; + snprintf(key, sizeof(key), "%s_%s_btn", prefix, base); + 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. else if (GET_HAT_DIR(bind->joykey)) - save_keybind_hat(conf, map, bind); + save_keybind_hat(conf, key, bind); #endif 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, - const struct bind_map *map, const struct retro_keybind *bind) +static void save_keybind_axis(config_file_t *conf, const char *prefix, const char *base, + const struct retro_keybind *bind) { + char key[64]; + snprintf(key, sizeof(key), "%s_%s_axis", prefix, base); + unsigned axis = 0; char dir = '\0'; 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) { dir = '-'; @@ -1082,28 +861,33 @@ static void save_keybind_axis(config_file_t *conf, axis = AXIS_POS_GET(bind->joyaxis); } - char config[16]; - snprintf(config, sizeof(config), "%c%u", dir, axis); - config_set_string(conf, map->axis, config); + if (dir) + { + 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, - const struct bind_map *map, const struct retro_keybind *bind) +static void save_keybind(config_file_t *conf, const char *prefix, const char *base, + const struct retro_keybind *bind) { - if (!map->valid) + if (!bind->valid) return; - save_keybind_key(conf, map, bind); - save_keybind_joykey(conf, map, bind); - save_keybind_axis(conf, map, bind); -#ifdef RARCH_CONSOLE -#endif + save_keybind_key(conf, prefix, base, bind); + save_keybind_joykey(conf, prefix, base, bind); + save_keybind_axis(conf, prefix, base, bind); } -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++) - save_keybind(conf, &bind_maps[i][j], &g_settings.input.binds[i][j]); + for (unsigned i = 0; input_config_bind_map[i].valid; i++) + { + 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 diff --git a/tools/retroarch-joyconfig.c b/tools/retroarch-joyconfig.c index ec7910443c..dad2e3d7a1 100644 --- a/tools/retroarch-joyconfig.c +++ b/tools/retroarch-joyconfig.c @@ -37,6 +37,7 @@ static int g_player = 1; static int g_joypad = 0; static char *g_in_path = NULL; static char *g_out_path = NULL; +static char *g_auto_path = NULL; static bool g_use_misc = false; static void print_help(void) @@ -44,91 +45,18 @@ static void print_help(void) puts("=================="); puts("retroarch-joyconfig"); puts("=================="); - puts("Usage: retroarch-joyconfig [ -p/--player <1-5> | -j/--joypad | -i/--input | -o/--output | -h/--help ]"); + puts("Usage: retroarch-joyconfig [ -p/--player <1-8> | -j/--joypad | -i/--input | -o/--output | -h/--help ]"); 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("-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("-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("-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_AXES 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(); 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); + 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}; 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; 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); 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 (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; } + 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", 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 (;;) { @@ -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]) { 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; } } 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]; - bool same_axis = last_axis == j; - bool require_negative = initial_axes[j] > 0; - bool require_positive = initial_axes[j] < 0; + if (abs(value) < 10000 || + (require_positive && value < 0) || + (require_negative && value > 0)) + block_axis = false; + } - // Block the axis config until we're sure axes have returned to their neutral state. - if (same_axis) + // If axes are in their neutral state, we can't allow it. + 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 || - (require_positive && value < 0) || - (require_negative && value > 0)) - block_axis = false; + snprintf(key, sizeof(key), "input_%s_axis", + input_config_bind_map[i].base); + config_set_string(auto_conf, key, buf); } - // If axes are in their neutral state, we can't allow it. - 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); - - config_set_string(conf, binds[i].confaxis[player_index], buf); - block_axis = true; - goto out; - } + 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); char buf[16]; 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; } } @@ -309,10 +294,11 @@ out: 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[] = { { "input", 1, NULL, 'i' }, { "output", 1, NULL, 'o' }, + { "autoconfig", 1, NULL, 'a' }, { "player", 1, NULL, 'p' }, { "joypad", 1, NULL, 'j' }, { "help", 0, NULL, 'h' }, @@ -341,6 +327,10 @@ static void parse_input(int argc, char *argv[]) g_out_path = strdup(optarg); break; + case 'a': + g_auto_path = strdup(optarg); + break; + case 'm': g_use_misc = true; break; @@ -410,12 +400,22 @@ int main(int argc, char *argv[]) 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_free(conf); - if (g_in_path) - free(g_in_path); - if (g_out_path) - free(g_out_path); + if (auto_conf) + { + fprintf(stderr, "Writing autoconfig profile to: %s.\n", g_auto_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; }