commit 9514cb8f2bb57de34c8e5a63bda8da79e92aae64
parent f711b9eed4b7b9a9804bb8c284954b2a975545c3
Author: Louis Burda <quent.burda@gmail.com>
Date: Mon, 20 Feb 2023 18:15:55 +0100
Using XInput2 extension for raw access
Diffstat:
M | Makefile | | | 2 | +- |
M | xkeylog.c | | | 160 | +++++++++++++++++++++++++++++++++++++++++++++++++------------------------------ |
2 files changed, 100 insertions(+), 62 deletions(-)
diff --git a/Makefile b/Makefile
@@ -1,7 +1,7 @@
PREFIX ?= /usr/local
BINDIR ?= /bin
-LDLIBS = -lX11
+LDLIBS = -lX11 -lXi
all: xkeylog
diff --git a/xkeylog.c b/xkeylog.c
@@ -1,7 +1,12 @@
+/* HEAVILY inspired by anko/xkbcat */
+
+#include <X11/extensions/XI2.h>
+#include <X11/extensions/XInput2.h>
+#include <X11/XKBlib.h>
#include <X11/X.h>
#include <X11/Xlib.h>
-#include <X11/Xutil.h>
+#include <X11/extensions/XKB.h>
#include <sys/types.h>
#include <signal.h>
#include <err.h>
@@ -13,83 +18,116 @@
const int mask = KeyPressMask | KeyReleaseMask | FocusChangeMask;
-FILE *logfile;
-Display *dpy;
-Window root;
-Window focus;
+int keysym_group;
+int xinput_op;
+int xkb_event;
void
-close_log(void)
+check_support(Display *dpy, int *xinput_op)
{
- fclose(logfile);
+ int op, ev, err;
+ int vminor, vmajor;
+ int ret;
+
+ ret = XQueryExtension(dpy, "XInputExtension", xinput_op, &ev, &err);
+ if (!ret) errx(1, "missing XInput extension");
+
+ vmajor = 2;
+ vminor = 0;
+ ret = XIQueryVersion(dpy, &vmajor, &vminor);
+ if (ret == BadRequest)
+ errx(1, "missing XInput2 support");
+
+ ret = XkbQueryExtension(dpy, &op, &ev, &err, &vmajor, &vminor);
+ if (!ret) errx(1, "missing Xkb extension");
}
void
-handle_event(void)
+setup_xinput(Display *dpy, Window root)
{
- XComposeStatus comp;
- XEvent ev;
- KeySym ks;
- char strbuf[128];
- int revert;
- int len;
-
- XNextEvent(dpy, &ev);
- switch (ev.type) {
- case FocusOut:
- fprintf(logfile, "Focus changed!\n");
- fprintf(logfile, "Old focus is %d\n", (int)focus);
- if (focus != root)
- XSelectInput(dpy, focus, 0);
- XGetInputFocus(dpy, &focus, &revert);
- fprintf(logfile, "New focus is %d\n", (int)focus);
- if (focus == PointerRoot)
- focus = root;
- XSelectInput(dpy, focus, mask);
- break;
- case KeyPress:
- fprintf(logfile, "Got key!\n");
- len = XLookupString(&ev.xkey, strbuf,
- sizeof(strbuf) - 1, &ks, &comp);
- if (len > 0) {
- strbuf[len] = 0;
- fprintf(logfile, "String is: %s\n", strbuf);
- } else {
- fprintf(logfile, "Key is: %d\n", (int)ks);
- }
- break;
- }
+ XIEventMask mask;
+
+ mask.deviceid = XIAllMasterDevices;
+ mask.mask_len = XIMaskLen(XI_LASTEVENT);
+ mask.mask = calloc(mask.mask_len, 1);
+ if (!mask.mask) err(1, "calloc");
+
+ XISetMask(mask.mask, XI_RawKeyPress);
+ XISetMask(mask.mask, XI_RawKeyRelease);
+ XISelectEvents(dpy, root, &mask, 1);
+ XSync(dpy, false);
+
+ free(mask.mask);
}
-int
-main(int argc, const char **argv)
+void
+handle_event(Display *dpy, Window root)
{
- const char *filepath;
- const char **arg;
- int revert;
-
- filepath = NULL;
- for (arg = argv + 1; *arg; arg++) {
- if (!filepath) {
- filepath = *arg;
- } else {
- errx(1, "unknown arg: %s", *arg);
- }
+ XGenericEventCookie *cookie;
+ XkbEvent *xkbev;
+ XIRawEvent *xiev;
+ XEvent xev;
+ KeySym sym;
+ char *name;
+
+ cookie = &xev.xcookie;
+ XNextEvent(dpy, &xev);
+
+ if (!XGetEventData(dpy, cookie)) {
+ /* keysym group change event */
+ if (xev.type != xkb_event)
+ return;
+ xkbev = (XkbEvent *)&xev;
+ if (xkbev->any.xkb_type == XkbStateNotify)
+ keysym_group = xkbev->state.group;
+ return;
}
- if (filepath) {
- logfile = fopen(filepath, "w+");
- if (!logfile) err(1, "fopen");
- atexit(close_log);
+ if (cookie->type != GenericEvent || cookie->extension != xinput_op)
+ return;
+
+ if (cookie->evtype != XI_RawKeyPress && cookie->evtype != XI_RawKeyRelease)
+ return;
+
+ xiev = cookie->data;
+ sym = XkbKeycodeToKeysym(dpy, xiev->detail, keysym_group, 0);
+ if (sym == NoSymbol) {
+ if (keysym_group == 0) return;
+ /* NoSymbol on upper layers defers to base group */
+ sym = XkbKeycodeToKeysym(dpy, xiev->detail, 0, 0);
+ if (sym == NoSymbol) return;
+ }
+
+ name = XKeysymToString(sym);
+ if (name == NULL) return;
+
+ if (cookie->evtype == XI_RawKeyPress) {
+ printf("+%s\n", name);
} else {
- logfile = stdout;
+ printf("-%s\n", name);
}
+ fflush(stdout);
+}
+
+int
+main(int argc, const char **argv)
+{
+ XkbStateRec state;
+ Display *dpy;
+ Window root;
dpy = XOpenDisplay(NULL);
root = DefaultRootWindow(dpy);
- XGetInputFocus(dpy, &focus, &revert);
- XSelectInput(dpy, focus, mask);
+ check_support(dpy, &xinput_op);
+ setup_xinput(dpy, root);
+
+ XkbSelectEventDetails(dpy, XkbUseCoreKbd, XkbStateNotify,
+ XkbGroupStateMask, XkbGroupStateMask);
+ XkbGetState(dpy, XkbUseCoreKbd, &state);
+ keysym_group = state.group;
- while (1) handle_event();
+ while (1) {
+ handle_event(dpy, root);
+ }
}