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 }