// SPDX-License-Identifier: GPL-3.0-only /* * Wayland input handling. * * Copyright (c) 2024, Richard Acayan. All rights reserved. */ #include #include #include #include #include #include #include "input.h" #include "keymap.h" #include "ufkbd.h" #include "wayland.h" #include "wayland/input-method-unstable-v2.h" #include "wayland/virtual-keyboard-unstable-v1.h" static void ignore() {} static void on_pointer_move(void *data, struct wl_pointer *pointer __attribute__((unused)), uint32_t serial __attribute__((unused)), wl_fixed_t x, wl_fixed_t y) { struct ufkbd_wl_input *ctx = data; struct ufkbd_input_wayland *ufkbd_wl = ctx->ufkbd_wl; struct ufkbd_ctx *ufkbd = ufkbd_wl->ufkbd; ufkbd_input_position(ufkbd, UFKBD_PRESS_MAX - 1, x / 256, y / 256); } static void on_pointer_button(void *data, struct wl_pointer *pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { struct ufkbd_wl_input *ctx = data; struct ufkbd_input_wayland *ufkbd_wl = ctx->ufkbd_wl; struct ufkbd_ctx *ufkbd = ufkbd_wl->ufkbd; if (state == WL_POINTER_BUTTON_STATE_PRESSED) ufkbd_input_press(ufkbd, UFKBD_PRESS_MAX - 1); else if (state == WL_POINTER_BUTTON_STATE_RELEASED) ufkbd_input_release(ufkbd, UFKBD_PRESS_MAX - 1); } static const struct wl_pointer_listener pointer_listener = { .enter = ignore, .leave = ignore, .motion = on_pointer_move, .button = on_pointer_button, .axis = ignore, .frame = ignore, .axis_source = ignore, .axis_stop = ignore, .axis_discrete = ignore, .axis_value120 = ignore, .axis_relative_direction = ignore, }; void on_touch_down(void *data, struct wl_touch *wl_touch, uint32_t serial, uint32_t time, struct wl_surface *surface, int32_t id, wl_fixed_t x, wl_fixed_t y) { struct ufkbd_wl_input *ctx = data; struct ufkbd_input_wayland *ufkbd_wl = ctx->ufkbd_wl; struct ufkbd_ctx *ufkbd = ufkbd_wl->ufkbd; ufkbd_input_position(ufkbd, id, x / 256, y / 256); ufkbd_input_press(ufkbd, id); } void on_touch_up(void *data, struct wl_touch *wl_touch, uint32_t serial, uint32_t time, int32_t id) { struct ufkbd_wl_input *ctx = data; struct ufkbd_input_wayland *ufkbd_wl = ctx->ufkbd_wl; struct ufkbd_ctx *ufkbd = ufkbd_wl->ufkbd; ufkbd_input_release(ufkbd, id); } void on_touch_motion(void *data, struct wl_touch *wl_touch, uint32_t time, int32_t id, wl_fixed_t x, wl_fixed_t y) { struct ufkbd_wl_input *ctx = data; struct ufkbd_input_wayland *ufkbd_wl = ctx->ufkbd_wl; struct ufkbd_ctx *ufkbd = ufkbd_wl->ufkbd; ufkbd_input_position(ufkbd, id, x / 256, y / 256); } static const struct wl_touch_listener touch_listener = { .down = on_touch_down, .up = on_touch_up, .motion = on_touch_motion, .frame = ignore, .cancel = ignore, .shape = ignore, .orientation = ignore, }; static void on_seat_caps(void *data, struct wl_seat *seat, uint32_t capabilities) { struct ufkbd_wl_input *ctx = data; if (capabilities & WL_SEAT_CAPABILITY_TOUCH) { ctx->touch = wl_seat_get_touch(seat); if (ctx->touch == NULL) return; wl_touch_add_listener(ctx->touch, &touch_listener, ctx); } else { if (ctx->touch != NULL) wl_touch_destroy(ctx->touch); ctx->touch = NULL; } if (capabilities & WL_SEAT_CAPABILITY_POINTER) { ctx->pointer = wl_seat_get_pointer(seat); if (ctx->pointer == NULL) return; wl_pointer_add_listener(ctx->pointer, &pointer_listener, ctx); } else { if (ctx->pointer != NULL) wl_pointer_destroy(ctx->pointer); ctx->pointer = NULL; } } static void on_seat_name(void *data, struct wl_seat *seat, const char *name) { printf("wl: found seat %s\n", name); } struct wl_seat_listener seat_listener = { .name = on_seat_name, .capabilities = on_seat_caps, }; static int try_create_virtual_keyboard(struct ufkbd_wl_input *ctx) { struct ufkbd_keymap *keymap = ctx->ufkbd_wl->ufkbd->keymap; if (ctx->manager == NULL) return -EINVAL; if (ctx->seat == NULL) return -EINVAL; if (ctx->kb != NULL) return -EEXIST; ctx->kb = zwp_virtual_keyboard_manager_v1_create_virtual_keyboard(ctx->manager, ctx->seat); if (ctx->kb == NULL) return -ENOMEM; zwp_virtual_keyboard_v1_keymap(ctx->kb, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, keymap->fd, keymap->size); return 0; } static void on_global_vkman(void *data, struct wl_registry *reg, uint32_t name, uint32_t version) { struct ufkbd_wl_input *ctx = data; ctx->manager = wl_registry_bind(reg, name, &zwp_virtual_keyboard_manager_v1_interface, version); if (ctx->manager == NULL) return; try_create_virtual_keyboard(ctx); } static void on_global_seat(void *data, struct wl_registry *reg, uint32_t name, uint32_t version) { struct ufkbd_wl_input *ctx = data; ctx->seat = wl_registry_bind(reg, name, &wl_seat_interface, version); if (ctx->seat == NULL) return; wl_seat_add_listener(ctx->seat, &seat_listener, ctx); try_create_virtual_keyboard(ctx); } int ufkbd_wl_input_send_key(struct ufkbd_wl_input *ctx, int key, bool repeat) { if (ctx->kb == NULL) return -ENODEV; zwp_virtual_keyboard_v1_key(ctx->kb, 0, key, WL_KEYBOARD_KEY_STATE_PRESSED); if (!repeat) zwp_virtual_keyboard_v1_key(ctx->kb, 0, key, WL_KEYBOARD_KEY_STATE_RELEASED); return 0; } int ufkbd_wl_input_send_mod(struct ufkbd_wl_input *ctx, enum ufkbd_modifier mod, bool pressed) { int bit; if (ctx->kb == NULL) return -ENODEV; if (mod == UFKBD_MOD_SHIFT) bit = 0x1; else if (mod == UFKBD_MOD_CTRL) bit = 0x4; else if (mod == UFKBD_MOD_ALT) bit = 0x8; else bit = 0; if (pressed) ctx->mod_state |= bit; else ctx->mod_state &= ~bit; zwp_virtual_keyboard_v1_modifiers(ctx->kb, ctx->mod_state, 0, 0, 0); return 0; } int ufkbd_wl_input_init(struct ufkbd_input_wayland *ufkbd_wl, struct ufkbd_wl_input **out) { struct ufkbd_wl_input *ctx; int ret; ctx = calloc(1, sizeof(*ctx)); if (ctx == NULL) return -errno; ctx->ufkbd_wl = ufkbd_wl; ctx->listener_seat.iface = &wl_seat_interface; ctx->listener_seat.cb = on_global_seat; ctx->listener_seat.data = ctx; ret = ufkbd_wl_global_listener_add(ufkbd_wl, &ctx->listener_seat); if (ret) goto err; ctx->listener_manager.iface = &zwp_virtual_keyboard_manager_v1_interface; ctx->listener_manager.cb = on_global_vkman; ctx->listener_manager.data = ctx; ret = ufkbd_wl_global_listener_add(ufkbd_wl, &ctx->listener_manager); if (ret) goto err; *out = ctx; return 0; err: free(ctx); return ret; } void ufkbd_wl_input_uninit(struct ufkbd_wl_input *ctx) { if (ctx->touch != NULL) wl_touch_destroy(ctx->touch); if (ctx->pointer != NULL) wl_pointer_destroy(ctx->pointer); if (ctx->seat != NULL) wl_seat_destroy(ctx->seat); if (ctx->kb != NULL) zwp_virtual_keyboard_v1_destroy(ctx->kb); if (ctx->manager != NULL) zwp_virtual_keyboard_manager_v1_destroy(ctx->manager); free(ctx); }