Question

I am currently searching for a way to receive widgets focus change events from X server on Linux OS.

I have tried using XSelectInput(dpy, focuswin, FocusChangeMask);, but the server notifies me only when the focused window changes, not the focused widget (e.g. a text input) inside a specific window.

I want to accomplish this in order to show a virtual keyboard whenever an editable text area gains focus.

The code written until now is:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/Intrinsic.h>

static Display *dpy;
static Window focuswin = None;

static void attach_to_focuswin(void) {
    int revert_to = 0;

    XGetInputFocus(dpy, &focuswin, &revert_to);
    XSetWindowAttributes attr;
    attr.event_mask = FocusChangeMask;
    XChangeWindowAttributes(dpy, focuswin, CWEventMask, &attr);

    if (focuswin != None)
            XSelectInput(dpy, focuswin, FocusChangeMask);
    else
        sleep(1);
}

static void handle_event(void) {
    XEvent ev;
    char buf[100];
    int len;

    XNextEvent(dpy, &ev);
    if (ev.xany.type == FocusOut) {
        focuswin = None;
        fprintf(stdout, "func: handle_event -> focusing out of window\n\n\n");
    } else if (ev.xany.type == FocusIn) {
        fprintf(stdout, "func: handle_event -> focusing out of window\n\n\n");
    } else if (ev.xany.type == KeyPress) {
        len = XLookupString(&ev.xkey, buf, 99, 0, 0);
        buf[len] = 0;
        printf("%s", buf);
        fflush(stdout);
    } else {
        fprintf(stdout, "func: handle_event -> something else %d\n\n\n", ev.type);
    }
}

int main(void) {

    dpy = XOpenDisplay(getenv("DISPLAY"));

    if (dpy == NULL) {
        fprintf(stdout, "cannot init display\n");
        exit(1);
    }

    while (1) {
        if (focuswin == None)
            attach_to_focuswin();
        else
            handle_event();
    }
}
Was it helpful?

Solution

The X server has no concept of widgets. It only knows windows.

If an application has a text input, a radio group, and a push button implemented all in a single window, then nothing outside of the application has any idea about which widget it currently considers active or focused or whatever.

If the text widget is actually implemented as a window, you can get focus change events on it. You need to call XSelectInput on that window.

In addition, it's not clear how you can differentiate between text input windows and other kinds of windows in other applications. The X server has no idea which windows are text-input ones.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top