I want to record all incoming key pressed events no matter what window is in focus or where the pointer is.
I have written a sample code which should capture the key pressed events 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 calling XGrabKeyboard to capture the keyboard as the application who created the window might have the keyboard events already grabbed. With the above mentioned code I am able to grab the keyboard but am unable to receive either of KeyPress or KeyRelease events for any keys on the keyboard inside the while loop. Is there anything that I am missing in the code due to which I am unable to receive the events ? Any help is highly appreciated.
My final aim is to capture key press events on the screen irrespective of the Window in focus. I have given the sample code for only window in focus for the code to be readable . I would do XQueryTree to get all the Windows and apply the same logic given above to get the expected result.
You need to have a mapped window to be able to grab the keyboard. Here is a proof of concept:
It uses a small window (I didn't even bother to set a title for it) it lowers to bottom of the window stack, so it goes behind any existing windows. You can communicate with the window manager (WM) to make the window decorationless and transparent, or iconified, so that there is no visible window on-screen; the above code does not bother.
The trick I used is that whenever the user manages to unmap the window -- say, by moving to another workspace --, the code destroys the old window, creates a new one, and re-grabs the keyboard. It should be fast enough to not lose any keypresses. There might be other ways to do it, but I suspect they require closer interaction with the window manager.
Note that I have never needed to really grab the keyboard so persistently, so the above approach is likely not the simplest. It was just an approach I think works; there are likely better ones.
The following command will print a list of all events of the whole X session to the console:
Example output: