aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKazuki Yamaguchi <k@rhe.jp>2021-09-08 13:11:44 +0900
committerKazuki Yamaguchi <k@rhe.jp>2021-09-08 13:26:19 +0900
commit2bc4ef9a54e16c3f1be9ab3ba3ad32bd35eca9e1 (patch)
treec258af4cfd35d23056597fba98eb69c598cca962
parent5fce9a3d23b4a219ef1c718d8e09c802c8b6cbb4 (diff)
downloadthotkeys-2bc4ef9a54e16c3f1be9ab3ba3ad32bd35eca9e1.tar.gz
use XI2 raw events
Although I don't understand what exactly is happening, running "xinput test-xi2 --root" on another terminal makes the current thotkeys fail to capture key events properly. XI2.1 states explicitly that RawEvents are sent regardless of the grab state of another client. Let's give it a try.
-rw-r--r--Makefile.am2
-rw-r--r--configure.ac2
-rw-r--r--thotkeys.c132
3 files changed, 66 insertions, 70 deletions
diff --git a/Makefile.am b/Makefile.am
index c466f86..b64c654 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,2 +1,2 @@
bin_PROGRAMS = thotkeys
-thotkeys_LDADD = @X11_LIBS@ @XINPUT_LIBS@
+thotkeys_LDADD = @X11_LIBS@ @XI21_LIBS@
diff --git a/configure.ac b/configure.ac
index b01755f..c05afe3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -13,7 +13,7 @@ CFLAGS="$CFLAGS -Wall -Wextra -Wconversion -Wno-parentheses"
# Checks for libraries.
PKG_CHECK_MODULES(X11, [x11])
-PKG_CHECK_MODULES(XINPUT, [xi])
+PKG_CHECK_MODULES(XI21, [xi >= 1.4.99.1] [inputproto >= 2.0.99.1])
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
diff --git a/thotkeys.c b/thotkeys.c
index 3daf3fd..90ff03e 100644
--- a/thotkeys.c
+++ b/thotkeys.c
@@ -7,8 +7,10 @@
#include <errno.h>
#include <stdbool.h>
#include <sys/wait.h>
+#include <X11/X.h>
+#include <X11/Xlib.h>
#include <X11/XKBlib.h>
-#include <X11/extensions/XInput.h>
+#include <X11/extensions/XInput2.h>
static int VERBOSE = 0;
@@ -34,6 +36,14 @@ static inline void *xrealloc(void *o, size_t size)
return p;
}
+static inline void *xcalloc(size_t nmemb, size_t size)
+{
+ void *p = calloc(nmemb, size);
+ if (!p)
+ fatal("calloc failed\n");
+ return p;
+}
+
static Display *get_display(void)
{
Display *display = XOpenDisplay(NULL);
@@ -42,7 +52,7 @@ static Display *get_display(void)
return display;
}
-static XDeviceInfo *get_device_info(Display *display, const char *name)
+static XIDeviceInfo *get_device_info(Display *display, const char *name)
{
bool use_id = true;
long id;
@@ -54,15 +64,15 @@ static XDeviceInfo *get_device_info(Display *display, const char *name)
use_id = false;
int num_devices;
- XDeviceInfo *devices = XListInputDevices(display, &num_devices);
+ XIDeviceInfo *devices = XIQueryDevice(display, XIAllDevices, &num_devices);
- XDeviceInfo *found = NULL;
+ XIDeviceInfo *found = NULL;
for (int i = 0; i < num_devices; i++) {
- XDeviceInfo *device = &devices[i];
+ XIDeviceInfo *device = &devices[i];
- if (device->use != IsXExtensionKeyboard)
+ if (device->use != XISlaveKeyboard)
continue;
- if (!strcmp(device->name, name) || use_id && (long)device->id == id) {
+ if (!strcmp(device->name, name) || use_id && (long)device->deviceid == id) {
if (found)
fatal("more than one keyboard found with the " \
"name '%s'\n", name);
@@ -72,65 +82,47 @@ static XDeviceInfo *get_device_info(Display *display, const char *name)
return found;
}
-static XID key_press_type, key_release_type;
-static bool is_key_press_event(const XDeviceKeyEvent *ev)
-{
- return ev->type == (int)key_press_type;
-}
-
-static void register_events(Display *display, XDevice *device)
-{
- int screen = DefaultScreen(display);
- Window root_win = RootWindow(display, screen);
-
- XEventClass event_list[2];
- DeviceKeyPress(device, key_press_type, event_list[0]);
- DeviceKeyRelease(device, key_release_type, event_list[1]);
- if (XSelectExtensionEvent(display, root_win, event_list, 2))
- fatal("XSelectExtensionEvent() failed\n");
-}
-
static void prepare_monitor(Display *display, const char *device_name)
{
- XDeviceInfo *info = get_device_info(display, device_name);
+ XIDeviceInfo *info = get_device_info(display, device_name);
if (!info)
fatal("unable to find device '%s'\n", device_name);
- XDevice *device = XOpenDevice(display, info->id);
- if (!device)
- fatal("unable to open device '%s'\n", device_name);
-
- register_events(display, device);
+ XIEventMask mask;
+ mask.deviceid = info->deviceid;
+ mask.mask_len = XIMaskLen(XI_LASTEVENT);
+ mask.mask = xcalloc((size_t)mask.mask_len, 1);
+ XISetMask(mask.mask, XI_RawKeyPress);
+ XISetMask(mask.mask, XI_RawKeyRelease);
+
+ if (XISelectEvents(display, DefaultRootWindow(display), &mask, 1))
+ fatal("XISelectEvents() failed\n");
+ XSync(display, False);
+ free(mask.mask);
}
-
-static XEvent process_event_ev;
-static const XDeviceKeyEvent *process_event(Display *display)
+static const XIRawEvent *process_event(Display *display, int *evtype)
{
-redo:
- XNextEvent(display, &process_event_ev);
-
- if (process_event_ev.type == (int)key_press_type)
- return (XDeviceKeyEvent *)&process_event_ev;
- if (process_event_ev.type == (int)key_release_type) {
- // Retriggered events
- if (XEventsQueued(display, QueuedAfterReading)) {
- XEvent nev;
- XPeekEvent(display, &nev);
-
- if (nev.type == (int)key_press_type &&
- nev.xkey.time == process_event_ev.xkey.time &&
- nev.xkey.keycode == process_event_ev.xkey.keycode) {
- // Consume the following KeyPress
- XNextEvent(display, &nev);
- goto redo;
- }
- }
- return (XDeviceKeyEvent *)&process_event_ev;
+ static XEvent ev;
+ XGenericEventCookie *cookie = &ev.xcookie;
+
+ static int xi_opcode;
+ if (!xi_opcode) {
+ int event, error;
+ if (!XQueryExtension(display, "XInputExtension", &xi_opcode, &event, &error))
+ fatal("X Input extension not available\n");
}
- debug("ignoring event %d\n", process_event_ev.type);
- goto redo;
+redo:
+ XNextEvent(display, &ev);
+ if (!XGetEventData(display, cookie) ||
+ cookie->type != GenericEvent ||
+ cookie->extension != xi_opcode ||
+ (cookie->evtype != XI_RawKeyPress && cookie->evtype != XI_RawKeyRelease))
+ goto redo;
+
+ *evtype = cookie->evtype;
+ return cookie->data;
}
static void command_help(void)
@@ -161,12 +153,14 @@ static void command_monitor(const char *device_name)
char keymap[256] = { 0 };
while (1) {
- const XDeviceKeyEvent *ev = process_event(display);
- bool pressed = is_key_press_event(ev);
+ int evtype;
+ const XIRawEvent *data = process_event(display, &evtype);
+ int keycode = data->detail;
+ bool pressed = evtype == XI_RawKeyPress;
- if (ev->keycode > 255)
- fatal("unexpected keycode %d\n", (int)ev->keycode);
- keymap[ev->keycode] = pressed;
+ if (keycode > 255)
+ fatal("unexpected keycode %d\n", keycode);
+ keymap[keycode] = pressed;
for (int i = 0; i < 256; i++) {
if (keymap[i]) {
@@ -174,7 +168,7 @@ static void command_monitor(const char *device_name)
printf("--key %s ", XKeysymToString(keysym));
}
}
- KeySym basekeysym = XkbKeycodeToKeysym(display, (KeyCode)ev->keycode, 0, 0);
+ KeySym basekeysym = XkbKeycodeToKeysym(display, (KeyCode)keycode, 0, 0);
printf("# %s %s\n",
pressed ? "pressed" : "released",
XKeysymToString(basekeysym));
@@ -232,18 +226,20 @@ static void command_hotkeys(const char *device_name, struct hotkey_config *hotke
}
}
- const XDeviceKeyEvent *ev = process_event(display);
- bool pressed = is_key_press_event(ev);
+ int evtype;
+ const XIRawEvent *data = process_event(display, &evtype);
+ int keycode = data->detail;
+ bool pressed = evtype == XI_RawKeyPress;
- if (ev->keycode > 255)
- fatal("unexpected keycode %d\n", (int)ev->keycode);
+ if (keycode > 255)
+ fatal("unexpected keycode %d\n", keycode);
for (size_t i = 0; i < numhotkeys; i++) {
struct hotkey_config *c = hotkeys + i;
- if (!c->checkmap[ev->keycode])
+ if (!c->checkmap[keycode])
continue;
- c->keymap[ev->keycode] = pressed;
+ c->keymap[keycode] = pressed;
bool matched = !memcmp(c->checkmap, c->keymap, sizeof(c->checkmap));
if (!c->activated && matched) {