xkbdw

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

xkbdw.c (2556B)


      1 #include <errno.h>
      2 #include <fcntl.h>
      3 #include <signal.h>
      4 #include <stdio.h>
      5 #include <stdlib.h>
      6 #include <string.h>
      7 #include <sys/wait.h>
      8 #include <unistd.h>
      9 
     10 #include <rcx/all.h>
     11 
     12 #include <xcb/xcb.h>
     13 #include <xcb/xkb.h>
     14 
     15 
     16 /* Same as xcb_generic_event_t, but with a reasonable name for the XKB event
     17  * subtype field. */
     18 typedef struct {
     19     u8 response_type;
     20     u8 xkbType;
     21     u16 sequence;
     22     u32 pad[7];
     23     u32 full_sequence;
     24 } xcb_xkb_generic_event_t;
     25 
     26 
     27 void
     28 sigchld(int sig) {
     29 	while (waitpid(-1, 0, WNOHANG) > 0);
     30 }
     31 
     32 void
     33 spawn(char **argv) {
     34 	switch (fork()) {
     35 	case 0: /* child */
     36 		execvp(argv[0], argv);
     37 		abort();
     38 		break;
     39 	case -1: /* error */
     40 		r_fatalf("fork: %s", strerror(errno));
     41 		break;
     42 	}
     43 }
     44 
     45 xcb_connection_t *
     46 xinit(void) {
     47 	xcb_connection_t *conn = xcb_connect(0, 0);
     48 	if (xcb_connection_has_error(conn))
     49 		r_fatalf("XCB connection error");
     50 
     51 	/* Init XKB */
     52 	u16 major = XCB_XKB_MAJOR_VERSION;
     53 	u16 minor = XCB_XKB_MINOR_VERSION;
     54 	xcb_xkb_use_extension_cookie_t cookie =
     55 		xcb_xkb_use_extension(conn, major, minor);
     56 	xcb_xkb_use_extension_reply_t *useext =
     57 		xcb_xkb_use_extension_reply(conn, cookie, 0);
     58 	if (!useext)
     59 		r_fatalf("XkbUseExtension");
     60 	if (!useext->supported)
     61 		r_fatalf("X server does not support XKB version %d.%d", major, minor);
     62 
     63 	return conn;
     64 }
     65 
     66 int
     67 main(int argc, char **argv) {
     68 	if (argc < 2) {
     69 		fprintf(stderr, "usage: %s CMD [ARGS...]\n", argv[0]);
     70 		exit(1);
     71 	}
     72 
     73 	if (sigaction(SIGCHLD, &(struct sigaction){.sa_handler = sigchld}, 0) < 0)
     74 		r_fatalf("sigaction: %s", strerror(errno));
     75 	
     76 	xcb_connection_t *conn = xinit();
     77 	fcntl(xcb_get_file_descriptor(conn), F_SETFD, FD_CLOEXEC);
     78 
     79 	xcb_void_cookie_t cookie = xcb_xkb_select_events_checked(
     80 		conn,
     81 		XCB_XKB_ID_USE_CORE_KBD,
     82 		XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY, /* events to affect */
     83 		0,                                      /* details to clear */
     84 		XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY, /* details to set entirely */
     85 		0, 0, 0                                 /* irrelevant */
     86 	);
     87 	if (xcb_request_check(conn, cookie))
     88 		r_fatalf("XkbSelectEvents");
     89 
     90 	u8 xkbevtbase = xcb_get_extension_data(conn, &xcb_xkb_id)->first_event;
     91 
     92 	for (;;) {
     93 		xcb_generic_event_t *evt = xcb_wait_for_event(conn);
     94 		if (!evt)
     95 			r_fatalf("IO error while waiting for event");
     96 
     97 		u8 code = evt->response_type & ~0x80;
     98 		if (code != xkbevtbase)
     99 			goto skip;
    100 
    101 		xcb_xkb_generic_event_t *xkbevt = (void *)evt;
    102 		if (xkbevt->xkbType != XCB_XKB_NEW_KEYBOARD_NOTIFY) {
    103 			r_warnf("unexpected XKB event");
    104 			goto skip;
    105 		}
    106 
    107 		spawn(&argv[1]);
    108 
    109 	skip:
    110 		free(evt);
    111 	}
    112 }