Почему XServer отправляет FocusOut уведомление дважды - PullRequest
1 голос
/ 09 января 2020

Я пишу фиктивное окно для программного обеспечения KVM . Идея в том, что когда пользователь переключается на другую машину, предыдущая машина открывает фиктивное окно за пределами экрана, поэтому имитирует потерянный фокус. Windows без фокуса прозрачны в моей системе (удивительный wm). Если я просто использую XSetInputFocus(dpy, None, RevertToNone, CurrentTime), текущее окно теряет фокус, но не становится прозрачным. Вот почему я использую фиктивное окно.

Подойди к делу. Моя программа сохраняет сфокусированное окно, прежде чем открывает фиктивное окно. Когда фиктивное окно теряет фокус, программа восстанавливает сохраненное. Поэтому необходимо перехватить событие FocusOut, а затем вызвать функцию XSetInputFocus для восстановления фокуса. Проблема в том, что xserver отправляет FocusOut уведомление дважды. Если я обработаю это только однажды, программа работает неправильно.

Код ниже.

#include <X11/Xlib.h>
#include <X11/Xatom.h>

int x = 100, y = 100, height = 200, width = 200;

typedef struct {
    Window win;
    int notify;
} wait_arg;

Bool xevent_handler(Display *dpy, XEvent *ev, XPointer arg) {
    wait_arg *a = (wait_arg *) arg;
    return (ev->type == a->notify) && (ev->xvisibility.window == a->win);
}

int main(int argc, char *argv[]) {
    Display *dpy;
    Window focused;
    int revert_to;
    Window win;
    wait_arg arg;
    XEvent ev;

    dpy = XOpenDisplay(NULL);

    // Save window which has focus now
    XGetInputFocus(dpy, &focused, &revert_to);

    int s = DefaultScreen(dpy);
    win = XCreateSimpleWindow(dpy, RootWindow(dpy, s), 0, 0, height, width, 0,
            CopyFromParent, CopyFromParent);

    // Set DIALOG type for window
    Atom type = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
    long value = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
    XChangeProperty(dpy, win, type, XA_ATOM, 32, PropModeReplace,
            (unsigned char *) &value, 1);

    // Set events to handle
    XSelectInput(dpy, win, VisibilityChangeMask | FocusChangeMask);

    // Draw window
    XMapWindow(dpy, win);

    // Wait until window stands visible, otherwise will get
    // "Error of failed request: BadMatch"
    arg.win = win;
    arg.notify = VisibilityNotify;
    XIfEvent(dpy, &ev, &xevent_handler, (XPointer) &arg);

    XSetInputFocus(dpy, win, RevertToNone, CurrentTime);
    XSync(dpy, False);

    XMoveWindow(dpy, win, x, y);

    // Wait until focus lost
    arg.win = win;
    arg.notify = FocusOut;
    XIfEvent(dpy, &ev, &xevent_handler, (XPointer) &arg);
    // Why I should handle this event twice?
    XIfEvent(dpy, &ev, &xevent_handler, (XPointer) &arg);

    // Restore focus
    XSetInputFocus(dpy, focused, revert_to, CurrentTime);
    XSync(dpy, False);
}

Как проверить неправильное поведение:

Я комментирую второй FocusOut обработка. Я использую два монитора: левый и правый. Я запускаю программу в терминале на правом мониторе. Программа открывает пустое окно на левом мониторе. Когда фиктивное окно теряет фокус, другое окно на левом мониторе получает его. Нет окна терминала на правом мониторе!

Почему xserver отправляет FocusOut дважды? Или это баг / фича моего оконного менеджера?

1 Ответ

1 голос
/ 09 января 2020

Без вашей реализации действительно невозможно точно знать, что является драйвером для поведения, которое вы видите, но справочные страницы дают некоторое представление о том, что может происходить.

Это конкретное поведение может быть результатом последнего изменения фокуса времени. Начиная с этой справочной страницы :

... Функция XSetInputFocus () изменяет фокус ввода и время изменения последнего фокуса. Он не действует, если указанное время раньше, чем текущее время последнего изменения фокуса или позже, чем текущее время X-сервера.

...