我为软件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?还是我的窗口管理器的错误/功能?
没有您的实现,实际上不可能确切知道所看到行为的驱动程序是什么,但是手册页的确提供了对可能发生的情况的一些了解。
此特定行为可能是last-focus-change-时间的结果。从this man page:
... XSetInputFocus()函数更改输入焦点和最后更改焦点的时间。如果指定时间早于当前时间,则无效上次焦点更改时间或晚于当前X服务器时间。