diff --git a/Makefile b/Makefile index 9d13690ebd..d9e3548e13 100644 --- a/Makefile +++ b/Makefile @@ -39,14 +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.o \ + tools/input_common_launch.o \ file_path.o \ compat/compat.o \ conf/config_file.o \ @@ -374,10 +374,14 @@ tools/linuxraw_joypad.o: input/linuxraw_joypad.c @$(if $(Q), $(shell echo echo CC $<),) $(Q)$(CC) $(CFLAGS) $(DEFINES) -DIS_JOYCONFIG -c -o $@ $< -tools/input_common.o: input/input_common.c +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 $<),) $(Q)$(CC) $(CFLAGS) $(ASFLAGS) $(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/general.h b/general.h index 1585c55cf1..76eac149a7 100644 --- a/general.h +++ b/general.h @@ -217,6 +217,10 @@ 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]; + float axis_threshold; int joypad_map[MAX_PLAYERS]; unsigned device[MAX_PLAYERS]; @@ -236,6 +240,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 fd997a575f..f30e12862b 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) diff --git a/input/input_common.c b/input/input_common.c index 8cd9ecdef1..4cb3a5b327 100644 --- a/input/input_common.c +++ b/input/input_common.c @@ -37,6 +37,8 @@ #include #endif +#include "../file.h" + static const rarch_joypad_driver_t *joypad_drivers[] = { #ifndef IS_RETROLAUNCH #ifdef HAVE_DINPUT @@ -95,7 +97,7 @@ const char *input_joypad_name(const rarch_joypad_driver_t *driver, unsigned joyp } 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; @@ -104,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; } @@ -122,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); @@ -134,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; } @@ -764,3 +794,69 @@ void input_config_parse_joy_axis(config_file_t *conf, const char *prefix, } } +#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; + + 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; + } + + 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)) + { + 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); + + 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 64c5083d7c..4061be10bc 100644 --- a/input/input_common.h +++ b/input/input_common.h @@ -75,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); @@ -136,5 +136,7 @@ void input_config_parse_joy_button(config_file_t *conf, const char *prefix, 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 983bc75ba0..33c64083a9 100644 --- a/input/linuxraw_joypad.c +++ b/input/linuxraw_joypad.c @@ -94,6 +94,8 @@ static void linuxraw_joypad_init_pad(const char *path, struct linuxraw_joypad *p snprintf(msg, sizeof(msg), "Joypad #%u (%s) connected.", (unsigned)(pad - g_pads), pad->ident); msg_queue_push(g_extern.msg_queue, msg, 0, 60); } + + input_config_autoconfigure_joypad(pad - g_pads, pad->ident, "linuxraw"); #endif } @@ -151,6 +153,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. @@ -159,6 +165,11 @@ 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]); + +#ifndef IS_JOYCONFIG + if (*g_pads[index].ident) + input_config_autoconfigure_joypad(index, g_pads[index].ident, "linuxraw"); +#endif } } } 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/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 0c2912e40b..5a1cd5a8d9 100644 --- a/settings.c +++ b/settings.c @@ -222,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++) @@ -234,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 @@ -651,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");