X Capturing KeyPress / Release events regardless of window in focus

I want to record all incoming keystrokes, regardless of which window is in focus or where the pointer is.

I wrote an example code that should capture the pressed keys of the current window in focus.

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <locale.h> #include <stdint.h> #include <stdarg.h> #include <errno.h> #include <pthread.h> #include <X11/Xlib.h> #include <X11/Xos.h> #include <X11/Xfuncs.h> #include <X11/Xutil.h> #include <X11/Xatom.h> int _invalid_window_handler(Display *dsp, XErrorEvent *err) { return 0; } int main() { Display *display = XOpenDisplay(NULL); int iError; KeySym k; int revert_to; Window window; XEvent event; Time time; XSetErrorHandler(_invalid_window_handler); XGetInputFocus(display, &window, &revert_to); XSelectInput(display, window, KeyPressMask | KeyReleaseMask ); iError = XGrabKeyboard(display, window, KeyPressMask | KeyReleaseMask, GrabModeAsync, GrabModeAsync, CurrentTime); if (iError != GrabSuccess && iError == AlreadyGrabbed) { XUngrabPointer(display, CurrentTime); XFlush(display); printf("Already Grabbed\n"); } else if (iError == GrabSuccess) { printf("Grabbed\n"); } while(1) { XNextEvent(display,&event); switch (event.type) { case KeyPress : printf("Key Pressed\n"); break; case KeyRelease : printf("Key Released\n"); break; case EnterNotify : printf("Enter\n"); break; } } XCloseDisplay(display); return 0; } 

I am invoking XGrabKeyboard to capture the keyboard, since the application that created the window may have already captured keyboard events. With the above code, I can grab the keyboard, but cannot get any of the KeyPress or KeyRelease events for any keys on the keyboard inside the while loop. Is there something that I am missing in the code because of which I cannot get events? Any help is appreciated.

My ultimate goal is to capture key click events on the screen, no matter which window is in focus. I gave the sample code only for the focus of the window so that the code is read. I would do XQueryTree to get all of Windows and apply the same logic as above to get the expected result.

+6
source share
2 answers

You need to have a window displayed in order to be able to capture the keyboard. Here is the proof of concept:

 #include <X11/Xlib.h> #include <X11/keysym.h> #include <stdio.h> int main() { Display *display; Window window, rootwindow; XEvent event; KeySym escape; display = XOpenDisplay(NULL); rootwindow = DefaultRootWindow(display); window = XCreateWindow(display, rootwindow, -99, -99, 1, 1, /* x, y, width, height */ 0, 0, InputOnly, /* border, depth, class */ CopyFromParent, /* visual */ 0, NULL); /* valuemask and attributes */ XSelectInput(display, window, StructureNotifyMask | SubstructureRedirectMask | ResizeRedirectMask | KeyPressMask | KeyReleaseMask); XLowerWindow(display, window); XMapWindow(display, window); do { XNextEvent(display, &event); } while (event.type != MapNotify); XGrabKeyboard(display, window, False, GrabModeAsync, GrabModeAsync, CurrentTime); XLowerWindow(display, window); escape = XKeysymToKeycode(display, XK_Escape); printf("\nPress ESC to exit.\n\n"); fflush(stdout); while (1) { XNextEvent(display, &event); if (event.type == KeyPress) { printf("KeyPress: keycode %u state %u\n", event.xkey.keycode, event.xkey.state); fflush(stdout); } else if (event.type == KeyRelease) { printf("KeyRelease: keycode %u state %u\n", event.xkey.keycode, event.xkey.state); fflush(stdout); if (event.xkey.keycode == escape) break; } else if (event.type == UnmapNotify) { XUngrabKeyboard(display, CurrentTime); XDestroyWindow(display, window); XCloseDisplay(display); display = XOpenDisplay(NULL); rootwindow = DefaultRootWindow(display); window = XCreateWindow(display, rootwindow, -99, -99, 1, 1, /* x, y, width, height */ 0, 0, InputOnly, /* border, depth, class */ CopyFromParent, /* visual */ 0, NULL); /* valuemask and attributes */ XSelectInput(display, window, StructureNotifyMask | SubstructureRedirectMask | ResizeRedirectMask | KeyPressMask | KeyReleaseMask); XLowerWindow(display, window); XMapWindow(display, window); do { XNextEvent(display, &event); } while (event.type != MapNotify); XGrabKeyboard(display, window, False, GrabModeAsync, GrabModeAsync, CurrentTime); XLowerWindow(display, window); escape = XKeysymToKeycode(display, XK_Escape); } else { printf("Event type %d\n", event.type); fflush(stdout); } } XUngrabKeyboard(display, CurrentTime); XDestroyWindow(display, window); XCloseDisplay(display); return 0; } 

It uses a small window (I didn’t even bother to set a title for it), it drops to the bottom of the window stack, so it goes for any existing windows. You can contact the window manager (WM) to make the window flawless and transparent or marked so that there is no visible window on the screen; the code above does not bother.

The trick I used is that whenever a window is deleted by the user, say, moving to another workspace, the code destroys the old window, creates a new one and grabs the keyboard again. It should be fast enough so as not to lose any keystrokes. There may be other ways to do this, but I suspect that they require closer interaction with the window manager.

Please note that I never needed to grab the keyboard for so long, so the above approach is most likely not the easiest. It was just an approach that I think works; most likely better.

+4
source

The following command will print to the console a list of all events of the entire X-session:

 $ xinput test-xi2 --root 

Output Example:

 ⎡ Virtual core pointer id=2 [master pointer (3)] ⎜ ↳ Virtual core XTEST pointer id=4 [slave pointer (2)] ⎜ ↳ USB Mouse id=10 [slave pointer (2)] ⎜ ↳ MCE IR Keyboard/Mouse (ite-cir) id=11 [slave pointer (2)] ⎣ Virtual core keyboard id=3 [master keyboard (2)] ↳ Virtual core XTEST keyboard id=5 [slave keyboard (3)] ↳ Power Button id=6 [slave keyboard (3)] ↳ Video Bus id=7 [slave keyboard (3)] ↳ Power Button id=8 [slave keyboard (3)] ↳ Oracle USB Keyboard id=9 [slave keyboard (3)] ↳ ITE8713 CIR transceiver id=12 [slave keyboard (3)] EVENT type 14 (RawKeyRelease) device: 3 (9) detail: 36 valuators: EVENT type 3 (KeyRelease) device: 9 (9) detail: 36 flags: root: 1324.55/821.81 event: 1324.55/821.81 buttons: modifiers: locked 0x10 latched 0 base 0 effective: 0x10 group: locked 0 latched 0 base 0 effective: 0 valuators: windows: root 0x9c event 0x9c child 0x7291d5 EVENT type 15 (RawButtonPress) device: 2 (10) detail: 1 valuators: flags: EVENT type 4 (ButtonPress) device: 10 (10) detail: 1 flags: root: 1324.55/821.81 event: 1324.55/821.81 buttons: modifiers: locked 0x10 latched 0 base 0 effective: 0x10 group: locked 0 latched 0 base 0 effective: 0 valuators: windows: root 0x9c event 0x9c child 0x7291d5 EVENT type 16 (RawButtonRelease) device: 2 (10) detail: 1 valuators: flags: 
+3
source

Source: https://habr.com/ru/post/919565/


All Articles