From a3f8043afa0b8ad03f57853920739a0dbd4a5a23 Mon Sep 17 00:00:00 2001 From: Phireh Date: Sun, 24 Nov 2024 21:01:34 +0100 Subject: [PATCH] Hook up mouse callbacks --- Makefile | 6 +-- wayland.c | 132 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 128 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index fe97df9..00e4788 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,9 @@ INTERFACEDIR:=$(shell pkg-config --variable=pkgdatadir wayland-protocols) CFLAGS=-Wall -Wextra -Og -ggdb3 -Wno-unused-but-set-variable -Wno-unused-parameter -LIBS=-lwayland-client -lwayland-egl -lEGL -lOpenGL +LIBS=-lwayland-client -lwayland-egl -lEGL -lOpenGL -lxkbcommon wayland: wayland.c xdg-shell-protocol.c xdg-shell-client-protocol.h gcc $(CFLAGS) $(LIBS) wayland.c xdg-shell-protocol.c -o wayland -xdg-shell-protocol.c: +xdg-shell-protocol.c: wayland-scanner private-code $(INTERFACEDIR)/stable/xdg-shell/xdg-shell.xml xdg-shell-protocol.c xdg-shell-client-protocol.h: - wayland-scanner client-header $(INTERFACEDIR)/stable/xdg-shell/xdg-shell.xml xdg-shell-client-protocol.h + wayland-scanner client-header $(INTERFACEDIR)/stable/xdg-shell/xdg-shell.xml xdg-shell-client-protocol.h diff --git a/wayland.c b/wayland.c index 88505e8..5892de5 100644 --- a/wayland.c +++ b/wayland.c @@ -3,9 +3,12 @@ #include #include #include +#include #include #include #include +#include +#include // Generated header using wayland-scanner #include "xdg-shell-client-protocol.h" @@ -16,13 +19,19 @@ struct xdg_wm_base *wm_base = NULL; struct xdg_surface *xdg_surface = NULL; struct xdg_toplevel *xdg_toplevel = NULL; struct wl_egl_window *window = NULL; + /* Input API */ struct wl_seat *wl_seat = NULL; struct wl_keyboard *wl_keyboard = NULL; struct wl_pointer *wl_pointer = NULL; -EGLBoolean errcode = 0; + +/* XKB library */ +struct xkb_context *xkb_context = NULL; +struct xkb_state *xkb_state = NULL; +struct xkb_keymap *xkb_keymap = NULL; /* Misc. global state */ +EGLBoolean errcode = 0; bool running = true; char *egl_s(int code) @@ -60,14 +69,15 @@ void registry_handle_global( void *data, struct wl_registry *registry, fprintf(stdout, "Registering XDG WM base interface...\n"); wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1); } + /* NOTE: Since wl_seat v5, pointer events are grouped into "frames", which do not appear to be useful for non-touch applications and + also create more events which are more complicated to dispatch. */ if (!strcmp(interface, wl_seat_interface.name)) { fprintf(stdout, "Registering WL seat interface...\n"); - wl_seat = wl_registry_bind(registry, name, &wl_seat_interface, 1); + wl_seat = wl_registry_bind(registry, name, &wl_seat_interface, 9); } } - void registry_handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { @@ -103,10 +113,24 @@ void wayland_ping(void *data, struct xdg_wm_base *base, uint32_t serial) xdg_wm_base_pong(base, serial); } +void wl_seat_capabilities_callback(void *data, struct wl_seat *wl_seat, uint capabilities) +{ + fprintf(stdout, "Capabilities changed for WL seat\n"); +} + void keymap_callback(void *data, struct wl_keyboard *kb, uint format, int fd, uint size) { // TODO: Check for XKB compatibility and use xkbcommon to extract fprintf(stdout, "KBMAP of size %d detected\n", size); + + char *mem = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + fprintf(stdout, "XKBMAP desc:\n\t%s\n", mem); + xkb_keymap = xkb_keymap_new_from_string(xkb_context, mem, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); + munmap(mem, size); + close(fd); + + if (!(xkb_state = xkb_state_new(xkb_keymap))) + fprintf(stderr, "Could not initialize XKB state machine\n"); } void enter_callback(void *data, struct wl_keyboard* kb, uint serial, struct wl_surface* surface, struct wl_array *keys) @@ -121,12 +145,27 @@ void leave_callback(void *data, struct wl_keyboard* kb, uint serial, struct wl_s void key_callback(void *data, struct wl_keyboard *kb, uint serial, uint time, uint key, uint state) { - fprintf(stdout, "Pressed key %d\n", key); + /* TODO: This does *not* respect the system's language defaults. At least not in KDE. + * Wayland's own (terrible) documentation does not go into detail about languages: + * https://wayland-book.com/seat/xkb.html + */ + key += 8; + uint32_t layout = xkb_state_key_get_layout(xkb_state, key); + if (layout >= xkb_keymap_num_layouts_for_key(xkb_keymap, key)) + fprintf(stderr, "Layout %d exceeds number of available key layouts\n", layout); + + xkb_keysym_t sym = xkb_state_key_get_one_sym(xkb_state, key); + char keyname[128]; + xkb_keysym_get_name(sym, keyname, sizeof(keyname)); + char keyutf8[128]; + xkb_state_key_get_utf8(xkb_state, key, keyutf8, sizeof(keyutf8)); + fprintf(stdout, "%s key %d layout %d (KEYSYM %s) (UTF8 %s)\n", (state == WL_KEYBOARD_KEY_STATE_PRESSED ? "Pressed" : "Released"), key, layout, keyname, keyutf8); } void modifier_callback(void *data, struct wl_keyboard *kb, uint serial, uint mods_depressed, uint mods_latched, uint mods_locked, uint group) { fprintf(stdout, "Keyboard modifier keys changed\n"); + xkb_state_update_mask(xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group); } void repeat_info_callback(void *data, struct wl_keyboard *kb, int rate, int delay) @@ -134,6 +173,56 @@ void repeat_info_callback(void *data, struct wl_keyboard *kb, int rate, int dela fprintf(stdout, "Keyboard repeat/delay rate changed\n"); } +void pointer_enter_callback(void *data, struct wl_pointer *pointer, uint serial, struct wl_surface *surface, wl_fixed_t x, wl_fixed_t y) +{ + fprintf(stdout, "Pointer entered WL surface on coordinates %d %d\n", x, y); +} + +void pointer_leave_callback(void *data, struct wl_pointer *pointer, uint serial, struct wl_surface *surface) +{ + fprintf(stdout, "Pointer left WL surface\n"); +} + +void pointer_motion_callback(void *data, struct wl_pointer *pointer, uint time, wl_fixed_t x, wl_fixed_t y) +{ + fprintf(stdout, "Pointer moved to %d %d\n", x, y); +} + +void pointer_button_callback(void *data, struct wl_pointer *pointer, uint serial, uint time, uint button, uint state) +{ + fprintf(stdout, "Pointer button %d changed state\n", button); +} + +void pointer_axis_callback(void *data, struct wl_pointer *pointer, uint time, uint axis, wl_fixed_t value) +{ + fprintf(stdout, "Pointer scroll on axis %d value %d\n", axis, value); +} + +/* NOTE: *some* events are logically grouped together. The frame event marks the boundary between groups of them. More info: https://wayland.freedesktop.org/docs/html/apa.html#protocol-spec-wl_pointer */ + +void pointer_frame_callback(void *data, struct wl_pointer *frame) +{ + fprintf(stdout, "Pointer frame ended\n"); +} + +/* NOTE: This event is also used to group pointer events together. It specifies info about all events in its frame */ +void pointer_axis_source_callback(void *data, struct wl_pointer *pointer, uint axis_source) +{ + fprintf(stdout, "Pointer axis source: %d\n", axis_source); +} + +/* Optional event to implement kinetic scrolling */ +void pointer_axis_stop_callback(void *data, struct wl_pointer *pointer, uint time, uint axis) +{ + fprintf(stdout, "Pointer axis stopped: %d\n", axis); +} + +/* Optional event. Carries discrete scroll info after an 'axis' event */ +void pointer_axis_discrete_callback(void *data, struct wl_pointer *pointer, uint axis, int discrete) +{ + fprintf(stdout, "Pointer axis discrete step: %d\n", discrete); +} + int main() { fprintf(stdout, "Starting platform layer...\n"); @@ -165,13 +254,19 @@ int main() xdg_toplevel = xdg_surface_get_toplevel(xdg_surface); xdg_toplevel_set_title(xdg_toplevel, "Hi"); + /* XKB library initialization */ + xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + + const struct xdg_toplevel_listener xdg_toplevel_listener = { .configure = xdg_toplevel_configure, .close = xdg_toplevel_close }; + const struct xdg_wm_base_listener xdg_wm_base_listener = { .ping = wayland_ping }; + if (xdg_toplevel_add_listener(xdg_toplevel, &xdg_toplevel_listener, NULL)) fprintf(stderr, "Could not set XDG toplevel listener\n"); if (xdg_wm_base_add_listener(wm_base, &xdg_wm_base_listener, NULL)) @@ -181,6 +276,13 @@ int main() if (!(wl_pointer = wl_seat_get_pointer(wl_seat))) fprintf(stderr, "Could not get WL pointer\n"); + const struct wl_seat_listener wl_seat_listener = { + .capabilities = wl_seat_capabilities_callback + }; + + if (wl_seat_add_listener(wl_seat, &wl_seat_listener, NULL)) + fprintf(stderr, "Could not set WL seat listener\n"); + /* NOTE: We have to define all available callbacks for the keyboard listener even if we are not interested in them: https://gitlab.freedesktop.org/wayland/wayland/-/issues/160 */ const struct wl_keyboard_listener wl_keyboard_listener = { @@ -191,8 +293,24 @@ int main() .modifiers = modifier_callback, .repeat_info = repeat_info_callback, }; - wl_keyboard_add_listener(wl_keyboard, &wl_keyboard_listener, NULL); - + if (wl_keyboard_add_listener(wl_keyboard, &wl_keyboard_listener, NULL)) + fprintf(stderr, "Could not set XKB keyboard listener\n"); + + // TODO: Apparently we are not setting opcode 10's callback? + const struct wl_pointer_listener wl_pointer_listener = { + .enter = pointer_enter_callback, + .leave = pointer_leave_callback, + .motion = pointer_motion_callback, + .button = pointer_button_callback, + .axis = pointer_axis_callback, + .frame = pointer_frame_callback, + .axis_source = pointer_axis_source_callback, + .axis_stop = pointer_axis_stop_callback, + .axis_discrete = pointer_axis_discrete_callback + }; + + if (wl_pointer_add_listener(wl_pointer, &wl_pointer_listener, NULL)) + fprintf(stderr, "Could not set WL pointer listener\n"); /* WL EGL initialization */ window = wl_egl_window_create(surface, 480, 460); @@ -339,7 +457,7 @@ int main() // necessary before our first eglSwapBuffers wl_surface_commit(surface); - + // Main event loop int framecount = 0; while (running)