From 1593de38791b077d49f61b9ceacfd7053a2edd68 Mon Sep 17 00:00:00 2001 From: Seunghun Lee Date: Mon, 18 Apr 2022 10:29:23 +0900 Subject: [PATCH] Add ds_keyboard A ds_keyboard is for abstracting phisical keyboard device. Change-Id: I97b3b02d37abd3adefb9da0b899a54bb58919784 --- include/libds/input_device.h | 5 + include/libds/interfaces/input_device.h | 2 + include/libds/interfaces/keyboard.h | 89 ++++++++++ include/libds/keyboard.h | 45 +++++ packaging/libds.spec | 1 + src/libds/input_device.c | 15 ++ src/libds/keyboard.c | 304 ++++++++++++++++++++++++++++++++ src/libds/meson.build | 5 + src/libds/util.h | 3 + src/libds/util/shm.c | 76 +++++++- 10 files changed, 544 insertions(+), 1 deletion(-) create mode 100644 include/libds/interfaces/keyboard.h create mode 100644 include/libds/keyboard.h create mode 100644 src/libds/keyboard.c diff --git a/include/libds/input_device.h b/include/libds/input_device.h index 45e125b..6143e00 100644 --- a/include/libds/input_device.h +++ b/include/libds/input_device.h @@ -5,6 +5,8 @@ struct ds_input_device; struct ds_pointer; +struct ds_keyboard; + enum ds_button_state { DS_BUTTON_RELEASED, @@ -24,6 +26,9 @@ ds_input_device_get_type(struct ds_input_device *dev); struct ds_pointer * ds_input_device_get_pointer(struct ds_input_device *dev); +struct ds_keyboard * +ds_input_device_get_keyboard(struct ds_input_device *dev); + void ds_input_device_add_destroy_listener(struct ds_input_device *dev, struct wl_listener *listener); diff --git a/include/libds/interfaces/input_device.h b/include/libds/interfaces/input_device.h index f31594b..7828503 100644 --- a/include/libds/interfaces/input_device.h +++ b/include/libds/interfaces/input_device.h @@ -4,6 +4,7 @@ #include #include #include +#include struct ds_input_device_interface { @@ -22,6 +23,7 @@ struct ds_input_device union { void *_device; struct ds_pointer *pointer; + struct ds_keyboard *keyboard; }; struct { diff --git a/include/libds/interfaces/keyboard.h b/include/libds/interfaces/keyboard.h new file mode 100644 index 0000000..f68c9a7 --- /dev/null +++ b/include/libds/interfaces/keyboard.h @@ -0,0 +1,89 @@ +#ifndef LIBDS_INTERFACES_KEYBOARD_H +#define LIBDS_INTERFACES_KEYBOARD_H + +#include +#include + +#include + +#define DS_LED_COUNT 3 + +enum ds_keyboard_led { + DS_LED_NUM_LOCK = 1 << 0, + DS_LED_CAPS_LOCK = 1 << 1, + DS_LED_SCROLL_LOCK = 1 << 2, +}; + +#define DS_MODIFIER_COUNT 8 + +enum ds_keyboard_modifier { + DS_MODIFIER_SHIFT = 1 << 0, + DS_MODIFIER_CAPS = 1 << 1, + DS_MODIFIER_CTRL = 1 << 2, + DS_MODIFIER_ALT = 1 << 3, + DS_MODIFIER_MOD2 = 1 << 4, + DS_MODIFIER_MOD3 = 1 << 5, + DS_MODIFIER_LOGO = 1 << 6, + DS_MODIFIER_MOD5 = 1 << 7, +}; + +#define DS_KEYBOARD_KEYS_CAP 32 + +struct ds_keyboard; + +struct ds_keyboard_interface +{ + void (*destroy)(struct ds_keyboard *keyboard); +}; + +struct ds_keyboard_modifiers +{ + xkb_mod_mask_t depressed; + xkb_mod_mask_t latched; + xkb_mod_mask_t locked; + xkb_mod_mask_t group; +}; + +struct ds_keyboard +{ + const struct ds_keyboard_interface *iface; + + char *keymap_string; + size_t keymap_size; + int keymap_fd; + struct xkb_keymap *keymap; + struct xkb_state *xkb_state; + xkb_led_index_t led_indexes[DS_LED_COUNT]; + xkb_mod_index_t mod_indexes[DS_MODIFIER_COUNT]; + + uint32_t keycodes[DS_KEYBOARD_KEYS_CAP]; + size_t num_keycodes; + struct ds_keyboard_modifiers modifiers; + + struct { + int32_t rate; + int32_t delay; + } repeat_info; + + struct { + struct wl_signal destroy; + struct wl_signal key; + struct wl_signal modifiers; + struct wl_signal keymap; + struct wl_signal repeat_info; + } events; +}; + +void ds_keyboard_init(struct ds_keyboard *keyboard, + const struct ds_keyboard_interface *iface); + +void ds_keyboard_destroy(struct ds_keyboard *keyboard); + +void ds_keyboard_notify_key(struct ds_keyboard *keyboard, + struct ds_event_keyboard_key *event); + +void ds_keyboard_notify_modifiers(struct ds_keyboard *keyboard, + uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, + uint32_t group); + +#endif diff --git a/include/libds/keyboard.h b/include/libds/keyboard.h new file mode 100644 index 0000000..e226cb4 --- /dev/null +++ b/include/libds/keyboard.h @@ -0,0 +1,45 @@ +#ifndef LIBDS_KEYBOARD_H +#define LIBDS_KEYBOARD_H + +#include +#include + +#include +#include + +struct ds_keyboard; + +struct ds_event_keyboard_key +{ + uint32_t time_msec; + uint32_t keycode; + bool update_state; + enum wl_keyboard_key_state state; +}; + +bool ds_keyboard_set_keymap(struct ds_keyboard *keyboard, + struct xkb_keymap *keymap); + +void ds_keyboard_set_repeat_info(struct ds_keyboard *keyboard, + int32_t rate, int32_t delay); + +uint32_t ds_keyboard_get_modifiers(struct ds_keyboard *keyboard); + +struct xkb_state *ds_keyboard_get_xkb_state(struct ds_keyboard *keyboard); + +void ds_keyboard_add_destroy_listener(struct ds_keyboard *keyboard, + struct wl_listener *listener); + +void ds_keyboard_add_key_listener(struct ds_keyboard *keyboard, + struct wl_listener *listener); + +void ds_keyboard_add_modifiers_listener(struct ds_keyboard *keyboard, + struct wl_listener *listener); + +void ds_keyboard_add_keymap_listener(struct ds_keyboard *keyboard, + struct wl_listener *listener); + +void ds_keyboard_add_repeat_info_listener(struct ds_keyboard *keyboard, + struct wl_listener *listener); + +#endif diff --git a/packaging/libds.spec b/packaging/libds.spec index 879a87d..806c75b 100644 --- a/packaging/libds.spec +++ b/packaging/libds.spec @@ -13,6 +13,7 @@ BuildRequires: pkgconfig(wayland-client) BuildRequires: pkgconfig(wayland-protocols) BuildRequires: pkgconfig(pixman-1) BuildRequires: pkgconfig(libdrm) +BuildRequires: pkgconfig(xkbcommon) BuildRequires: pkgconfig(libtdm) BuildRequires: pkgconfig(libtbm) diff --git a/src/libds/input_device.c b/src/libds/input_device.c index db84100..351361f 100644 --- a/src/libds/input_device.c +++ b/src/libds/input_device.c @@ -6,6 +6,7 @@ #include "libds/log.h" #include "libds/interfaces/input_device.h" #include "libds/interfaces/pointer.h" +#include "libds/interfaces/keyboard.h" WL_EXPORT enum ds_input_device_type ds_input_device_get_type(struct ds_input_device *dev) @@ -24,6 +25,17 @@ ds_input_device_get_pointer(struct ds_input_device *dev) return dev->pointer; } +WL_EXPORT struct ds_keyboard * +ds_input_device_get_keyboard(struct ds_input_device *dev) +{ + if (dev->type != DS_INPUT_DEVICE_KEYBOARD) { + ds_err("Given ds_input_device is not a keyboard device"); + return NULL; + } + + return dev->keyboard; +} + WL_EXPORT void ds_input_device_add_destroy_listener(struct ds_input_device *dev, struct wl_listener *listener) @@ -54,6 +66,9 @@ ds_input_device_destroy(struct ds_input_device *dev) case DS_INPUT_DEVICE_POINTER: ds_pointer_destroy(dev->pointer); break; + case DS_INPUT_DEVICE_KEYBOARD: + ds_keyboard_destroy(dev->keyboard); + break; default: ds_err("Warning: leaking memory %p %p %d", dev->_device, dev, dev->type); diff --git a/src/libds/keyboard.c b/src/libds/keyboard.c new file mode 100644 index 0000000..d122e81 --- /dev/null +++ b/src/libds/keyboard.c @@ -0,0 +1,304 @@ +#include +#include +#include +#include + +#include + +#include "libds/log.h" +#include "libds/interfaces/keyboard.h" +#include "util.h" + +static bool keyboard_modifier_update(struct ds_keyboard *keyboard); +static void keyboard_key_update(struct ds_keyboard *keyboard, + struct ds_event_keyboard_key *event); +static void keyboard_led_update(struct ds_keyboard *keyboard); + +WL_EXPORT bool +ds_keyboard_set_keymap(struct ds_keyboard *keyboard, struct xkb_keymap *keymap) +{ + char *tmp_keymap_string; + void *dst; + int rw_fd, ro_fd; + xkb_keycode_t keycode; + const char *led_names[DS_LED_COUNT] = { + XKB_LED_NAME_NUM, + XKB_LED_NAME_CAPS, + XKB_LED_NAME_SCROLL, + }; + const char *mod_names[DS_MODIFIER_COUNT] = { + XKB_MOD_NAME_SHIFT, + XKB_MOD_NAME_CAPS, + XKB_MOD_NAME_CTRL, + XKB_MOD_NAME_ALT, + XKB_MOD_NAME_NUM, + "Mod3", + XKB_MOD_NAME_LOGO, + "Mod5", + }; + + if (keyboard->keymap) + xkb_keymap_unref(keyboard->keymap); + + keyboard->keymap = xkb_keymap_ref(keymap); + + if (keyboard->xkb_state) + xkb_state_unref(keyboard->xkb_state); + + keyboard->xkb_state = xkb_state_new(keyboard->keymap); + if (!keyboard->xkb_state) { + ds_err("Failed to create XKB state"); + goto err_state; + } + + for (size_t i = 0; i < DS_LED_COUNT; i++) { + keyboard->led_indexes[i] = + xkb_map_led_get_index(keyboard->keymap, led_names[i]); + } + + for (size_t i = 0; i < DS_MODIFIER_COUNT; i++) { + keyboard->mod_indexes[i] = + xkb_map_mod_get_index(keyboard->keymap, mod_names[i]); + } + + tmp_keymap_string = xkb_keymap_get_as_string(keyboard->keymap, + XKB_KEYMAP_FORMAT_TEXT_V1); + if (!tmp_keymap_string) { + ds_err("Failed to get string version of keymap"); + goto err_keymap_string; + } + + if (keyboard->keymap_string) + free(keyboard->keymap_string); + keyboard->keymap_string = tmp_keymap_string; + keyboard->keymap_size = strlen(keyboard->keymap_string) + 1; + + if (!allocate_shm_file_pair(keyboard->keymap_size, &rw_fd, &ro_fd)) { + ds_err("Failed to allocate shm_file for keymap"); + goto err_shm_file; + } + + dst = mmap(NULL, keyboard->keymap_size, PROT_READ | PROT_WRITE, + MAP_SHARED, rw_fd, 0); + if (dst == MAP_FAILED) { + ds_log_errno(DS_ERR, "mmap failed"); + goto err_mmap; + } + + memcpy(dst, keyboard->keymap_string, keyboard->keymap_size); + munmap(dst, keyboard->keymap_size); + close(rw_fd); + + if (keyboard->keymap_fd >= 0) + close(keyboard->keymap_fd); + keyboard->keymap_fd = ro_fd; + + for (size_t i = 0; i < keyboard->num_keycodes; i++) { + keycode = keyboard->keycodes[i] + 8; + xkb_state_update_key(keyboard->xkb_state, keycode, XKB_KEY_DOWN); + } + + keyboard_modifier_update(keyboard); + + wl_signal_emit(&keyboard->events.keymap, keyboard); + + return true; + +err_mmap: + close(rw_fd); + close(ro_fd); +err_shm_file: + free(keyboard->keymap_string); + keyboard->keymap_string = NULL; +err_keymap_string: + xkb_state_unref(keyboard->xkb_state); + keyboard->xkb_state = NULL; +err_state: + xkb_keymap_unref(keymap); + keyboard->keymap = NULL; + + return false; +} + +WL_EXPORT void +ds_keyboard_set_repeat_info(struct ds_keyboard *keyboard, + int32_t rate, int32_t delay) +{ + if (keyboard->repeat_info.rate == rate && + keyboard->repeat_info.delay == delay) + return; + keyboard->repeat_info.rate = rate; + keyboard->repeat_info.delay = delay; + + wl_signal_emit(&keyboard->events.repeat_info, keyboard); +} + +WL_EXPORT uint32_t +ds_keyboard_get_modifiers(struct ds_keyboard *keyboard) +{ + xkb_mod_mask_t mask; + uint32_t modifiers = 0; + + mask = keyboard->modifiers.depressed | keyboard->modifiers.latched; + for (size_t i = 0; i < DS_MODIFIER_COUNT; i++) { + if (keyboard->mod_indexes[i] != XKB_MOD_INVALID && + (mask & (1 << keyboard->mod_indexes[i]))) + modifiers |= (1 << i); + } + + return modifiers; +} + +WL_EXPORT struct xkb_state * +ds_keyboard_get_xkb_state(struct ds_keyboard *keyboard) +{ + return keyboard->xkb_state; +} + +WL_EXPORT void +ds_keyboard_notify_key(struct ds_keyboard *keyboard, + struct ds_event_keyboard_key *event) +{ + uint32_t keycode; + enum xkb_key_direction dir; + bool updated; + + keyboard_key_update(keyboard, event); + + wl_signal_emit(&keyboard->events.key, event); + + if (!keyboard->xkb_state) + return; + + if (event->update_state) { + keycode = event->keycode + 8; + dir = (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) ? + XKB_KEY_DOWN : XKB_KEY_UP; + xkb_state_update_key(keyboard->xkb_state, keycode, dir); + } + + updated = keyboard_modifier_update(keyboard); + if (updated) + wl_signal_emit(&keyboard->events.modifiers, keyboard); + + keyboard_led_update(keyboard); +} + +WL_EXPORT void +ds_keyboard_notify_modifiers(struct ds_keyboard *keyboard, + uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, + uint32_t group) +{ + bool updated; + + if (!keyboard->xkb_state) + return; + + xkb_state_update_mask(keyboard->xkb_state, mods_depressed, mods_latched, + mods_locked, 0, 0, group); + + updated = keyboard_modifier_update(keyboard); + if (updated) + wl_signal_emit(&keyboard->events.modifiers, keyboard); + + keyboard_led_update(keyboard); +} + +WL_EXPORT void +ds_keyboard_add_destroy_listener(struct ds_keyboard *keyboard, + struct wl_listener *listener) +{ + wl_signal_add(&keyboard->events.destroy, listener); +} + +WL_EXPORT void +ds_keyboard_add_key_listener(struct ds_keyboard *keyboard, + struct wl_listener *listener) +{ + wl_signal_add(&keyboard->events.key, listener); +} + +WL_EXPORT void +ds_keyboard_add_modifiers_listener(struct ds_keyboard *keyboard, + struct wl_listener *listener) +{ + wl_signal_add(&keyboard->events.modifiers, listener); +} + +WL_EXPORT void +ds_keyboard_add_keymap_listener(struct ds_keyboard *keyboard, + struct wl_listener *listener) +{ + wl_signal_add(&keyboard->events.keymap, listener); +} + +WL_EXPORT void +ds_keyboard_add_repeat_info_listener(struct ds_keyboard *keyboard, + struct wl_listener *listener) +{ + wl_signal_add(&keyboard->events.repeat_info, listener); +} + +void +ds_keyboard_init(struct ds_keyboard *keyboard, + const struct ds_keyboard_interface *iface) +{ + keyboard->iface = iface; + keyboard->keymap_fd = -1; + + wl_signal_init(&keyboard->events.destroy); + wl_signal_init(&keyboard->events.key); + wl_signal_init(&keyboard->events.modifiers); + wl_signal_init(&keyboard->events.keymap); + wl_signal_init(&keyboard->events.repeat_info); +} + +void +ds_keyboard_destroy(struct ds_keyboard *keyboard) +{ + if (keyboard->iface && keyboard->iface->destroy) + keyboard->iface->destroy(keyboard); + else + free(keyboard); +} + +static bool +keyboard_modifier_update(struct ds_keyboard *keyboard) +{ + xkb_mod_mask_t depressed, latched, locked, group; + + if (!keyboard->xkb_state) + return false; + + depressed = xkb_state_serialize_mods(keyboard->xkb_state, + XKB_STATE_MODS_DEPRESSED); + latched = xkb_state_serialize_mods(keyboard->xkb_state, + XKB_STATE_MODS_LATCHED); + locked = xkb_state_serialize_mods(keyboard->xkb_state, + XKB_STATE_MODS_LOCKED); + group = xkb_state_serialize_layout(keyboard->xkb_state, + XKB_STATE_LAYOUT_EFFECTIVE); + if (depressed == keyboard->modifiers.depressed && + latched == keyboard->modifiers.latched && + locked == keyboard->modifiers.locked && + group == keyboard->modifiers.group) + return false; + + keyboard->modifiers.depressed = depressed; + keyboard->modifiers.latched = latched; + keyboard->modifiers.locked = locked; + keyboard->modifiers.group = group; + + return true; +} + +static void keyboard_key_update(struct ds_keyboard *keyboard, + struct ds_event_keyboard_key *event) +{ + // TODO +} + +static void keyboard_led_update(struct ds_keyboard *keyboard) +{ + // TODO +} diff --git a/src/libds/meson.build b/src/libds/meson.build index a08dfdc..4d1ae39 100644 --- a/src/libds/meson.build +++ b/src/libds/meson.build @@ -20,6 +20,7 @@ libds_files = [ 'backend.c', 'input_device.c', 'pointer.c', + 'keyboard.c', ] protocols = { @@ -52,12 +53,16 @@ math = meson.get_compiler('c').find_library('m') wayland_server = dependency('wayland-server', required: true) pixman = dependency('pixman-1', required: true) libdrm = dependency('libdrm', required: true) +xkbcommon = dependency('xkbcommon', required: true) +rt = meson.get_compiler('c').find_library('rt') libds_deps = [ math, wayland_server, pixman, libdrm, + xkbcommon, + rt, ] subdir('backend') diff --git a/src/libds/util.h b/src/libds/util.h index 3b7448b..1c9326a 100644 --- a/src/libds/util.h +++ b/src/libds/util.h @@ -10,4 +10,7 @@ timespec_to_msec(const struct timespec *a); int allocate_shm_file(size_t size); +bool +allocate_shm_file_pair(size_t size, int *rw_fd_ptr, int *ro_fd_ptr); + #endif diff --git a/src/libds/util/shm.c b/src/libds/util/shm.c index c8c84e3..4abd229 100644 --- a/src/libds/util/shm.c +++ b/src/libds/util/shm.c @@ -24,13 +24,18 @@ */ #define _POSIX_C_SOURCE 200809L - +#include #include #include #include #include #include +#include #include +#include +#include + +#define RANDNAME_PATTERN "/libds-XXXXXX" int os_fd_set_cloexec(int fd) @@ -172,3 +177,72 @@ allocate_shm_file(off_t size) return fd; } + +static void +randname(char *buf) +{ + struct timespec ts; + long r; + + clock_gettime(CLOCK_REALTIME, &ts); + r = ts.tv_nsec; + for (int i = 0; i < 6; i++) { + buf[i] = 'A' + (r & 15) + (r & 16) * 2; + r >>= 5; + } +} + +static int +excl_shm_open(char *name) +{ + int retries = 100; + int fd; + + do { + randname(name + strlen(RANDNAME_PATTERN) - 6); + + --retries; + + fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600); + if (fd >= 0) + return fd; + } while (retries > 0 && errno == EEXIST); + + return -1; +} + +bool +allocate_shm_file_pair(size_t size, int *rw_fd_ptr, int *ro_fd_ptr) +{ + char name[] = RANDNAME_PATTERN; + int rw_fd, ro_fd; + int ret; + + rw_fd = excl_shm_open(name); + if (rw_fd < 0) + return false; + + ro_fd = shm_open(name, O_RDONLY, 0); + if (ro_fd < 0) { + shm_unlink(name); + close(rw_fd); + return false; + } + + shm_unlink(name); + + do { + ret = ftruncate(rw_fd, size); + } while (ret < 0 && errno == EINTR); + + if (ret < 0) { + close(rw_fd); + close(ro_fd); + return false; + } + + *rw_fd_ptr = rw_fd; + *ro_fd_ptr = ro_fd; + + return true; +} -- 2.7.4