From d21046ace8ee5db18f555122bbc344ce90c4ffcd Mon Sep 17 00:00:00 2001 From: Themaister Date: Sun, 8 Dec 2013 16:05:05 +0100 Subject: [PATCH] Add keyboard layout support to udev. Adds input_keyboard_layout setting. --- general.h | 1 + input/udev_input.c | 63 ++++++++++++++++++++++++++++++---------------- retroarch.cfg | 4 +++ settings.c | 3 +++ 4 files changed, 50 insertions(+), 21 deletions(-) diff --git a/general.h b/general.h index 7a2825c4ac..a1695f223d 100644 --- a/general.h +++ b/general.h @@ -220,6 +220,7 @@ struct settings { char driver[32]; char joypad_driver[32]; + char keyboard_layout[64]; struct retro_keybind binds[MAX_PLAYERS][RARCH_BIND_LIST_END]; // Set by autoconfiguration in joypad_autoconfig_dir. Does not override main binds. diff --git a/input/udev_input.c b/input/udev_input.c index 162ad58ec0..4a73948f87 100644 --- a/input/udev_input.c +++ b/input/udev_input.c @@ -15,6 +15,8 @@ #include "input_common.h" #include "../general.h" +#include "../conf/config_file.h" +#include "../file_path.h" #include #include #include @@ -111,31 +113,31 @@ static inline void set_bit(uint8_t *buf, unsigned bit) } #ifdef HAVE_XKBCOMMON +// FIXME: Don't handle composed and dead-keys properly. Waiting for support in libxkbcommon ... static void handle_xkb(udev_input_t *udev, int code, int value) { + unsigned i; int xk_code = code + 8; // Convert Linux evdev to X11 (xkbcommon docs say so at least ...) if (value == 2) // Repeat, release first explicitly. xkb_state_update_key(udev->xkb_state, xk_code, XKB_KEY_UP); - uint32_t utf32 = 0; + const xkb_keysym_t *syms = NULL; + unsigned num_syms = 0; if (value) - { - xkb_keysym_t sym = xkb_state_key_get_one_sym(udev->xkb_state, xk_code); - utf32 = xkb_keysym_to_utf32(sym); - } + num_syms = xkb_state_key_get_syms(udev->xkb_state, xk_code, &syms); xkb_state_update_key(udev->xkb_state, xk_code, value ? XKB_KEY_DOWN : XKB_KEY_UP); // Build mod state. uint16_t mod = 0; - unsigned i; for (i = 0; i < ARRAY_SIZE(udev->mod_map); i++) if (udev->mod_map[i].index != XKB_MOD_INVALID) mod |= xkb_state_mod_index_is_active(udev->xkb_state, udev->mod_map[i].index, XKB_STATE_MODS_EFFECTIVE) > 0 ? udev->mod_map[i].bit : 0; - if (g_extern.system.key_event) - g_extern.system.key_event(value, input_translate_keysym_to_rk(code), utf32, mod); + g_extern.system.key_event(value, input_translate_keysym_to_rk(code), num_syms ? xkb_keysym_to_utf32(syms[0]) : 0, mod); + for (i = 1; i < num_syms; i++) + g_extern.system.key_event(value, RETROK_UNKNOWN, xkb_keysym_to_utf32(syms[i]), mod); } #endif @@ -150,7 +152,7 @@ static void udev_handle_keyboard(udev_input_t *udev, const struct input_event *e clear_bit(udev->key_state, event->code); #ifdef HAVE_XKBCOMMON - if (udev->xkb_state) + if (udev->xkb_state && g_extern.system.key_event) handle_xkb(udev, event->code, event->value); #endif break; @@ -630,22 +632,41 @@ static void *udev_input_init(void) #ifdef HAVE_XKBCOMMON udev->xkb_ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - // Uses "default" layout, which seems to be US layout. if (udev->xkb_ctx) - udev->xkb_map = xkb_keymap_new_from_names(udev->xkb_ctx, NULL, XKB_MAP_COMPILE_NO_FLAGS); + { + struct xkb_rule_names rule = {0}; + rule.rules = "evdev"; + + struct string_list *list = NULL; + + if (*g_settings.input.keyboard_layout) + { + list = string_split(g_settings.input.keyboard_layout, ":"); + if (list && list->size >= 2) + rule.variant = list->elems[1].data; + if (list && list->size >= 1) + rule.layout = list->elems[0].data; + } + + udev->xkb_map = xkb_keymap_new_from_names(udev->xkb_ctx, &rule, XKB_MAP_COMPILE_NO_FLAGS); + if (list) + string_list_free(list); + } if (udev->xkb_map) + { udev->xkb_state = xkb_state_new(udev->xkb_map); - udev->mod_map[0].index = xkb_keymap_mod_get_index(udev->xkb_map, XKB_MOD_NAME_CAPS); - udev->mod_map[0].bit = RETROKMOD_CAPSLOCK; - udev->mod_map[1].index = xkb_keymap_mod_get_index(udev->xkb_map, XKB_MOD_NAME_SHIFT); - udev->mod_map[1].bit = RETROKMOD_SHIFT; - udev->mod_map[2].index = xkb_keymap_mod_get_index(udev->xkb_map, XKB_MOD_NAME_CTRL); - udev->mod_map[2].bit = RETROKMOD_CTRL; - udev->mod_map[3].index = xkb_keymap_mod_get_index(udev->xkb_map, XKB_MOD_NAME_ALT); - udev->mod_map[3].bit = RETROKMOD_ALT; - udev->mod_map[4].index = xkb_keymap_mod_get_index(udev->xkb_map, XKB_MOD_NAME_LOGO); - udev->mod_map[4].bit = RETROKMOD_META; + udev->mod_map[0].index = xkb_keymap_mod_get_index(udev->xkb_map, XKB_MOD_NAME_CAPS); + udev->mod_map[0].bit = RETROKMOD_CAPSLOCK; + udev->mod_map[1].index = xkb_keymap_mod_get_index(udev->xkb_map, XKB_MOD_NAME_SHIFT); + udev->mod_map[1].bit = RETROKMOD_SHIFT; + udev->mod_map[2].index = xkb_keymap_mod_get_index(udev->xkb_map, XKB_MOD_NAME_CTRL); + udev->mod_map[2].bit = RETROKMOD_CTRL; + udev->mod_map[3].index = xkb_keymap_mod_get_index(udev->xkb_map, XKB_MOD_NAME_ALT); + udev->mod_map[3].bit = RETROKMOD_ALT; + udev->mod_map[4].index = xkb_keymap_mod_get_index(udev->xkb_map, XKB_MOD_NAME_LOGO); + udev->mod_map[4].bit = RETROKMOD_META; + } #endif udev->epfd = epoll_create(32); diff --git a/retroarch.cfg b/retroarch.cfg index 3a3b38c4b2..8758f08fcb 100644 --- a/retroarch.cfg +++ b/retroarch.cfg @@ -222,6 +222,10 @@ # Joypad driver. (Valid: linuxraw, sdl, dinput) # input_joypad_driver = +# Keyboard layout for input driver if applicable (udev/evdev for now). +# Syntax is either just layout (e.g. "no"), or a layout and variant separated with colon ("no:nodeadkeys"). +# input_keyboard_layout = + # Defines axis threshold. Possible values are [0.0, 1.0] # input_axis_threshold = 0.5 diff --git a/settings.c b/settings.c index 30695b0e72..4cad6dfbcf 100644 --- a/settings.c +++ b/settings.c @@ -310,6 +310,7 @@ void config_set_defaults(void) g_settings.input.overlay_scale = 1.0f; g_settings.input.debug_enable = input_debug_enable; g_settings.input.autodetect_enable = input_autodetect_enable; + *g_settings.input.keyboard_layout = '\0'; #ifdef ANDROID g_settings.input.back_behavior = BACK_BUTTON_QUIT; #endif @@ -769,6 +770,7 @@ bool config_load_file(const char *path) CONFIG_GET_PATH(audio.dsp_plugin, "audio_dsp_plugin"); CONFIG_GET_STRING(input.driver, "input_driver"); CONFIG_GET_STRING(input.joypad_driver, "input_joypad_driver"); + CONFIG_GET_STRING(input.keyboard_layout, "input_keyboard_layout"); if (!*g_settings.libretro) CONFIG_GET_PATH(libretro, "libretro_path"); @@ -1193,6 +1195,7 @@ bool config_save_file(const char *path) config_set_string(conf, "input_driver", g_settings.input.driver); config_set_string(conf, "input_joypad_driver", g_settings.input.joypad_driver); + config_set_string(conf, "input_keyboard_layout", g_settings.input.keyboard_layout); for (i = 0; i < MAX_PLAYERS; i++) { char cfg[64];