You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

XServer为何发送两次FocusOut通知?软件KVM焦点异常排查

分析XServer发送两次FocusOut事件的原因及解决方案

这是个典型的X11窗口系统与平铺窗口管理器(Awesome WM)交互时的特性问题,咱们一步步拆解:

为什么会收到两次FocusOut事件?

首先要明确:X11的FocusOut事件本身就带有不同的触发场景,通过事件的detail字段区分(比如NotifyNormal、NotifyGrab、NotifyUngrab等)。在Awesome WM这类平铺WM中,窗口状态的变更(比如位置移动、窗口类型属性设置)会触发WM的额外逻辑,进而导致XServer发送两次FocusOut:

  1. 第一次FocusOut:通常是WM在处理虚拟窗口的状态变化时触发的——比如你调用XMoveWindow把窗口移到目标位置后,Awesome WM会重新评估窗口的布局和焦点优先级,这时候会发送一次非用户主动触发的FocusOut(可能带有NotifyGrab或其他系统级的detail值)。
  2. 第二次FocusOut:这才是真正的用户操作导致的焦点丢失(比如你切换到其他窗口/机器),对应的detail是NotifyNormal,这是你需要处理的有效事件。

你的代码里的xevent_handler只判断了事件类型和窗口,没有区分触发场景,所以会把两次事件都当成有效事件,必须调用两次XIfEvent才能等到真正需要的那一次。

这是Awesome WM的Bug吗?

不是Bug,而是平铺WM的特性。Awesome WM对窗口的管理逻辑非常严格,会在窗口的位置、类型、可见性等属性变化时,主动同步XServer的状态,确保WM的内部状态和实际窗口行为一致。这种额外的事件发送是为了避免焦点状态出现不一致,属于设计上的严谨性体现。

如何修复代码?

不需要手动调用两次XIfEvent,只需要修改事件处理器,过滤掉非用户触发的FocusOut事件即可。修改你的xevent_handler函数:

Bool xevent_handler(Display *dpy, XEvent *ev, XPointer arg) {
    wait_arg *a = (wait_arg *) arg;
    if (ev->type == a->notify) {
        if (ev->type == FocusOut) {
            // 只处理用户主动切换导致的焦点丢失(排除Grab/Ungrab等系统触发的场景)
            XFocusChangeEvent *focus_ev = &ev->xfocus;
            return (focus_ev->window == a->win) && 
                   (focus_ev->detail != NotifyGrab) && 
                   (focus_ev->detail != NotifyUngrab);
        } else if (ev->type == VisibilityNotify) {
            return (ev->xvisibility.window == a->win);
        }
    }
    return False;
}

然后去掉代码中第二次XIfEvent调用,只保留一次:

// Wait until focus lost
arg.win = win;
arg.notify = FocusOut;
XIfEvent(dpy, &ev, &xevent_handler, (XPointer) &arg);
// Restore focus
XSetInputFocus(dpy, focused, revert_to, CurrentTime);
XSync(dpy, False);

这样就能精准捕获到真正的焦点丢失事件,恢复焦点时也会正确回到原来的终端窗口。

内容的提问来源于stack exchange,提问作者Kirill Bugaev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 08:13:12