diff --git a/gfx/common/x11_common.c b/gfx/common/x11_common.c index 257c804704..99fff79ed7 100644 --- a/gfx/common/x11_common.c +++ b/gfx/common/x11_common.c @@ -79,6 +79,10 @@ static Atom g_x11_quit_atom; static XIM g_x11_xim; static XIC g_x11_xic; +static enum retro_key x11_keysym_lut[RETROK_LAST]; +static unsigned *x11_keysym_rlut = NULL; +static unsigned x11_keysym_rlut_size = 0; + static void x11_hide_mouse(Display *dpy, Window win) { Cursor no_ptr; @@ -352,9 +356,39 @@ void x11_exit_fullscreen(Display *dpy) XF86VidModeSetViewPort(dpy, DefaultScreen(dpy), 0, 0); } +static void x11_init_keyboard_lut(void) +{ + const struct rarch_key_map *map = rarch_key_map_x11; + const struct rarch_key_map *map_start = rarch_key_map_x11; + + memset(x11_keysym_lut, 0, sizeof(x11_keysym_lut)); + x11_keysym_rlut_size = 0; + + for (; map->rk != RETROK_UNKNOWN; map++) + { + x11_keysym_lut[map->rk] = (enum retro_key)map->sym; + if (map->sym > x11_keysym_rlut_size) + x11_keysym_rlut_size = map->sym; + } + + if (x11_keysym_rlut_size < 65536) + { + if (x11_keysym_rlut) + free(x11_keysym_rlut); + + x11_keysym_rlut = (unsigned*)calloc(++x11_keysym_rlut_size, sizeof(unsigned)); + + for (map = map_start; map->rk != RETROK_UNKNOWN; map++) + x11_keysym_rlut[map->sym] = (enum retro_key)map->rk; + } + else + x11_keysym_rlut_size = 0; +} + bool x11_create_input_context(Display *dpy, Window win, XIM *xim, XIC *xic) { x11_destroy_input_context(xim, xic); + x11_init_keyboard_lut(); g_x11_has_focus = true; *xim = XOpenIM(dpy, NULL, NULL, NULL); @@ -391,6 +425,14 @@ void x11_destroy_input_context(XIM *xim, XIC *xic) XCloseIM(*xim); *xim = NULL; } + + memset(x11_keysym_lut, 0, sizeof(x11_keysym_lut)); + if (x11_keysym_rlut) + { + free(x11_keysym_rlut); + x11_keysym_rlut = NULL; + } + x11_keysym_rlut_size = 0; } bool x11_get_metrics(void *data, @@ -433,6 +475,26 @@ bool x11_get_metrics(void *data, return true; } +static enum retro_key x11_translate_keysym_to_rk(unsigned sym) +{ + size_t i; + + /* Fast path */ + if (x11_keysym_rlut && sym < x11_keysym_rlut_size) + return (enum retro_key)x11_keysym_rlut[sym]; + + /* Slow path */ + for (i = 0; i < ARRAY_SIZE(x11_keysym_lut); i++) + { + if (x11_keysym_lut[i] != sym) + continue; + + return (enum retro_key)i; + } + + return RETROK_UNKNOWN; +} + static void x11_handle_key_event(unsigned keycode, XEvent *event, XIC ic, bool filter) { int i; @@ -486,7 +548,7 @@ static void x11_handle_key_event(unsigned keycode, XEvent *event, XIC ic, bool f /* Get the real keycode, that correctly ignores international layouts as windows code does. */ - key = input_keymaps_translate_keysym_to_rk(keycode); + key = x11_translate_keysym_to_rk(keycode); if (state & ShiftMask) mod |= RETROKMOD_SHIFT; diff --git a/input/drivers/udev_input.c b/input/drivers/udev_input.c index 1ce320d817..56e076b684 100644 --- a/input/drivers/udev_input.c +++ b/input/drivers/udev_input.c @@ -196,14 +196,41 @@ static void udev_handle_keyboard(void *data, else BIT_CLEAR(udev->state, keysym); -#ifdef UDEV_XKB_HANDLING - if (udev->xkb_handling && handle_xkb(keysym, event->value) == 0) - return; -#endif + /* TODO/FIXME: The udev driver is incomplete. + * When calling input_keyboard_event() the + * following parameters are omitted: + * - character: the localised Unicode/UTF-8 + * value of the pressed key + * - mod: the current keyboard modifier + * bitmask + * Without these values, input_keyboard_event() + * does not function correctly (e.g. it is + * impossible to use text entry in the menu). + * I cannot find any usable reference for + * converting a udev-returned key code into a + * localised Unicode/UTF-8 value, so for the + * time being we must rely on other sources: + * - If we are using an X11-based context driver, + * input_keyboard_event() is handled correctly + * in x11_common:x11_check_window() + * - If we are using KMS, input_keyboard_event() + * is handled correctly in + * keyboard_event_xkb:handle_xkb() + * If neither are available, then just call + * input_keyboard_event() without character and + * mod, and hope for the best... */ + + if (video_driver_display_type_get() != RARCH_DISPLAY_X11) + { +#ifdef UDEV_XKB_HANDLING + if (udev->xkb_handling && handle_xkb(keysym, event->value) == 0) + return; +#endif + input_keyboard_event(event->value, + input_keymaps_translate_keysym_to_rk(keysym), + 0, 0, RETRO_DEVICE_KEYBOARD); + } - input_keyboard_event(event->value, - input_keymaps_translate_keysym_to_rk(keysym), - 0, 0, RETRO_DEVICE_KEYBOARD); break; default: @@ -486,35 +513,32 @@ static void udev_handle_mouse(void *data, static int udev_input_add_device(udev_input_t *udev, enum udev_input_dev_type type, const char *devnode, device_handle_cb cb) { - unsigned char keycaps[(KEY_MAX / 8) + 1]; - unsigned char abscaps[(ABS_MAX / 8) + 1]; - int has_absolutes = 0; - int fd; + unsigned char keycaps[(KEY_MAX / 8) + 1] = {'\0'}; + unsigned char abscaps[(ABS_MAX / 8) + 1] = {'\0'}; + udev_input_device_t **tmp = NULL; + udev_input_device_t *device = NULL; + int has_absolutes = 0; + int fd = -1; + int ret = 0; struct stat st; #if defined(HAVE_EPOLL) struct epoll_event event; #elif defined(HAVE_KQUEUE) struct kevent event; #endif - struct input_absinfo absinfo; - udev_input_device_t **tmp; - udev_input_device_t *device = NULL; - memset(keycaps, '\0', sizeof (keycaps)); - memset(keycaps, '\0', sizeof (abscaps)); - - st.st_dev = 0; + st.st_dev = 0; if (stat(devnode, &st) < 0) - return false; + goto end; fd = open(devnode, O_RDONLY | O_NONBLOCK); if (fd < 0) - return false; + goto end; device = (udev_input_device_t*)calloc(1, sizeof(*device)); if (!device) - goto error; + goto end; device->fd = fd; device->dev = st.st_dev; @@ -526,46 +550,51 @@ static int udev_input_add_device(udev_input_t *udev, /* UDEV_INPUT_MOUSE may report in absolute coords too */ if (type == UDEV_INPUT_MOUSE || type == UDEV_INPUT_TOUCHPAD ) { + /* gotta have some buttons! return -1 to skip error logging for this:) */ if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof (keycaps)), keycaps) == -1) - return -1; /* gotta have some buttons! return -1 to skip error logging for this:) */ - - + { + ret = -1; + goto end; + } if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof (abscaps)), abscaps) != -1) { - if ( (test_bit(abscaps, ABS_X)) && (test_bit(abscaps, ABS_Y)) ) - { - /* might be a touchpad... */ - if (test_bit(keycaps, BTN_TOUCH)) - { - /* touchpad, touchscreen, or tablet. */ - has_absolutes = 1; - } - } - } - device->mouse.x_min = device->mouse.y_min = device->mouse.x_max = device->mouse.y_max = 0; + if ( (test_bit(abscaps, ABS_X)) && (test_bit(abscaps, ABS_Y)) ) + { + /* might be a touchpad... */ + if (test_bit(keycaps, BTN_TOUCH)) + { + /* touchpad, touchscreen, or tablet. */ + has_absolutes = 1; + } + } + } + + device->mouse.x_min = 0; + device->mouse.y_min = 0; + device->mouse.x_max = 0; + device->mouse.y_max = 0; if (has_absolutes) { - struct input_absinfo absinfo; - if (ioctl(fd, EVIOCGABS(ABS_X), &absinfo) == -1) - return 0; - device->mouse.x_min = absinfo.minimum; - device->mouse.x_max = absinfo.maximum; + struct input_absinfo absinfo; + if (ioctl(fd, EVIOCGABS(ABS_X), &absinfo) == -1) + goto end; + device->mouse.x_min = absinfo.minimum; + device->mouse.x_max = absinfo.maximum; - if (ioctl(fd, EVIOCGABS(ABS_Y), &absinfo) == -1) - return 0; - device->mouse.y_min = absinfo.minimum; - device->mouse.y_max = absinfo.maximum; + if (ioctl(fd, EVIOCGABS(ABS_Y), &absinfo) == -1) + goto end; + device->mouse.y_min = absinfo.minimum; + device->mouse.y_max = absinfo.maximum; } - } - tmp = ( udev_input_device_t**)realloc(udev->devices, + tmp = (udev_input_device_t**)realloc(udev->devices, (udev->num_devices + 1) * sizeof(*udev->devices)); if (!tmp) - goto error; + goto end; tmp[udev->num_devices++] = device; udev->devices = tmp; @@ -589,14 +618,20 @@ static int udev_input_add_device(udev_input_t *udev, } #endif - return true; + ret = 1; -error: - close(fd); - if (device) - free(device); +end: + /* Free resources in the event of + * an error */ + if (ret != 1) + { + if (fd >= 0) + close(fd); + if (device) + free(device); + } - return false; + return ret; } static void udev_input_remove_device(udev_input_t *udev, const char *devnode)