2

I'm looking to create a simple input method program. Currently, I'm able to capture input from any window using passive key grabs (thanks user dirkt!).

Separately, I'm also able to send key presses using XTestFakeKeyEvent().

The issue is that I can't put these two parts together. When emitting a fake key event, it is received by my program instead of the intended application that has focus. This occurs even though my program does not have a passive grab on the keycode emitted by the fake event. I suspect this is because I'm getting events from the root window, but if I don't do that, I don't think I can capture input from other windows.

#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <X11/extensions/XTest.h>
#include <stdio.h>

int main()
{
    Display *display;
    Window   rootwindow;
    XEvent   event;

    display = XOpenDisplay(NULL);
    rootwindow = DefaultRootWindow(display);

    XSelectInput(display, rootwindow, KeyPressMask);    

    unsigned int modifiers[] = {
        0, 
        ShiftMask,
        LockMask, //capslock
        Mod2Mask, //numlock
        ShiftMask | LockMask,
        ShiftMask | Mod2Mask,
        LockMask | Mod2Mask,
        LockMask | Mod2Mask | ShiftMask,
    };

    int keys [] = {
        XKeysymToKeycode(display, XK_A),
        XKeysymToKeycode(display, XK_B),
        ...
        XKeysymToKeycode(display, XK_Z)
    };

    int l;
    int m;
    for(l = 0; l < sizeof(keys)/sizeof(int); l++){
        for(m = 0; m < sizeof(modifiers)/sizeof(unsigned int); m++){
            XGrabKey(display, keys[l], modifiers[m], rootwindow, False, GrabModeAsync, GrabModeAsync);
        }
    }

    int repeat = 0;

    while (1) {

        XNextEvent(display, &event);

        if (event.type == KeyPress && event.xkey.send_event == 0) {
            printf("KeyPress: keycode %u state %u send-event %d\n", event.xkey.keycode, event.xkey.state, event.xkey.send_event);
            fflush(stdout);

            if(repeat == 0){ //to avoid infinite loops
                repeat = 1;

                XTestFakeKeyEvent(display, XKeysymToKeycode(display, XK_7), 1, CurrentTime);
                XTestFakeKeyEvent(display, XKeysymToKeycode(display, XK_7), 0, CurrentTime);
            }

        }
    }

    XCloseDisplay(display);
    return 0;
}
  • You may want to have a look at how xbindkeys does it (and xte/xdotool which you'd typically use there to send key events). – Stéphane Chazelas Apr 23 '18 at 14:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.