From b2059dcb7920d3b9cec2bfe6fa9df181a3b6e18d Mon Sep 17 00:00:00 2001 From: Hans-Kristian Arntzen Date: Sat, 17 Dec 2016 20:21:45 +0100 Subject: [PATCH 1/4] Implement basic Wayland keyboard and mouse. --- gfx/drivers_context/wayland_ctx.c | 736 ++++++++++++++++---- input/drivers/udev_input.c | 4 +- input/drivers_keyboard/keyboard_event_xkb.c | 46 +- input/input_driver.h | 1 - 4 files changed, 617 insertions(+), 170 deletions(-) diff --git a/gfx/drivers_context/wayland_ctx.c b/gfx/drivers_context/wayland_ctx.c index a8c166e186..307dca0d6f 100644 --- a/gfx/drivers_context/wayland_ctx.c +++ b/gfx/drivers_context/wayland_ctx.c @@ -44,6 +44,10 @@ #include "../../configuration.h" #include "../../frontend/frontend_driver.h" #include "../../runloop.h" +#include "../../input/input_keyboard.h" +#include "../../input/input_keymaps.h" +#include "../../input/input_joypad_driver.h" +#include typedef struct gfx_ctx_wayland_data { @@ -65,11 +69,29 @@ typedef struct gfx_ctx_wayland_data struct wl_shell *shell; struct wl_keyboard *wl_keyboard; struct wl_pointer *wl_pointer; + struct wl_seat *seat; unsigned swap_interval; bool core_hw_context_enable; unsigned buffer_scale; + /* Wayland uses Linux keysyms. */ +#define UDEV_MAX_KEYS (KEY_MAX + 7) / 8 + uint8_t key_state[UDEV_MAX_KEYS]; + bool keyboard_focus; + + struct + { + int last_x, last_y; + int x, y; + int delta_x, delta_y; + bool last_valid; + bool focus; + bool left, right, middle; + } mouse; + const input_device_driver_t *joypad; + bool blocked; + #ifdef HAVE_VULKAN gfx_ctx_vulkan_data_t vk; #endif @@ -81,6 +103,260 @@ static enum gfx_ctx_api wl_api = GFX_CTX_NONE; #define EGL_OPENGL_ES3_BIT_KHR 0x0040 #endif +#ifdef HAVE_XKBCOMMON +/* FIXME: Move this into a header? */ +int init_xkb(int fd, size_t size); +int handle_xkb(int code, int value); +void free_xkb(void); +#endif + +static void keyboard_handle_keymap(void* data, + struct wl_keyboard* keyboard, + uint32_t format, + int fd, + uint32_t size) +{ + (void)data; + if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) + { + close(fd); + return; + } + +#ifdef HAVE_XKBCOMMON + if (init_xkb(fd, size) < 0) + RARCH_ERR("[Wayland]: Failed to init keymap.\n"); +#endif + close(fd); + + RARCH_LOG("[Wayland]: Loaded keymap.\n"); +} + +static void keyboard_handle_enter(void* data, + struct wl_keyboard* keyboard, + uint32_t serial, + struct wl_surface* surface, + struct wl_array* keys) +{ + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; + wl->keyboard_focus = true; +} + +static void keyboard_handle_leave(void *data, + struct wl_keyboard *keyboard, + uint32_t serial, + struct wl_surface *surface) +{ + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; + wl->keyboard_focus = false; +} + +static void keyboard_handle_key(void *data, + struct wl_keyboard *keyboard, + uint32_t serial, + uint32_t time, + uint32_t key, + uint32_t state) +{ + (void)serial; + (void)time; + (void)keyboard; + + int value = 1; + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; + if (state == WL_KEYBOARD_KEY_STATE_PRESSED) + { + BIT_SET(wl->key_state, key); + value = 1; + } + else if (state == WL_KEYBOARD_KEY_STATE_RELEASED) + { + BIT_CLEAR(wl->key_state, key); + value = 0; + } + +#ifdef HAVE_XKBCOMMON + if (handle_xkb(key, state) == 0) + return; +#endif + input_keyboard_event(value, + input_keymaps_translate_keysym_to_rk(key), + 0, 0, RETRO_DEVICE_KEYBOARD); +} + +static void keyboard_handle_modifiers(void *data, + struct wl_keyboard *keyboard, + uint32_t serial, + uint32_t modsDepressed, + uint32_t modsLatched, + uint32_t modsLocked, + uint32_t group) +{ + (void)data; + (void)keyboard; + (void)serial; + (void)modsDepressed; + (void)modsLatched; + (void)modsLocked; + (void)group; +} + +static void keyboard_handle_repeat_info(void *data, + struct wl_keyboard *wl_keyboard, + int32_t rate, + int32_t delay) +{ + (void)data; + (void)wl_keyboard; + (void)rate; + (void)delay; +} + +static const struct wl_keyboard_listener keyboard_listener = { + keyboard_handle_keymap, + keyboard_handle_enter, + keyboard_handle_leave, + keyboard_handle_key, + keyboard_handle_modifiers, + keyboard_handle_repeat_info, +}; + +static void pointer_handle_enter(void *data, + struct wl_pointer *pointer, + uint32_t serial, + struct wl_surface *surface, + wl_fixed_t sx, + wl_fixed_t sy) +{ + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; + (void)pointer; + (void)serial; + (void)surface; + + wl->mouse.last_x = wl_fixed_to_int(sx * (wl_fixed_t)wl->buffer_scale); + wl->mouse.last_y = wl_fixed_to_int(sy * (wl_fixed_t)wl->buffer_scale); + wl->mouse.x = wl->mouse.last_x; + wl->mouse.y = wl->mouse.last_y; + wl->mouse.focus = true; +} + +static void pointer_handle_leave(void *data, + struct wl_pointer *pointer, + uint32_t serial, + struct wl_surface *surface) +{ + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; + wl->mouse.focus = false; + (void)pointer; + (void)serial; + (void)surface; +} + +static void pointer_handle_motion(void *data, + struct wl_pointer *pointer, + uint32_t time, + wl_fixed_t sx, + wl_fixed_t sy) +{ + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; + wl->mouse.x = wl_fixed_to_int((wl_fixed_t)wl->buffer_scale * sx); + wl->mouse.y = wl_fixed_to_int((wl_fixed_t)wl->buffer_scale * sy); +} + +static void pointer_handle_button(void *data, + struct wl_pointer *wl_pointer, + uint32_t serial, + uint32_t time, + uint32_t button, + uint32_t state) +{ + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; + if (state == WL_POINTER_BUTTON_STATE_PRESSED) + { + if (button == BTN_LEFT) + { + wl->mouse.left = true; + + /* This behavior matches mpv, seems like a decent way to support window moving for now. */ + if (BIT_GET(wl->key_state, KEY_LEFTALT) && wl->shell_surf) + wl_shell_surface_move(wl->shell_surf, wl->seat, serial); + } + else if (button == BTN_RIGHT) + wl->mouse.right = true; + else if (button == BTN_MIDDLE) + wl->mouse.middle = true; + } + else + { + if (button == BTN_LEFT) + wl->mouse.left = false; + else if (button == BTN_RIGHT) + wl->mouse.right = false; + else if (button == BTN_MIDDLE) + wl->mouse.middle = false; + } +} + +static void pointer_handle_axis(void *data, + struct wl_pointer *wl_pointer, + uint32_t time, + uint32_t axis, + wl_fixed_t value) +{ + (void)data; + (void)wl_pointer; + (void)time; + (void)axis; + (void)value; +} + +static const struct wl_pointer_listener pointer_listener = { + pointer_handle_enter, + pointer_handle_leave, + pointer_handle_motion, + pointer_handle_button, + pointer_handle_axis, +}; + +static void seat_handle_capabilities(void *data, +struct wl_seat *seat, unsigned caps) +{ + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; + + if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !wl->wl_keyboard) + { + wl->wl_keyboard = wl_seat_get_keyboard(seat); + wl_keyboard_add_listener(wl->wl_keyboard, &keyboard_listener, wl); + } + else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && wl->wl_keyboard) + { + wl_keyboard_destroy(wl->wl_keyboard); + wl->wl_keyboard = NULL; + } + if ((caps & WL_SEAT_CAPABILITY_POINTER) && !wl->wl_pointer) + { + wl->wl_pointer = wl_seat_get_pointer(seat); + wl_pointer_add_listener(wl->wl_pointer, &pointer_listener, wl); + } + else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && wl->wl_pointer) + { + wl_pointer_destroy(wl->wl_pointer); + wl->wl_pointer = NULL; + } +} + +static void seat_handle_name(void *data, struct wl_seat *seat, const char *name) +{ + (void)data; + (void)seat; + RARCH_LOG("[Wayland]: Seat name: %s.\n", name); +} + +static const struct wl_seat_listener seat_listener = { + seat_handle_capabilities, + seat_handle_name, +}; + /* Shell surface callbacks. */ static void shell_surface_handle_ping(void *data, struct wl_shell_surface *shell_surface, @@ -211,6 +487,11 @@ static void registry_handle_global(void *data, struct wl_registry *reg, else if (string_is_equal(interface, "wl_shell")) wl->shell = (struct wl_shell*) wl_registry_bind(reg, id, &wl_shell_interface, 1); + else if (string_is_equal(interface, "wl_seat")) + { + wl->seat = wl_registry_bind(reg, id, &wl_seat_interface, 4); + wl_seat_add_listener(wl->seat, &seat_listener, wl); + } } static void registry_handle_global_remove(void *data, @@ -265,6 +546,17 @@ static void gfx_ctx_wl_destroy_resources(gfx_ctx_wayland_data_t *wl) break; } +#ifdef HAVE_XKBCOMMON + free_xkb(); +#endif + + if (wl->wl_keyboard) + wl_keyboard_destroy(wl->wl_keyboard); + if (wl->wl_pointer) + wl_pointer_destroy(wl->wl_pointer); + + if (wl->seat) + wl_seat_destroy(wl->seat); if (wl->shell) wl_shell_destroy(wl->shell); if (wl->compositor) @@ -602,6 +894,9 @@ static void *gfx_ctx_wl_init(void *video_driver) break; } + wl->keyboard_focus = true; + wl->mouse.focus = true; + return wl; error: @@ -830,23 +1125,304 @@ error: return false; } +static int16_t input_wl_mouse_state(gfx_ctx_wayland_data_t *wl, unsigned id) +{ + switch (id) + { + case RETRO_DEVICE_ID_MOUSE_X: + return wl->mouse.delta_x; + case RETRO_DEVICE_ID_MOUSE_Y: + return wl->mouse.delta_y; + case RETRO_DEVICE_ID_MOUSE_LEFT: + return wl->mouse.left; + case RETRO_DEVICE_ID_MOUSE_RIGHT: + return wl->mouse.right; + case RETRO_DEVICE_ID_MOUSE_MIDDLE: + return wl->mouse.middle; + + /* TODO: Rest of the mouse inputs. */ + } + + return 0; +} + +static int16_t input_wl_lightgun_state(gfx_ctx_wayland_data_t *wl, unsigned id) +{ + switch (id) + { + case RETRO_DEVICE_ID_LIGHTGUN_X: + return wl->mouse.delta_x; + case RETRO_DEVICE_ID_LIGHTGUN_Y: + return wl->mouse.delta_y; + case RETRO_DEVICE_ID_LIGHTGUN_TRIGGER: + return wl->mouse.left; + case RETRO_DEVICE_ID_LIGHTGUN_CURSOR: + return wl->mouse.middle; + case RETRO_DEVICE_ID_LIGHTGUN_TURBO: + return wl->mouse.right; + case RETRO_DEVICE_ID_LIGHTGUN_START: + return wl->mouse.middle && wl->mouse.right; + case RETRO_DEVICE_ID_LIGHTGUN_PAUSE: + return wl->mouse.middle && wl->mouse.left; + } + + return 0; +} + +static void input_wl_poll(void *data) +{ + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; + if (!wl) + return; + + flush_wayland_fd(wl); + + wl->mouse.delta_x = wl->mouse.x - wl->mouse.last_x; + wl->mouse.delta_y = wl->mouse.y - wl->mouse.last_y; + wl->mouse.last_x = wl->mouse.x; + wl->mouse.last_y = wl->mouse.y; + if (!wl->mouse.focus) + { + wl->mouse.delta_x = 0; + wl->mouse.delta_y = 0; + } + + if (wl->joypad) + wl->joypad->poll(); +} + +bool input_wl_is_pressed(gfx_ctx_wayland_data_t *wl, const struct retro_keybind *binds, unsigned id) +{ + if (id < RARCH_BIND_LIST_END) + { + const struct retro_keybind *bind = &binds[id]; + unsigned bit = input_keymaps_translate_rk_to_keysym(binds[id].key); + return BIT_GET(wl->key_state, bit); + } + return false; +} + +static int16_t input_wl_analog_pressed(gfx_ctx_wayland_data_t *wl, const struct retro_keybind *binds, + unsigned idx, unsigned id) +{ + unsigned id_minus = 0; + unsigned id_plus = 0; + int16_t pressed_minus = 0; + int16_t pressed_plus = 0; + + input_conv_analog_id_to_bind_id(idx, id, &id_minus, &id_plus); + + if (binds && binds[id_minus].valid && input_wl_is_pressed(wl, binds, id_minus)) + pressed_minus = -0x7fff; + if (binds && binds[id_plus].valid && input_wl_is_pressed(wl, binds, id_plus)) + pressed_plus = 0x7fff; + + return pressed_plus + pressed_minus; +} + +bool input_wl_state_kb(gfx_ctx_wayland_data_t *wl, const struct retro_keybind **binds, + unsigned port, unsigned device, unsigned idx, unsigned id) +{ + unsigned bit = input_keymaps_translate_rk_to_keysym((enum retro_key)id); + return id < RETROK_LAST && BIT_GET(wl->key_state, bit); +} + +static int16_t input_wl_pointer_state(gfx_ctx_wayland_data_t *wl, + unsigned idx, unsigned id, bool screen) +{ + bool inside = false; + struct video_viewport vp = {0}; + int16_t res_x = 0, res_y = 0, res_screen_x = 0, res_screen_y = 0; + + if (!(video_driver_translate_coord_viewport_wrap(&vp, wl->mouse.x, wl->mouse.y, + &res_x, &res_y, &res_screen_x, &res_screen_y))) + return 0; + + if (screen) + { + res_x = res_screen_x; + res_y = res_screen_y; + } + + inside = (res_x >= -0x7fff) && (res_y >= -0x7fff); + + if (!inside) + return 0; + + switch (id) + { + case RETRO_DEVICE_ID_POINTER_X: + return res_x; + case RETRO_DEVICE_ID_POINTER_Y: + return res_y; + case RETRO_DEVICE_ID_POINTER_PRESSED: + return wl->mouse.left; + } + + return 0; +} + + +static int16_t input_wl_state(void *data, const struct retro_keybind **binds, + unsigned port, unsigned device, unsigned idx, unsigned id) +{ + int16_t ret; + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; + + if (!wl) + return 0; + + switch (device) + { + case RETRO_DEVICE_JOYPAD: + if (binds[port] && binds[port][id].valid) + return input_wl_is_pressed(wl, binds[port], id) || + input_joypad_pressed(wl->joypad, port, binds[port], id); + break; + case RETRO_DEVICE_ANALOG: + ret = input_wl_analog_pressed(wl, binds[port], idx, id); + if (!ret && binds[port]) + ret = input_joypad_analog(wl->joypad, port, idx, id, binds[port]); + return ret; + + case RETRO_DEVICE_KEYBOARD: + return input_wl_state_kb(wl, binds, port, device, idx, id); + case RETRO_DEVICE_MOUSE: + return input_wl_mouse_state(wl, id); + + case RETRO_DEVICE_POINTER: + case RARCH_DEVICE_POINTER_SCREEN: + if (idx == 0) + return input_wl_pointer_state(wl, idx, id, + device == RARCH_DEVICE_POINTER_SCREEN); + break; + case RETRO_DEVICE_LIGHTGUN: + return input_wl_lightgun_state(wl, id); + } + + return 0; +} + +static void input_wl_free(void *data) +{ + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; + if (!wl) + return; + + if (wl->joypad) + wl->joypad->destroy(); +} + +static bool input_wl_init(void *data) +{ + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; + settings_t *settings = config_get_ptr(); + wl->joypad = input_joypad_init_driver(settings->input.joypad_driver, wl); + if (!wl->joypad) + return false; + input_keymaps_init_keyboard_lut(rarch_key_map_linux); + return true; +} + +static uint64_t input_wl_get_capabilities(void *data) +{ + (void)data; + + return + (1 << RETRO_DEVICE_JOYPAD) | + (1 << RETRO_DEVICE_ANALOG) | + (1 << RETRO_DEVICE_KEYBOARD) | + (1 << RETRO_DEVICE_MOUSE) | + (1 << RETRO_DEVICE_LIGHTGUN); +} + +static void input_wl_grab_mouse(void *data, bool state) +{ + /* Dummy for now. Might be useful in the future. */ + (void)data; + (void)state; +} + +static bool input_wl_set_rumble(void *data, unsigned port, enum retro_rumble_effect effect, uint16_t strength) +{ + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; + if (wl && wl->joypad) + return input_joypad_set_rumble(wl->joypad, port, effect, strength); + return false; +} + +static const input_device_driver_t *input_wl_get_joypad_driver(void *data) +{ + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; + if (!wl) + return NULL; + return wl->joypad; +} + +static bool input_wl_keyboard_mapping_is_blocked(void *data) +{ + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; + if (!wl) + return false; + return wl->blocked; +} + +static void input_wl_keyboard_mapping_set_block(void *data, bool value) +{ + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; + if (!wl) + return; + wl->blocked = value; +} + +static bool input_wl_meta_key_pressed(void *data, int key) +{ + (void)data; + (void)key; + /* FIXME: What is this supposed to do? */ + return false; +} + +static const input_driver_t input_wayland = { + NULL, + input_wl_poll, + input_wl_state, + input_wl_meta_key_pressed, + input_wl_free, + NULL, + NULL, + input_wl_get_capabilities, + "wayland", + input_wl_grab_mouse, + NULL, + input_wl_set_rumble, + input_wl_get_joypad_driver, + NULL, + input_wl_keyboard_mapping_is_blocked, + input_wl_keyboard_mapping_set_block, +}; + static void gfx_ctx_wl_input_driver(void *data, const input_driver_t **input, void **input_data) { - (void)data; -#if 0 - void *wl = input_wayland.init(); - *input = wl ? &input_wayland : NULL; - *input_data = wl; -#endif - *input = NULL; - *input_data = NULL; + /* Input is heavily tied to the window stuff on Wayland, so just implement the input driver here. */ + if (!input_wl_init(data)) + { + *input = NULL; + *input_data = NULL; + } + else + { + *input = &input_wayland; + *input_data = data; + } } static bool gfx_ctx_wl_has_focus(void *data) { (void)data; - return true; + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; + return wl->keyboard_focus; } static bool gfx_ctx_wl_suppress_screensaver(void *data, bool enable) @@ -916,148 +1492,6 @@ static bool gfx_ctx_wl_bind_api(void *video_driver, return false; } -static void keyboard_handle_keymap(void* data, -struct wl_keyboard* keyboard, -uint32_t format, -int fd, -uint32_t size) -{ - /* TODO */ -} - -static void keyboard_handle_enter(void* data, -struct wl_keyboard* keyboard, -uint32_t serial, -struct wl_surface* surface, -struct wl_array* keys) -{ - /* TODO */ -} - -static void keyboard_handle_leave(void* data, -struct wl_keyboard* keyboard, -uint32_t serial, -struct wl_surface* surface) -{ - /* TODO */ -} - -static void keyboard_handle_key(void* data, -struct wl_keyboard* keyboard, -uint32_t serial, -uint32_t time, -uint32_t key, -uint32_t state) -{ - /* TODO */ -} - -static void keyboard_handle_modifiers(void* data, -struct wl_keyboard* keyboard, -uint32_t serial, -uint32_t modsDepressed, -uint32_t modsLatched, -uint32_t modsLocked, -uint32_t group) -{ - /* TODO */ -} - -static const struct wl_keyboard_listener keyboard_listener = { - keyboard_handle_keymap, - keyboard_handle_enter, - keyboard_handle_leave, - keyboard_handle_key, - keyboard_handle_modifiers, -}; - -static void pointer_handle_enter(void* data, -struct wl_pointer* pointer, -uint32_t serial, -struct wl_surface* surface, -wl_fixed_t sx, -wl_fixed_t sy) -{ - /* TODO */ -} - -static void pointer_handle_leave(void* data, -struct wl_pointer* pointer, -uint32_t serial, -struct wl_surface* surface) -{ - /* TODO */ -} - -static void pointer_handle_motion(void* data, -struct wl_pointer* pointer, -uint32_t time, -wl_fixed_t sx, -wl_fixed_t sy) -{ - /* TODO */ -} - -static void pointer_handle_button(void* data, -struct wl_pointer* wl_pointer, -uint32_t serial, -uint32_t time, -uint32_t button, -uint32_t state) -{ - /* TODO */ -} - -static void pointer_handle_axis(void* data, -struct wl_pointer* wl_pointer, -uint32_t time, -uint32_t axis, -wl_fixed_t value) -{ - /* TODO */ -} - - -static const struct wl_pointer_listener pointer_listener = { - pointer_handle_enter, - pointer_handle_leave, - pointer_handle_motion, - pointer_handle_button, - pointer_handle_axis, -}; - -static void seat_handle_capabilities(void *data, -struct wl_seat *seat, unsigned caps) -{ - gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; - - if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !wl->wl_keyboard) - { - wl->wl_keyboard = wl_seat_get_keyboard(seat); - wl_keyboard_add_listener(wl->wl_keyboard, &keyboard_listener, NULL); - } - else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && wl->wl_keyboard) - { - wl_keyboard_destroy(wl->wl_keyboard); - wl->wl_keyboard = NULL; - } - if ((caps & WL_SEAT_CAPABILITY_POINTER) && !wl->wl_pointer) - { - wl->wl_pointer = wl_seat_get_pointer(seat); - wl_pointer_add_listener(wl->wl_pointer, &pointer_listener, NULL); - } - else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && wl->wl_pointer) - { - wl_pointer_destroy(wl->wl_pointer); - wl->wl_pointer = NULL; - } -} - -/* Seat callbacks - TODO/FIXME */ -static const struct wl_seat_listener seat_listener = { - seat_handle_capabilities, -}; - #ifdef HAVE_VULKAN static void *gfx_ctx_wl_get_context_data(void *data) { diff --git a/input/drivers/udev_input.c b/input/drivers/udev_input.c index de9ecb88c2..b48b00d01a 100644 --- a/input/drivers/udev_input.c +++ b/input/drivers/udev_input.c @@ -100,7 +100,7 @@ struct udev_input }; #ifdef HAVE_XKBCOMMON -int init_xkb(void); +int init_xkb(int fd, size_t size); #endif static void udev_handle_touchpad(void *data, @@ -629,7 +629,7 @@ static void *udev_input_init(void) } #ifdef HAVE_XKBCOMMON - if (init_xkb() == -1) + if (init_xkb(-1, 0) == -1) goto error; #endif diff --git a/input/drivers_keyboard/keyboard_event_xkb.c b/input/drivers_keyboard/keyboard_event_xkb.c index d01369adb3..3cbe45490e 100644 --- a/input/drivers_keyboard/keyboard_event_xkb.c +++ b/input/drivers_keyboard/keyboard_event_xkb.c @@ -24,6 +24,7 @@ #include "../input_keymaps.h" #include "../input_keyboard.h" #include "../../configuration.h" +#include #define MOD_MAP_SIZE 5 @@ -53,8 +54,9 @@ void free_xkb(void) xkb_state = NULL; } -int init_xkb(void) +int init_xkb(int fd, size_t size) { + char *map_str; settings_t *settings = config_get_ptr(); mod_map_idx = (xkb_mod_index_t *)calloc(MOD_MAP_SIZE, sizeof(xkb_mod_index_t)); @@ -69,23 +71,35 @@ int init_xkb(void) xkb_ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS); if (xkb_ctx) { - struct string_list *list = NULL; - struct xkb_rule_names rule = {0}; - - rule.rules = "evdev"; - - if (*settings->input.keyboard_layout) + if (fd >= 0) { - list = string_split(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; - } + map_str = (char*)mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + if (map_str == MAP_FAILED) + goto error; - xkb_map = xkb_keymap_new_from_names(xkb_ctx, &rule, XKB_MAP_COMPILE_NO_FLAGS); - if (list) - string_list_free(list); + xkb_map = xkb_keymap_new_from_string(xkb_ctx, map_str, XKB_KEYMAP_FORMAT_TEXT_V1, 0); + munmap(map_str, size); + } + else + { + struct string_list *list = NULL; + struct xkb_rule_names rule = {0}; + + rule.rules = "evdev"; + + if (*settings->input.keyboard_layout) + { + list = string_split(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; + } + + xkb_map = xkb_keymap_new_from_names(xkb_ctx, &rule, XKB_MAP_COMPILE_NO_FLAGS); + if (list) + string_list_free(list); + } } if (xkb_map) { diff --git a/input/input_driver.h b/input/input_driver.h index 5c4602cff2..f7dbe8596e 100644 --- a/input/input_driver.h +++ b/input/input_driver.h @@ -314,7 +314,6 @@ extern input_driver_t input_android; extern input_driver_t input_sdl; extern input_driver_t input_dinput; extern input_driver_t input_x; -extern input_driver_t input_wayland; extern input_driver_t input_ps3; extern input_driver_t input_psp; extern input_driver_t input_ctr; From 313ac63c335f77c80e7d5e2f6a76352d98451d1c Mon Sep 17 00:00:00 2001 From: Hans-Kristian Arntzen Date: Sat, 17 Dec 2016 20:42:48 +0100 Subject: [PATCH 2/4] Make use of xkb_state_update_mask. Fixes mod keys in Wayland it seems. --- gfx/drivers_context/wayland_ctx.c | 7 ++++++- input/drivers_keyboard/keyboard_event_xkb.c | 7 +++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/gfx/drivers_context/wayland_ctx.c b/gfx/drivers_context/wayland_ctx.c index 307dca0d6f..046e8fd187 100644 --- a/gfx/drivers_context/wayland_ctx.c +++ b/gfx/drivers_context/wayland_ctx.c @@ -107,6 +107,7 @@ static enum gfx_ctx_api wl_api = GFX_CTX_NONE; /* FIXME: Move this into a header? */ int init_xkb(int fd, size_t size); int handle_xkb(int code, int value); +void handle_xkb_state_mask(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group); void free_xkb(void); #endif @@ -176,7 +177,7 @@ static void keyboard_handle_key(void *data, } #ifdef HAVE_XKBCOMMON - if (handle_xkb(key, state) == 0) + if (handle_xkb(key, value) == 0) return; #endif input_keyboard_event(value, @@ -195,10 +196,14 @@ static void keyboard_handle_modifiers(void *data, (void)data; (void)keyboard; (void)serial; +#ifdef HAVE_XKBCOMMON + handle_xkb_state_mask(modsDepressed, modsLatched, modsLocked, group); +#else (void)modsDepressed; (void)modsLatched; (void)modsLocked; (void)group; +#endif } static void keyboard_handle_repeat_info(void *data, diff --git a/input/drivers_keyboard/keyboard_event_xkb.c b/input/drivers_keyboard/keyboard_event_xkb.c index 3cbe45490e..a422e3d9e3 100644 --- a/input/drivers_keyboard/keyboard_event_xkb.c +++ b/input/drivers_keyboard/keyboard_event_xkb.c @@ -136,6 +136,13 @@ error: return -1; } +void handle_xkb_state_mask(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group) +{ + if (!xkb_state) + return; + xkb_state_update_mask(xkb_state, depressed, latched, locked, 0, 0, group); +} + /* FIXME: Don't handle composed and dead-keys properly. * Waiting for support in libxkbcommon ... */ int handle_xkb(int code, int value) From c48bb6284e7092e14c3756ac3f780fa9a4b0935b Mon Sep 17 00:00:00 2001 From: Hans-Kristian Arntzen Date: Sat, 17 Dec 2016 21:34:51 +0100 Subject: [PATCH 3/4] Wayland: Cursor toggling. --- Makefile.common | 4 +- gfx/drivers_context/wayland_ctx.c | 68 ++++++++++++++++++++++++++++++- qb/config.libs.sh | 3 +- 3 files changed, 70 insertions(+), 5 deletions(-) diff --git a/Makefile.common b/Makefile.common index bbcc702598..8245497b45 100644 --- a/Makefile.common +++ b/Makefile.common @@ -580,8 +580,8 @@ endif ifeq ($(HAVE_WAYLAND), 1) OBJ += gfx/drivers_context/wayland_ctx.o - DEFINES += $(WAYLAND_CFLAGS) - LIBS += $(WAYLAND_LIBS) + DEFINES += $(WAYLAND_CFLAGS) $(WAYLAND_CURSOR_CFLAGS) + LIBS += $(WAYLAND_LIBS) $(WAYLAND_CURSOR_LIBS) endif #Input diff --git a/gfx/drivers_context/wayland_ctx.c b/gfx/drivers_context/wayland_ctx.c index 046e8fd187..d5e231e23a 100644 --- a/gfx/drivers_context/wayland_ctx.c +++ b/gfx/drivers_context/wayland_ctx.c @@ -18,6 +18,7 @@ #include #include +#include #include @@ -70,6 +71,7 @@ typedef struct gfx_ctx_wayland_data struct wl_keyboard *wl_keyboard; struct wl_pointer *wl_pointer; struct wl_seat *seat; + struct wl_shm *shm; unsigned swap_interval; bool core_hw_context_enable; @@ -89,6 +91,16 @@ typedef struct gfx_ctx_wayland_data bool focus; bool left, right, middle; } mouse; + + struct + { + struct wl_cursor *default_cursor; + struct wl_cursor_theme *theme; + struct wl_surface *surface; + uint32_t serial; + bool visible; + } cursor; + const input_device_driver_t *joypad; bool blocked; @@ -215,6 +227,7 @@ static void keyboard_handle_repeat_info(void *data, (void)wl_keyboard; (void)rate; (void)delay; + /* TODO: Seems like we'll need this to get repeat working. We'll have to do it on our own. */ } static const struct wl_keyboard_listener keyboard_listener = { @@ -226,6 +239,8 @@ static const struct wl_keyboard_listener keyboard_listener = { keyboard_handle_repeat_info, }; +static void gfx_ctx_wl_show_mouse(void *data, bool state); + static void pointer_handle_enter(void *data, struct wl_pointer *pointer, uint32_t serial, @@ -243,6 +258,9 @@ static void pointer_handle_enter(void *data, wl->mouse.x = wl->mouse.last_x; wl->mouse.y = wl->mouse.last_y; wl->mouse.focus = true; + wl->cursor.serial = serial; + + gfx_ctx_wl_show_mouse(data, wl->cursor.visible); } static void pointer_handle_leave(void *data, @@ -492,6 +510,8 @@ static void registry_handle_global(void *data, struct wl_registry *reg, else if (string_is_equal(interface, "wl_shell")) wl->shell = (struct wl_shell*) wl_registry_bind(reg, id, &wl_shell_interface, 1); + else if (string_is_equal(interface, "wl_shm")) + wl->shm = wl_registry_bind(reg, id, &wl_shm_interface, 1); else if (string_is_equal(interface, "wl_seat")) { wl->seat = wl_registry_bind(reg, id, &wl_seat_interface, 4); @@ -560,6 +580,11 @@ static void gfx_ctx_wl_destroy_resources(gfx_ctx_wayland_data_t *wl) if (wl->wl_pointer) wl_pointer_destroy(wl->wl_pointer); + if (wl->cursor.theme) + wl_cursor_theme_destroy(wl->cursor.theme); + if (wl->cursor.surface) + wl_surface_destroy(wl->cursor.surface); + if (wl->seat) wl_seat_destroy(wl->seat); if (wl->shell) @@ -862,6 +887,12 @@ static void *gfx_ctx_wl_init(void *video_driver) goto error; } + if (!wl->shm) + { + RARCH_ERR("Failed to create shm.\n"); + goto error; + } + if (!wl->shell) { RARCH_ERR("Failed to create shell.\n"); @@ -902,6 +933,11 @@ static void *gfx_ctx_wl_init(void *video_driver) wl->keyboard_focus = true; wl->mouse.focus = true; + wl->cursor.surface = wl_compositor_create_surface(wl->compositor); + wl->cursor.theme = wl_cursor_theme_load(NULL, 16, wl->shm); + wl->cursor.default_cursor = wl_cursor_theme_get_cursor(wl->cursor.theme, "left_ptr"); + flush_wayland_fd(wl); + return wl; error: @@ -1123,6 +1159,14 @@ static bool gfx_ctx_wl_set_video_mode(void *data, break; } + if (fullscreen) + { + wl->cursor.visible = false; + gfx_ctx_wl_show_mouse(wl, false); + } + else + wl->cursor.visible = true; + return true; error: @@ -1592,6 +1636,26 @@ static void gfx_ctx_wl_set_flags(void *data, uint32_t flags) wl->core_hw_context_enable = true; } +static void gfx_ctx_wl_show_mouse(void *data, bool state) +{ + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; + if (!wl->wl_pointer) + return; + + if (state) + { + struct wl_cursor_image *image = wl->cursor.default_cursor->images[0]; + wl_pointer_set_cursor(wl->wl_pointer, wl->cursor.serial, wl->cursor.surface, image->hotspot_x, image->hotspot_y); + wl_surface_attach(wl->cursor.surface, wl_cursor_image_get_buffer(image), 0, 0); + wl_surface_damage(wl->cursor.surface, 0, 0, image->width, image->height); + wl_surface_commit(wl->cursor.surface); + } + else + wl_pointer_set_cursor(wl->wl_pointer, wl->cursor.serial, NULL, 0, 0); + + wl->cursor.visible = state; +} + const gfx_ctx_driver_t gfx_ctx_wayland = { gfx_ctx_wl_init, gfx_ctx_wl_destroy, @@ -1615,7 +1679,7 @@ const gfx_ctx_driver_t gfx_ctx_wayland = { gfx_ctx_wl_get_proc_address, NULL, NULL, - NULL, + gfx_ctx_wl_show_mouse, "wayland", gfx_ctx_wl_get_flags, gfx_ctx_wl_set_flags, @@ -1625,5 +1689,5 @@ const gfx_ctx_driver_t gfx_ctx_wayland = { #else NULL, #endif - NULL + NULL, }; diff --git a/qb/config.libs.sh b/qb/config.libs.sh index 850a2b36e9..c151f6b1e1 100644 --- a/qb/config.libs.sh +++ b/qb/config.libs.sh @@ -395,6 +395,7 @@ check_pkgconf XCB xcb [ "$HAVE_X11" = "no" ] && HAVE_XEXT=no && HAVE_XF86VM=no && HAVE_XINERAMA=no && HAVE_XSHM=no check_pkgconf WAYLAND wayland-egl +check_pkgconf WAYLAND_CURSOR wayland-cursor check_pkgconf XKBCOMMON xkbcommon 0.3.2 check_pkgconf DBUS dbus-1 @@ -483,6 +484,6 @@ fi # Creates config.mk and config.h. add_define_make GLOBAL_CONFIG_DIR "$GLOBAL_CONFIG_DIR" -VARS=$(eval set | grep ^HAVE_ | sed s/=.*// | sed s/^HAVE_//) +VARS="$(eval set | grep ^HAVE_ | sed s/=.*// | sed s/^HAVE_//) WAYLAND_CURSOR" create_config_make config.mk $VARS create_config_header config.h $VARS From eecb60549f7dc56dd6780403153efbb973af663a Mon Sep 17 00:00:00 2001 From: Hans-Kristian Arntzen Date: Sat, 17 Dec 2016 21:48:04 +0100 Subject: [PATCH 4/4] No need to ugly hack in qb/conf.libs.sh. --- qb/config.libs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qb/config.libs.sh b/qb/config.libs.sh index c151f6b1e1..b09ab7c8b1 100644 --- a/qb/config.libs.sh +++ b/qb/config.libs.sh @@ -484,6 +484,6 @@ fi # Creates config.mk and config.h. add_define_make GLOBAL_CONFIG_DIR "$GLOBAL_CONFIG_DIR" -VARS="$(eval set | grep ^HAVE_ | sed s/=.*// | sed s/^HAVE_//) WAYLAND_CURSOR" +VARS=$(eval set | grep ^HAVE_ | sed s/=.*// | sed s/^HAVE_//) create_config_make config.mk $VARS create_config_header config.h $VARS