commit 8df7dfbf4eb0d951e346eca3b6f9515ce4853350
Author: Robert Russell <robertrussell.72001@gmail.com>
Date: Sat, 3 Sep 2022 16:52:59 -0700
Initial commit
Diffstat:
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);
+ }
+}