aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNathan Reiner <nathan@nathanreiner.xyz>2023-04-07 22:51:18 +0200
committerNathan Reiner <nathan@nathanreiner.xyz>2023-04-07 22:51:18 +0200
commit77d32cb5e492d68f871516c171d192d755e7c4c6 (patch)
tree3ecbc6124e71e386a508f855972a50a466dd8b7a
parent63288c4d50dea63a5a403b86762cf66c9db35325 (diff)
add keyboard support
-rw-r--r--swt.c240
-rw-r--r--wayland.h26
2 files changed, 239 insertions, 27 deletions
diff --git a/swt.c b/swt.c
index d204bb0..dbd06b8 100644
--- a/swt.c
+++ b/swt.c
@@ -1,5 +1,14 @@
+#include <bits/time.h>
+#include <sys/mman.h>
+#include <sys/timerfd.h>
+#include <unistd.h>
#include <string.h>
#include <stdio.h>
+#include <wayland-client-protocol.h>
+#include <xkbcommon/xkbcommon.h>
+#include <time.h>
+#include <poll.h>
+#include <errno.h>
#include "wayland.h"
#include "drw.h"
@@ -27,24 +36,32 @@ typedef struct {
} Window;
+static void setup();
static void buffer_release(void *data, struct wl_buffer *buffer);
static void draw_frame();
static void surface_configure(void *data, struct xdg_surface *surface, uint32_t serial);
-static void toplevel_configure(void *data, struct xdg_toplevel *toplevel, int32_t width, int32_t height, struct wl_array *states);
+static void toplevel_configure(void *data, struct xdg_toplevel *toplevel, int32_t width, int32_t height, struct wl_array *clients);
static void toplevel_close(void *data, struct xdg_toplevel *toplevel);
static void wm_base_ping(void *data, struct xdg_wm_base *wm_base, uint32_t serial);
static void registry_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version);
static void registry_global_remove(void *data, struct wl_registry *registry, uint32_t name);
+static void seat_capabilities(void *dat, struct wl_seat *seat, uint32_t capabilities);
+static void keyboard_keymap(void *data, struct wl_keyboard *keyboard, uint32_t format, int32_t fd, uint32_t size);
+static void keyboard_key(void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t client);
+static void keyboard_modifiers(void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group);
+static void keyboard_repeat_info(void *data, struct wl_keyboard *keyboard, int32_t rate, int32_t delay);
+static void handle_keyboard_event();
+static void dummy() {}
/* global variables */
static const struct wl_registry_listener registry_listener = {
.global = registry_global,
- .global_remove = registry_global_remove
+ .global_remove = registry_global_remove,
};
static const struct wl_buffer_listener buffer_listener = {
- .release = buffer_release
+ .release = buffer_release,
};
static const struct xdg_surface_listener surface_listener = {
@@ -53,14 +70,30 @@ static const struct xdg_surface_listener surface_listener = {
static const struct xdg_toplevel_listener toplevel_listener = {
.configure = toplevel_configure,
- .close = toplevel_close
+ .close = toplevel_close,
+ .configure_bounds = dummy,
+ .wm_capabilities = dummy
};
static const struct xdg_wm_base_listener wm_base_listener = {
- .ping = wm_base_ping
+ .ping = wm_base_ping,
};
-struct client_state state = { 0 };
+static const struct wl_keyboard_listener keyboard_listener = {
+ .keymap = keyboard_keymap,
+ .key = keyboard_key,
+ .modifiers = keyboard_modifiers,
+ .repeat_info = keyboard_repeat_info,
+ .enter = dummy,
+ .leave = dummy,
+};
+
+static const struct wl_seat_listener seat_listener = {
+ .capabilities = seat_capabilities,
+ .name = dummy
+};
+
+Client client = { 0 };
Window win = { 0 };
int running = 1;
@@ -91,7 +124,7 @@ surface_configure(void *data, struct xdg_surface *surface, uint32_t serial)
if (win.canvas != 0) {
free_drw(win.canvas);
}
- win.canvas = create_drw(state.shm, win.width, win.height);
+ win.canvas = create_drw(client.shm, win.width, win.height);
draw_frame();
wl_surface_attach(win.surface.wl, win.canvas->buffer, 0, 0);
@@ -101,7 +134,7 @@ surface_configure(void *data, struct xdg_surface *surface, uint32_t serial)
void
-toplevel_configure(void *data, struct xdg_toplevel *toplevel, int32_t width, int32_t height, struct wl_array *states)
+toplevel_configure(void *data, struct xdg_toplevel *toplevel, int32_t width, int32_t height, struct wl_array *clients)
{
if (width == 0 || height == 0) {
win.width = 800;
@@ -130,11 +163,11 @@ wm_base_ping(void *data, struct xdg_wm_base *wm_base, uint32_t serial)
void
registry_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version)
{
- match_then_bind(state.shm, wl_shm_interface, 1)
- or_match match_then_bind(state.compositor, wl_compositor_interface, 4)
- or_match match_then_bind(state.wm_base, xdg_wm_base_interface, 1)
- /* TODO: or_match match_then_bind(state.seat, wl_seat_interface, 7)
- wl_seat_add_listener(state.seat, &seat_listener, &state);*/
+ match_then_bind(client.shm, wl_shm_interface, 1)
+ or_match match_then_bind(client.compositor, wl_compositor_interface, 4)
+ or_match match_then_bind(client.wm_base, xdg_wm_base_interface, 1)
+ or_match match_then_bind(client.seat, wl_seat_interface, 7)
+ wl_seat_add_listener(client.seat, &seat_listener, &client);
end_match
}
@@ -142,32 +175,189 @@ registry_global(void *data, struct wl_registry *registry, uint32_t name, const c
void
registry_global_remove(void *data, struct wl_registry *registry, uint32_t name)
{
- /* TODO */
+ /* TODO: Check if this is necessary */
}
-int main()
+void
+seat_capabilities(void *data, struct wl_seat *seat, uint32_t capabilities)
+{
+ int has_keyboard = capabilities & WL_SEAT_CAPABILITY_KEYBOARD;
+
+ if (has_keyboard && client.kb.keyboard == 0) {
+ client.kb.keyboard = wl_seat_get_keyboard(client.seat);
+ wl_keyboard_add_listener(client.kb.keyboard, &keyboard_listener, &client);
+ } else {
+ wl_keyboard_release(client.kb.keyboard);
+ client.kb.keyboard = 0;
+ }
+}
+
+
+void
+keyboard_keymap(void *data, struct wl_keyboard *keyboard, uint32_t format, int32_t fd, uint32_t size)
+{
+ char *key_shm;
+ struct xkb_keymap *keymap;
+ struct xkb_state *state;
+
+ if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1)
+ return;
+
+ key_shm = mmap(0, size, PROT_READ, MAP_SHARED, fd, 0);
+
+ if (key_shm == MAP_FAILED)
+ return;
+
+ keymap = xkb_keymap_new_from_string(client.kb.context, key_shm,
+ XKB_KEYMAP_FORMAT_TEXT_V1,
+ XKB_KEYMAP_COMPILE_NO_FLAGS
+ );
+
+ munmap(key_shm, size);
+ close(fd);
+
+ state = xkb_state_new(keymap);
+ xkb_keymap_unref(client.kb.keymap);
+ xkb_state_unref(client.kb.state);
+
+ client.kb.state = state;
+ client.kb.keymap = keymap;
+}
+
+
+void
+keyboard_key(void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state)
+{
+ struct itimerspec spec = { 0 };
+ uint32_t keycode = key + 8;
+
+ client.kb.event.sym = xkb_state_key_get_one_sym(client.kb.state, keycode);
+ client.kb.event.state = state;
+
+ handle_keyboard_event();
+
+ if (client.kb.event.state == WL_KEYBOARD_KEY_STATE_PRESSED && client.kb.repeat.period >= 0) {
+ spec.it_value.tv_sec = client.kb.repeat.delay / 1000;
+ spec.it_value.tv_nsec = (client.kb.repeat.delay % 1000) * 1000000;
+ timerfd_settime(client.kb.repeat.timer, 0, &spec, 0);
+ } else if (client.kb.event.state == WL_KEYBOARD_KEY_STATE_RELEASED) {
+ timerfd_settime(client.kb.repeat.timer, 0, &spec, 0);
+ }
+}
+
+
+void
+keyboard_modifiers(void *data, struct wl_keyboard *keybaord, uint32_t serial, uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group)
+{
+ xkb_state_update_mask(client.kb.state, depressed, latched, locked, 0, 0, group);
+
+ client.kb.event.mods.alt = xkb_state_mod_name_is_active(
+ client.kb.state,
+ XKB_MOD_NAME_ALT,
+ XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED
+ );
+ client.kb.event.mods.ctrl = xkb_state_mod_name_is_active(
+ client.kb.state,
+ XKB_MOD_NAME_CTRL,
+ XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED
+ );
+ client.kb.event.mods.shift = xkb_state_mod_name_is_active(
+ client.kb.state,
+ XKB_MOD_NAME_SHIFT,
+ XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED
+ );
+}
+
+
+void
+keyboard_repeat_info(void *data, struct wl_keyboard *keyboard, int32_t rate, int32_t delay)
{
- state.display = wl_display_connect(0);
- state.registry = wl_display_get_registry(state.display);
- wl_registry_add_listener(state.registry, &registry_listener, &state);
- wl_display_roundtrip(state.display);
+ client.kb.repeat.delay = delay;
- xdg_wm_base_add_listener(state.wm_base, &wm_base_listener, &state);
- wl_display_roundtrip(state.display);
+ if (rate > 0)
+ client.kb.repeat.period = 1000 / rate;
+ else
+ client.kb.repeat.period = -1;
+}
+
+
+void
+handle_keyboard_event()
+{
+ char buf[8];
+
+ if (client.kb.event.state == WL_KEYBOARD_KEY_STATE_PRESSED) {
+ if (xkb_keysym_to_utf8(client.kb.event.sym, buf, sizeof(buf))) {
+ fprintf(stderr, "keypress: %s\n", buf);
+ }
+ }
+}
- win.surface.wl = wl_compositor_create_surface(state.compositor);
- win.surface.xdg = xdg_wm_base_get_xdg_surface(state.wm_base, win.surface.wl);
+
+void
+setup()
+{
+ client.kb.repeat.timer = timerfd_create(CLOCK_MONOTONIC, 0);
+ client.kb.context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
+ client.display = wl_display_connect(0);
+ client.registry = wl_display_get_registry(client.display);
+ wl_registry_add_listener(client.registry, &registry_listener, &client);
+ wl_display_roundtrip(client.display);
+
+ xdg_wm_base_add_listener(client.wm_base, &wm_base_listener, &client);
+ wl_display_roundtrip(client.display);
+
+ win.surface.wl = wl_compositor_create_surface(client.compositor);
+ win.surface.xdg = xdg_wm_base_get_xdg_surface(client.wm_base, win.surface.wl);
xdg_surface_add_listener(win.surface.xdg, &surface_listener, &win);
win.surface.toplevel = xdg_surface_get_toplevel(win.surface.xdg);
xdg_toplevel_add_listener(win.surface.toplevel, &toplevel_listener, &win);
xdg_toplevel_set_title(win.surface.toplevel, "swt");
wl_surface_commit(win.surface.wl);
+}
+
+
+int main()
+{
+ struct itimerspec spec = { 0 };
+ struct pollfd fds[2];
+
+ setup();
+
+ fds[0].fd = wl_display_get_fd(client.display);
+ fds[0].events = POLLIN;
+ fds[1].fd = client.kb.repeat.timer;
+ fds[1].events = POLLIN;
+
+ while (running) {
+ if (wl_display_flush(client.display) < 0) {
+ if (errno == EAGAIN)
+ continue;
+ break;
+ }
+
+ if (poll(fds, sizeof(fds) / sizeof(*fds), -1) < 0) {
+ if (errno == EAGAIN)
+ continue;
+ break;
+ }
+
+ if (fds[0].revents & POLLIN) {
+ if (wl_display_dispatch(client.display) < 0) {
+ running = 0;
+ }
+ }
- while (wl_display_dispatch(state.display) && running) {
+ if (fds[1].revents & POLLIN) {
+ handle_keyboard_event();
+ spec.it_value.tv_sec = client.kb.repeat.period / 1000;
+ spec.it_value.tv_nsec = (client.kb.repeat.period % 1000) / 1000000;
+ timerfd_settime(client.kb.repeat.timer, 0, &spec, 0);
+ }
}
- wl_display_disconnect(state.display);
+ wl_display_disconnect(client.display);
return 0;
}
diff --git a/wayland.h b/wayland.h
index 494c0e3..9e6a602 100644
--- a/wayland.h
+++ b/wayland.h
@@ -1,17 +1,39 @@
#ifndef WAYLAND_H
#define WAYLAND_H
+#include <xkbcommon/xkbcommon.h>
#include <wayland-client.h>
#include "xdg-shell-client-protocol.h"
-struct client_state {
+
+typedef struct {
struct wl_display *display;
struct wl_registry *registry;
struct wl_shm *shm;
struct wl_compositor *compositor;
struct xdg_wm_base *wm_base;
struct wl_seat *seat;
-};
+ struct {
+ struct xkb_state *state;
+ struct xkb_keymap *keymap;
+ struct xkb_context *context;
+ struct wl_keyboard *keyboard;
+ struct {
+ uint32_t state;
+ uint32_t sym;
+ struct {
+ uint32_t ctrl;
+ uint32_t alt;
+ uint32_t shift;
+ } mods;
+ } event;
+ struct {
+ int timer;
+ int delay;
+ int period;
+ } repeat;
+ } kb;
+} Client;
int allocate_shm_file(size_t size);