Я пишу фиктивное окно для программного обеспечения 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 дважды? Или это баг / фича моего оконного менеджера?