diff options
Diffstat (limited to 'src/wayland/keyboard.c')
| -rw-r--r-- | src/wayland/keyboard.c | 452 |
1 files changed, 0 insertions, 452 deletions
diff --git a/src/wayland/keyboard.c b/src/wayland/keyboard.c deleted file mode 100644 index f1170c6..0000000 --- a/src/wayland/keyboard.c +++ /dev/null @@ -1,452 +0,0 @@ -#include "internal.h" -#include "common/keyboard.h" - -#define XKB_EVDEV_OFFSET 8 -#define KEYMAP_MAX_BYTES (8u * 1024 * 1024) - -void keyboard_text_reset(struct seat *s) -{ - if (s->compose) xkb_compose_state_reset(s->compose); -} - -void keyboard_repeat_stop(struct seat *s) -{ - if (!s->repeat_held) return; - s->repeat_held = false; - if (s->repeat_fd >= 0) { - struct itimerspec off = {0}; - timerfd_settime(s->repeat_fd, 0, &off, NULL); - } -} - -void keyboard_repeat_sync(struct seat *s) -{ - if (s->repeat_fd < 0) return; - struct itimerspec timer = {0}; - if (s->repeat_held && s->repeat_rate > 0) { - uint64_t interval = 1000000000ull / (uint32_t)s->repeat_rate; - if (!interval) interval = 1; - uint64_t delay_ns = (uint64_t)s->repeat_delay * 1000000ull; - if (!delay_ns) delay_ns = 1; - timer.it_value = (struct timespec){ (time_t)(delay_ns / 1000000000ull), (long)(delay_ns % 1000000000ull) }; - timer.it_interval = (struct timespec){ (time_t)(interval / 1000000000ull), (long)(interval % 1000000000ull) }; - } - timerfd_settime(s->repeat_fd, 0, &timer, NULL); -} - -static const char *const input_mod_names[5] = { - XKB_MOD_NAME_SHIFT, XKB_MOD_NAME_ALT, - XKB_MOD_NAME_CTRL, XKB_MOD_NAME_LOGO, - XKB_MOD_NAME_CAPS -}; - -static bool mod_active(const struct seat *s, const char *name) -{ - return xkb_state_mod_name_is_active(s->xkb_state, name, XKB_STATE_MODS_EFFECTIVE) > 0; -} - -static void resolve_input_mod_masks(struct seat *s) -{ - memset(s->input_mod_masks, 0, sizeof(s->input_mod_masks)); - if (!s->xkb_keymap) return; - struct xkb_state *probe = xkb_state_new(s->xkb_keymap); - if (!probe) return; - for (unsigned real = 0; real < 8; real++) { - xkb_state_update_mask(probe, 1u << real, 0, 0, 0, 0, 0); - for (unsigned i = 0; i < 5; i++) { - if (xkb_state_mod_name_is_active(probe, input_mod_names[i], - XKB_STATE_MODS_EFFECTIVE) > 0) - s->input_mod_masks[i] |= 1u << real; - } - } - xkb_state_unref(probe); - s->input_mods_ready = true; -} - -static void keyboard_keymap(void *data, struct wl_keyboard *keyboard, - uint32_t format, int32_t fd, uint32_t size) -{ - (void)keyboard; - struct seat *s = data; - keyboard_repeat_stop(s); - keyboard_text_reset(s); - s->input_mods_ready = false; - if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { - close(fd); - return; - } - struct stat st; - if (size == 0 || size > KEYMAP_MAX_BYTES || - fstat(fd, &st) < 0 || st.st_size < 0 || (uint64_t)st.st_size < size) { - close(fd); - return; - } - char *map_str = malloc(size); - if (!map_str) { - close(fd); - return; - } - size_t off = 0; - while (off < size) { - ssize_t n = pread(fd, map_str + off, size - off, (off_t)off); - if (n > 0) off += (size_t)n; - else if (n == 0 || errno != EINTR) break; - } - close(fd); - if (off < size) { - free(map_str); - return; - } - - if (s->xkb_keymap) - xkb_keymap_unref(s->xkb_keymap); - if (s->xkb_state) - xkb_state_unref(s->xkb_state); - s->xkb_state = NULL; - - s->xkb_keymap = xkb_keymap_new_from_buffer(s->conn->xkb_ctx, map_str, size, - XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); - free(map_str); - - if (s->xkb_keymap) { - s->xkb_state = xkb_state_new(s->xkb_keymap); - s->mod_shift = xkb_keymap_mod_get_index(s->xkb_keymap, XKB_MOD_NAME_SHIFT); - s->mod_alt = xkb_keymap_mod_get_index(s->xkb_keymap, XKB_MOD_NAME_ALT); - s->mod_ctrl = xkb_keymap_mod_get_index(s->xkb_keymap, XKB_MOD_NAME_CTRL); - s->mod_super = xkb_keymap_mod_get_index(s->xkb_keymap, XKB_MOD_NAME_LOGO); - resolve_input_mod_masks(s); - } -} - -static void keyboard_reset_keys(struct seat *s, struct wayland_surface *w) -{ - (void)w; - keyboard_repeat_stop(s); - keyboard_text_reset(s); -} - -bool conn_keyboard_on(const struct wayland_display *c, const struct wayland_surface *w, - const struct seat *except) -{ - for (const struct seat *s = c->seats; s; s = s->next) - if (s != except && s->keyboard_focus == w) - return true; - return false; -} - -static void push_focus(struct wayland_surface *w, bool focused) -{ - struct yasl_input_event ev = { - .kind = YASL_INPUT_FOCUS, - .surface = &w->base, - .focus = { .focused = focused }, - }; - input_queue_push(&w->input, &ev); -} - -static void keyboard_enter(void *data, struct wl_keyboard *keyboard, - uint32_t serial, struct wl_surface *surface, struct wl_array *keys) -{ - (void)keyboard; (void)serial; (void)keys; - struct seat *s = data; - struct wayland_surface *w = surface_root(conn_surface_for(s->conn, surface)); - bool same_window = w && w == s->focus_leaving; - if (same_window) - s->focus_leaving = NULL; - if (!w) { - s->keyboard_focus = NULL; - return; - } - bool focused = conn_keyboard_on(s->conn, w, s); - s->keyboard_focus = w; - keyboard_reset_keys(s, w); - if (!same_window && !focused) - push_focus(w, true); -} - -static void seat_settle_focus(struct seat *s) -{ - struct wayland_surface *w = s->focus_leaving; - if (!w) - return; - s->focus_leaving = NULL; - if (!conn_keyboard_on(s->conn, w, NULL)) - push_focus(w, false); -} - -void keyboard_settle_focus(struct wayland_display *c) -{ - for (struct seat *s = c->seats; s; s = s->next) - if (s->focus_leaving) - seat_settle_focus(s); -} - -static void keyboard_leave(void *data, struct wl_keyboard *keyboard, - uint32_t serial, struct wl_surface *surface) -{ - (void)keyboard; (void)serial; (void)surface; - struct seat *s = data; - struct wayland_surface *w = s->keyboard_focus; - s->keyboard_focus = NULL; - if (!w) return; - keyboard_reset_keys(s, w); - seat_settle_focus(s); - s->focus_leaving = w; -} - -static unsigned input_modifiers(struct seat *s) -{ - if (!s->input_mods_ready) - resolve_input_mod_masks(s); - - unsigned mods = 0; - xkb_mod_mask_t reported = 0; - for (unsigned i = 0; i < 5; i++) { - xkb_mod_mask_t mask = s->input_mod_masks[i]; - if (mask && (mask & ~reported) && mod_active(s, input_mod_names[i])) { - mods |= 1u << i; - reported |= mask; - } - } - return mods; -} - -static uint32_t key_codepoint_at_level(struct seat *s, uint32_t key, xkb_layout_index_t layout, xkb_level_index_t level) -{ - const xkb_keysym_t *syms; - int n = xkb_keymap_key_get_syms_by_level(s->xkb_keymap, key + XKB_EVDEV_OFFSET, layout, level, &syms); - return n == 1 ? xkb_keysym_to_utf32(syms[0]) : 0; -} - -static struct yasl_key_event xkb_key_event(struct seat *s, uint32_t key, - enum yasl_key_action action) -{ - uint32_t code = key + XKB_EVDEV_OFFSET; - xkb_layout_index_t layout = xkb_state_key_get_layout(s->xkb_state, code); - - struct yasl_key_event k = { - .key = yasl_key_from_evdev(key), - .action = action, - .mods = input_modifiers(s), - .codepoint = yasl_key_label(key_codepoint_at_level(s, key, layout, 0)), - .shifted = yasl_key_label(key_codepoint_at_level(s, key, layout, 1)), - .native_code = key, - }; - return k; -} - -static void keyboard_text(struct seat *s, struct wayland_surface *w, uint32_t key) -{ - uint32_t code = key + XKB_EVDEV_OFFSET; - unsigned mods = input_modifiers(s); - if ((mods & (YASL_MOD_CTRL | YASL_MOD_SUPER)) || - ((mods & YASL_MOD_ALT) && - !xkb_state_mod_index_is_consumed(s->xkb_state, code, s->mod_alt))) { - keyboard_text_reset(s); - return; - } - if (!s->compose_attempted) { - s->compose_attempted = true; - const char *locale = getenv("LC_ALL"); - if (!locale || !*locale) locale = getenv("LC_CTYPE"); - if (!locale || !*locale) locale = getenv("LANG"); - if (!locale || !*locale) locale = "C.UTF-8"; - struct xkb_compose_table *table = xkb_compose_table_new_from_locale( - s->conn->xkb_ctx, locale, XKB_COMPOSE_COMPILE_NO_FLAGS); - if (table) { - s->compose = xkb_compose_state_new(table, XKB_COMPOSE_STATE_NO_FLAGS); - xkb_compose_table_unref(table); - } - } - bool composed = false; - if (s->compose) { - xkb_compose_state_feed(s->compose, xkb_state_key_get_one_sym(s->xkb_state, code)); - switch (xkb_compose_state_get_status(s->compose)) { - case XKB_COMPOSE_COMPOSING: return; - case XKB_COMPOSE_CANCELLED: keyboard_text_reset(s); return; - case XKB_COMPOSE_COMPOSED: composed = true; break; - default: break; - } - } - int len = composed ? xkb_compose_state_get_utf8(s->compose, NULL, 0) : - xkb_state_key_get_utf8(s->xkb_state, code, NULL, 0); - if (len > 0) { - char local[128]; - char *text = (size_t)len < sizeof(local) ? local : malloc((size_t)len + 1); - if (text) { - if (composed) xkb_compose_state_get_utf8(s->compose, text, (size_t)len + 1); - else xkb_state_key_get_utf8(s->xkb_state, code, text, (size_t)len + 1); - /* Editing commands belong to key events, not text insertion. */ - if ((unsigned char)text[0] >= 0x20 && text[0] != 0x7f) { - struct yasl_input_event ev = {.kind = YASL_INPUT_TEXT, - .surface = &w->base, .text = {text, (size_t)len}}; - input_queue_push(&w->input, &ev); - } - if (text != local) free(text); - } else { - struct yasl_input_event ev = {.kind = YASL_INPUT_RESET, .surface = &w->base}; - input_queue_push(&w->input, &ev); - } - } - if (composed) keyboard_text_reset(s); -} - -static bool keyboard_dispatch_key(struct seat *s, struct wayland_surface *w, uint32_t key, - enum yasl_key_action action) -{ - if (!s->xkb_state || !w->cbs) return false; - - struct yasl_key_event k = xkb_key_event(s, key, action); - struct yasl_input_event ev = { - .kind = YASL_INPUT_KEY, - .surface = &w->base, - .key = k, - }; - if (!input_queue_push(&w->input, &ev)) { - keyboard_text_reset(s); - return false; - } - if (action != YASL_KEY_RELEASE) keyboard_text(s, w, key); - return action != YASL_KEY_RELEASE; -} - -static void keyboard_key(void *data, struct wl_keyboard *keyboard, - uint32_t serial, uint32_t time, uint32_t key, uint32_t state) -{ - (void)keyboard; (void)time; - struct seat *s = data; - struct wayland_surface *w = s->keyboard_focus; - if (!w) return; - seat_note_input(s, serial); - if (state == WL_KEYBOARD_KEY_STATE_RELEASED) { - if (s->repeat_held && key == s->repeat_key) keyboard_repeat_stop(s); - keyboard_dispatch_key(s, w, key, YASL_KEY_RELEASE); - return; - } - if (state != WL_KEYBOARD_KEY_STATE_PRESSED || !s->xkb_keymap) return; - bool repeatable = xkb_keymap_key_repeats(s->xkb_keymap, key + XKB_EVDEV_OFFSET); - if (repeatable) keyboard_repeat_stop(s); - if (keyboard_dispatch_key(s, w, key, YASL_KEY_PRESS) && repeatable) { - s->repeat_key = key; - s->repeat_held = true; - s->repeat_started_ns = yasl_loop_now_ns(); - keyboard_repeat_sync(s); - } -} - -static int keyboard_repeat_ready(int fd, short revents, void *data) -{ - struct seat *s = data; - uint64_t count; - if (!(revents & POLLIN) || read(fd, &count, sizeof(count)) != sizeof(count)) return 0; - - struct wayland_surface *w = s->keyboard_focus; - if (!w) { - keyboard_repeat_stop(s); - return 0; - } - if (count > 32) count = 32; - while (count-- && s->repeat_held && s->repeat_rate > 0) { - if (!keyboard_dispatch_key(s, w, s->repeat_key, YASL_KEY_REPEAT)) - keyboard_repeat_stop(s); - } - return 0; -} - -static void keyboard_modifiers(void *data, struct wl_keyboard *keyboard, - uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, - uint32_t mods_locked, uint32_t group) -{ - (void)keyboard; (void)serial; - struct seat *s = data; - if (s->xkb_state) { - xkb_state_update_mask(s->xkb_state, mods_depressed, mods_latched, - mods_locked, 0, 0, group); - } -} - -static void keyboard_repeat_info(void *data, struct wl_keyboard *keyboard, - int32_t rate, int32_t delay) -{ - (void)keyboard; - struct seat *s = data; - s->repeat_rate = rate > 0 ? rate : 0; - s->repeat_delay = delay > 0 ? delay : 0; - keyboard_repeat_sync(s); -} - -const struct wl_keyboard_listener keyboard_listener = { - .keymap = keyboard_keymap, - .enter = keyboard_enter, - .leave = keyboard_leave, - .key = keyboard_key, - .modifiers = keyboard_modifiers, - .repeat_info = keyboard_repeat_info, -}; - -int keyboard_repeat_init(struct seat *s) -{ - if (s->repeat_fd >= 0) return 0; - s->repeat_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); - if (s->repeat_fd < 0) return -1; - s->repeat_watch = yasl_loop_add_fd(s->conn->loop, s->repeat_fd, POLLIN, - keyboard_repeat_ready, s); - if (s->repeat_watch <= 0) { - s->repeat_watch = 0; - close(s->repeat_fd); - s->repeat_fd = -1; - return -1; - } - keyboard_repeat_sync(s); - return 0; -} - -static bool mod_index_active(const struct seat *s, xkb_mod_index_t mod) -{ - return mod != XKB_MOD_INVALID && - xkb_state_mod_index_is_active(s->xkb_state, mod, - XKB_STATE_MODS_EFFECTIVE) > 0; -} - -int seat_modifiers(const struct seat *s) -{ - if (unlikely(!s || !s->xkb_state)) return 0; - return (mod_index_active(s, s->mod_shift) * YASL_MOD_SHIFT) | - (mod_index_active(s, s->mod_alt) * YASL_MOD_ALT) | - (mod_index_active(s, s->mod_ctrl) * YASL_MOD_CTRL) | - (mod_index_active(s, s->mod_super) * YASL_MOD_SUPER) | - (mod_active(s, XKB_MOD_NAME_CAPS) * YASL_MOD_CAPS); -} - -int wayland_display_modifiers(const struct wayland_display *c) -{ - return c ? seat_modifiers(conn_active_seat(c)) : 0; -} - -void wayland_display_copy(struct wayland_display *c, enum yasl_selection sel, const char *text) -{ - struct seat *s = c ? conn_active_seat(c) : NULL; - if (!s || !s->clipboard) - return; - if (sel == YASL_PRIMARY) - clipboard_copy_primary(s->clipboard, text, s->input_serial); - else - clipboard_copy(s->clipboard, text, s->input_serial); -} - -void wayland_display_paste(struct wayland_display *c, enum yasl_selection sel, struct wayland_surface *target) -{ - struct seat *s = c ? conn_active_seat(c) : NULL; - if (!s || !s->clipboard) - return; - if (!target || target->closed) return; - if (sel == YASL_PRIMARY) - clipboard_paste_primary(s->clipboard, target->surface); - else - clipboard_paste(s->clipboard, target->surface); -} - -bool wayland_surface_input_poll(struct wayland_surface *w, struct yasl_input_event *ev) -{ - return w && ev && input_queue_pop(&w->input, ev); -} |
