xkeylog.c (3096B)
1/* HEAVILY inspired by anko/xkbcat */ 2 3#include <X11/extensions/XI2.h> 4#include <X11/extensions/XInput2.h> 5#include <X11/XKBlib.h> 6#include <X11/X.h> 7#include <X11/Xlib.h> 8 9#include <X11/extensions/XKB.h> 10#include <sys/types.h> 11#include <signal.h> 12#include <ctype.h> 13#include <string.h> 14#include <stdio.h> 15#include <stdbool.h> 16#include <stdarg.h> 17#include <stdlib.h> 18 19static int keysym_group; 20static int xinput_op; 21static int xkb_event; 22 23static void 24__attribute__((format(printf,1,2))) 25die(const char *fmt, ...) 26{ 27 va_list ap; 28 29 va_start(ap, fmt); 30 fputs("xkeylog: ", stderr); 31 vfprintf(stderr, fmt, ap); 32 if (*fmt && fmt[strlen(fmt)-1] == ':') { 33 fputc(' ', stderr); 34 perror(NULL); 35 } else { 36 fputc('\n', stderr); 37 } 38 va_end(ap); 39 40 exit(1); 41} 42 43static void 44check_support(Display *dpy, int *xinput_op) 45{ 46 int op, ev, err; 47 int vminor, vmajor; 48 int ret; 49 50 ret = XQueryExtension(dpy, "XInputExtension", xinput_op, &ev, &err); 51 if (!ret) die("missing XInput extension"); 52 53 vmajor = 2; 54 vminor = 0; 55 ret = XIQueryVersion(dpy, &vmajor, &vminor); 56 if (ret == BadRequest) 57 die("missing XInput2 support"); 58 59 ret = XkbQueryExtension(dpy, &op, &ev, &err, &vmajor, &vminor); 60 if (!ret) die("missing Xkb extension"); 61} 62 63static void 64setup_xinput(Display *dpy, Window root) 65{ 66 XIEventMask mask; 67 68 mask.deviceid = XIAllMasterDevices; 69 mask.mask_len = XIMaskLen(XI_LASTEVENT); 70 mask.mask = calloc(mask.mask_len, 1); 71 if (!mask.mask) die("calloc:"); 72 73 XISetMask(mask.mask, XI_RawKeyPress); 74 XISetMask(mask.mask, XI_RawKeyRelease); 75 XISelectEvents(dpy, root, &mask, 1); 76 XSync(dpy, false); 77 78 free(mask.mask); 79} 80 81static void 82handle_event(Display *dpy, Window root) 83{ 84 XGenericEventCookie *cookie; 85 XkbEvent *xkbev; 86 XIRawEvent *xiev; 87 XEvent xev; 88 KeySym sym; 89 char *name; 90 91 cookie = &xev.xcookie; 92 XNextEvent(dpy, &xev); 93 94 if (!XGetEventData(dpy, cookie)) { 95 /* keysym group change event */ 96 if (xev.type != xkb_event) 97 return; 98 xkbev = (XkbEvent *)&xev; 99 if (xkbev->any.xkb_type == XkbStateNotify) 100 keysym_group = xkbev->state.group; 101 return; 102 } 103 104 if (cookie->type != GenericEvent || cookie->extension != xinput_op) 105 return; 106 107 if (cookie->evtype != XI_RawKeyPress && cookie->evtype != XI_RawKeyRelease) 108 return; 109 110 xiev = cookie->data; 111 sym = XkbKeycodeToKeysym(dpy, xiev->detail, keysym_group, 0); 112 if (sym == NoSymbol) { 113 if (keysym_group == 0) return; 114 /* NoSymbol on upper layers defers to base group */ 115 sym = XkbKeycodeToKeysym(dpy, xiev->detail, 0, 0); 116 if (sym == NoSymbol) return; 117 } 118 119 name = XKeysymToString(sym); 120 if (name == NULL) return; 121 122 if (cookie->evtype == XI_RawKeyPress) { 123 printf("+%s\n", name); 124 } else { 125 printf("-%s\n", name); 126 } 127 fflush(stdout); 128} 129 130int 131main(int argc, const char **argv) 132{ 133 XkbStateRec state; 134 Display *dpy; 135 Window root; 136 137 dpy = XOpenDisplay(NULL); 138 root = DefaultRootWindow(dpy); 139 140 check_support(dpy, &xinput_op); 141 setup_xinput(dpy, root); 142 143 XkbSelectEventDetails(dpy, XkbUseCoreKbd, XkbStateNotify, 144 XkbGroupStateMask, XkbGroupStateMask); 145 XkbGetState(dpy, XkbUseCoreKbd, &state); 146 keysym_group = state.group; 147 148 while (1) handle_event(dpy, root); 149}