xkbdw

X keyboard device watcher
git clone git://git.rr3.xyz/xkbdw
Log | Files | Refs | README | LICENSE

commit 8df7dfbf4eb0d951e346eca3b6f9515ce4853350
Author: Robert Russell <robertrussell.72001@gmail.com>
Date:   Sat,  3 Sep 2022 16:52:59 -0700

Initial commit

Diffstat:
A.gitignore | 2++
AMakefile | 19+++++++++++++++++++
Aconfig.mk | 6++++++
Axkbdw.c | 102+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 129 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1 @@ +xkbdw +\ No newline at end of file diff --git a/Makefile b/Makefile @@ -0,0 +1,19 @@ +.POSIX: + +include config.mk + +xkbdw: xkbdw.c config.mk + $(CC) $(CFLAGS) -o $@ xkbdw.c $(LDFLAGS) + +install: xkbdw + mkdir -p $(DESTDIR)$(PREFIX)/bin + cp -f xkbdw $(DESTDIR)$(PREFIX)/bin/ + chmod 755 $(DESTDIR)$(PREFIX)/bin/xkbdw + +uninstall: + rm -f $(DESTDIR)$(PREFIX)/bin/xkbdw + +clean: + rm -f xkbdw + +.PHONY: install uninstall clean diff --git a/config.mk b/config.mk @@ -0,0 +1,6 @@ +PREFIX = /usr/local + +CFLAGS = -Wall +LDFLAGS = -lxcb-xkb -lxcb -lrcx + +CC = cc diff --git a/xkbdw.c b/xkbdw.c @@ -0,0 +1,102 @@ +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <rcx/all.h> + +#include <xcb/xcb.h> +#include <xcb/xkb.h> + + +/* Same as xcb_generic_event_t, but with a reasonable name for the XKB event + * subtype field. */ +typedef struct { + u8 response_type; + u8 xkbType; + u16 sequence; + u32 pad[7]; + u32 full_sequence; +} xcb_xkb_generic_event_t; + + +void +spawn(char **argv) { + switch (fork()) { + case 0: /* child */ + execvp(argv[0], argv); + abort(); + break; + case -1: /* error */ + r_fatalf("fork: %s", strerror(errno)); + break; + } +} + +xcb_connection_t * +xinit(void) { + xcb_connection_t *conn = xcb_connect(0, 0); + if (xcb_connection_has_error(conn)) + r_fatalf("XCB connection error"); + + /* Init XKB */ + u16 major = XCB_XKB_MAJOR_VERSION; + u16 minor = XCB_XKB_MINOR_VERSION; + xcb_xkb_use_extension_cookie_t cookie = + xcb_xkb_use_extension(conn, major, minor); + xcb_xkb_use_extension_reply_t *useext = + xcb_xkb_use_extension_reply(conn, cookie, 0); + if (!useext) + r_fatalf("XkbUseExtension"); + if (!useext->supported) + r_fatalf("X server does not support XKB version %d.%d", major, minor); + + return conn; +} + +int +main(int argc, char **argv) { + if (argc < 2) { + fprintf(stderr, "usage: %s PROG [ARGS...]\n", argv[0]); + exit(1); + } + + xcb_connection_t *conn = xinit(); + fcntl(xcb_get_file_descriptor(conn), F_SETFD, FD_CLOEXEC); + + xcb_void_cookie_t cookie = xcb_xkb_select_events_checked( + conn, + XCB_XKB_ID_USE_CORE_KBD, + XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY, /* events to affect */ + 0, /* details to clear */ + XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY, /* details to set entirely */ + 0, 0, 0 /* irrelevant */ + ); + if (xcb_request_check(conn, cookie)) + r_fatalf("XkbSelectEvents"); + + u8 xkbevtbase = xcb_get_extension_data(conn, &xcb_xkb_id)->first_event; + + for (;;) { + xcb_generic_event_t *evt = xcb_wait_for_event(conn); + if (!evt) + r_fatalf("IO error while waiting for event"); + + u8 code = evt->response_type & ~0x80; + if (code != xkbevtbase) + goto skip; + + xcb_xkb_generic_event_t *xkbevt = (void *)evt; + if (xkbevt->xkbType != XCB_XKB_NEW_KEYBOARD_NOTIFY) { + r_warnf("unexpected XKB event"); + goto skip; + } + + spawn(&argv[1]); + + skip: + free(evt); + } +}