diff --git a/wayland/CMakeLists.txt b/wayland/CMakeLists.txt index 4b363b5..8ad94e4 100644 --- a/wayland/CMakeLists.txt +++ b/wayland/CMakeLists.txt @@ -47,4 +47,9 @@ add_dependencies(wayland generate-xdg-shell) pkg_check_modules(WAYLAND_CLIENT REQUIRED wayland-client) target_link_libraries(wayland ${WAYLAND_CLIENT_LIBRARIES}) -target_include_directories(wayland PRIVATE ${WAYLAND_CLIENT_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR}) \ No newline at end of file +target_include_directories(wayland PRIVATE ${WAYLAND_CLIENT_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR}) + +# xkbcommon for keyboard layout handling +pkg_check_modules(XKBCOMMON REQUIRED xkbcommon) +target_include_directories(wayland PRIVATE ${XKBCOMMON_INCLUDE_DIRS}) +target_link_libraries(wayland ${XKBCOMMON_LIBRARIES}) \ No newline at end of file diff --git a/wayland/c.c b/wayland/c.c index 3c2ffef..03c284c 100644 --- a/wayland/c.c +++ b/wayland/c.c @@ -4,10 +4,18 @@ #include #include #include +#include #include "xdg-shell-client-protocol.h" static struct wl_compositor *compositor; static struct xdg_wm_base *xdg_wm_base; +static struct wl_seat *seat; +static struct wl_keyboard *keyboard; + +/* xkbcommon helpers */ +static struct xkb_context *xkb_ctx = NULL; +static struct xkb_keymap *xkb_keymap = NULL; +static struct xkb_state *xkb_state = NULL; // Всё ниже - для одного окна static struct xdg_surface *xdg_surface; @@ -60,6 +68,153 @@ void resize_new(int16_t w, int16_t h) } } +/* ---------------- Keyboard handling ----------------- */ + +void keyboard_keymap(void *data, struct wl_keyboard *keyboard, uint32_t format, int fd, uint32_t size) +{ + printf("keyboard: keymap format=%u size=%u\n", format, size); + if (fd < 0) + return; + + /* Only support xkb v1 */ + if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) + { + /* nothing we can do */ + close(fd); + return; + } + + /* Map keymap to memory */ + char *map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + if (map == MAP_FAILED) + { + perror("mmap"); + close(fd); + return; + } + + if (!xkb_ctx) + xkb_ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + + /* Free previous keymap/state */ + if (xkb_state) + { + xkb_state_unref(xkb_state); + xkb_state = NULL; + } + if (xkb_keymap) + { + xkb_keymap_unref(xkb_keymap); + xkb_keymap = NULL; + } + + xkb_keymap = xkb_keymap_new_from_string(xkb_ctx, map, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); + if (!xkb_keymap) + { + fprintf(stderr, "Unable to compile xkb keymap\n"); + munmap(map, size); + close(fd); + return; + } + + xkb_state = xkb_state_new(xkb_keymap); + + munmap(map, size); + close(fd); +} + +void keyboard_enter(void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys) +{ + printf("keyboard: enter serial=%u surface=%p\n", serial, surface); +} + +void keyboard_leave(void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface) +{ + printf("keyboard: leave serial=%u surface=%p\n", serial, surface); +} + +void keyboard_key(void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) +{ + const char *s = (state == WL_KEYBOARD_KEY_STATE_PRESSED) ? "PRESSED" : "RELEASED"; + printf("keyboard: key serial=%u time=%u key=%u state=%s\n", serial, time, key, s); + + /* Convert keycode to keysym using xkb */ + if (xkb_state) + { + /* xkb uses keycodes offset by 8 */ + xkb_keycode_t kc = (xkb_keycode_t)key + 8; + xkb_keysym_t ks = xkb_state_key_get_one_sym(xkb_state, kc); + if (ks != XKB_KEY_NoSymbol) + { + char buf[64]; + int n = xkb_keysym_to_utf8(ks, buf, sizeof(buf)); + if (n > 0) + printf("keyboard: symbol '%s' (keysym 0x%x)\n", buf, ks); + else + printf("keyboard: keysym 0x%x (no UTF-8 representation)\n", ks); + } + } +} + +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) +{ + printf("keyboard: modifiers serial=%u depressed=%u latched=%u locked=%u group=%u\n", serial, mods_depressed, mods_latched, mods_locked, group); + if (xkb_state) + { + /* newer libxkbcommon uses three layout args; use group for depressed */ + xkb_state_update_mask(xkb_state, mods_depressed, mods_latched, mods_locked, group, 0, 0); + } +} + +void keyboard_repeat_info(void *data, struct wl_keyboard *keyboard, int32_t rate, int32_t delay) +{ + printf("keyboard: repeat rate=%d delay=%d\n", rate, delay); +} + +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 +}; + +void seat_capabilities(void *data, struct wl_seat *seat, uint32_t caps) +{ + if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !keyboard) + { + keyboard = wl_seat_get_keyboard(seat); + wl_keyboard_add_listener(keyboard, &keyboard_listener, 0); + printf("Seat reports keyboard capability - keyboard bound\n"); + } + else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && keyboard) + { + wl_keyboard_destroy(keyboard); + keyboard = NULL; + if (xkb_state) + { + xkb_state_unref(xkb_state); + xkb_state = NULL; + } + if (xkb_keymap) + { + xkb_keymap_unref(xkb_keymap); + xkb_keymap = NULL; + } + } +} + +void seat_name(void *data, struct wl_seat *seat, const char *name) +{ + printf("Seat name: %s\n", name); +} + +struct wl_seat_listener seat_listener = { + .capabilities = seat_capabilities, + .name = seat_name +}; + int8_t c; void draw() { @@ -132,6 +287,11 @@ void registry_global(void *data, struct wl_registry *reg, uint32_t name, const c xdg_wm_base = wl_registry_bind(reg, name, &xdg_wm_base_interface, version); xdg_wm_base_add_listener(xdg_wm_base, &wm_base_listener, 0); } + else if (!strcmp(intf, wl_seat_interface.name)) + { + seat = wl_registry_bind(reg, name, &wl_seat_interface, version); + wl_seat_add_listener(seat, &seat_listener, 0); + } } void registry_global_remove(void *data, struct wl_registry *reg, uint32_t name) {} @@ -189,6 +349,10 @@ int32_t run_wayland() xdg_toplevel_destroy(xdg_toplevel); xdg_surface_destroy(xdg_surface); wl_surface_destroy(wl_surface); + if (keyboard) + wl_keyboard_destroy(keyboard); + if (seat) + wl_seat_destroy(seat); wl_display_disconnect(display); return 0; } \ No newline at end of file