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

如何让极简XLIB窗口管理器支持OnBoard虚拟键盘向其他窗口传输入

嘿,这个问题我之前也碰到过!你的极简Xlib窗口管理器之所以没法让OnBoard给其他窗口传递输入,核心是缺了输入焦点管理和基础的顶级窗口事件处理——DWM看起来没特意写OnBoard相关的逻辑,但它默认实现了X窗口管理器必备的焦点切换、窗口映射管理,这些正是OnBoard正常工作的前提。

下面是具体的修改方案和完整代码:

核心原因拆解

OnBoard这类屏幕键盘是通过X11的事件机制发送模拟键盘输入的,这些事件只会被当前拥有输入焦点的窗口接收。你的原始代码只创建了一个简单窗口,既没处理xterm、OnBoard这类外部顶级窗口的显示请求,也没实现焦点切换逻辑,导致输入焦点始终锁在WM自己的窗口上,OnBoard的输入自然传不到xterm里。

修改后的完整代码

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

int main() {
    Display *display;
    Window root;
    Window current_focus = None; // 跟踪当前拥有输入焦点的窗口
    XEvent event;
    int s;

    // 打开与X服务器的连接
    display = XOpenDisplay(NULL);
    if (display == NULL) {
        fprintf(stderr, "Cannot open display\n");
        exit(1);
    }
    s = DefaultScreen(display);
    root = RootWindow(display, s);

    // 监听根窗口的关键事件:窗口映射请求、鼠标点击、窗口进入/离开、键盘事件
    XSelectInput(display, root, 
        SubstructureRedirectMask | SubstructureNotifyMask |
        ButtonPressMask | EnterWindowMask | LeaveWindowMask |
        KeyPressMask | KeyReleaseMask);

    // 主事件循环
    while (1) {
        XNextEvent(display, &event);

        switch(event.type) {
            // 处理窗口映射请求:允许xterm、OnBoard这类客户端窗口显示
            case MapRequest:
                XMapWindow(display, event.xmaprequest.window);
                break;

            // 鼠标点击窗口时切换输入焦点
            case ButtonPress:
                if (event.xbutton.window != root) {
                    XSetInputFocus(display, event.xbutton.window, RevertToPointerRoot, CurrentTime);
                    current_focus = event.xbutton.window;
                }
                break;

            // 鼠标进入窗口时自动切换焦点(可选,按需开启)
            case EnterNotify:
                if (event.xcrossing.window != root && event.xcrossing.mode == NotifyNormal) {
                    XSetInputFocus(display, event.xcrossing.window, RevertToPointerRoot, CurrentTime);
                    current_focus = event.xcrossing.window;
                }
                break;

            // 保留你原来的键盘事件处理逻辑
            case KeyPress:
                printf("KeyPress: %x\n", event.xkey.keycode);
                // 按ESC键退出(注意:不同系统ESC的keycode可能不同,这里0x09是示例)
                if (event.xkey.keycode == 0x09) {
                    goto cleanup;
                }
                break;

            case KeyRelease:
                printf("KeyRelease: %x\n", event.xkey.keycode);
                break;

            // 其他事件默认忽略
            default:
                break;
        }
    }

cleanup:
    XCloseDisplay(display);
    return 0;
}

关键修改点说明

  1. 监听根窗口事件:不再只监听WM自己的窗口,而是监听根窗口的SubstructureRedirectMask和SubstructureNotifyMask——这能让WM拦截并处理其他客户端的窗口显示请求,不然xterm、OnBoard根本没法正常显示。
  2. 处理MapRequest事件:当客户端窗口要显示时,WM必须允许它映射到屏幕上,这行XMapWindow就是干这个的。
  3. 焦点管理逻辑:通过ButtonPress(点击窗口)和EnterNotify(鼠标进入窗口)事件,调用XSetInputFocus把输入焦点切换到目标窗口——这是OnBoard输入能传递的核心,因为X11的键盘事件只会发给焦点窗口。
  4. 跟踪焦点状态:用current_focus变量记录当前焦点窗口,方便后续扩展(比如焦点边框高亮之类的)。

编译与测试

编译时记得链接X11库:

gcc -o minimal-wm minimal-wm.c -lX11

然后用xinit启动你的WM:

xinit ./minimal-wm -- :1

启动后,在另一个终端(如果是远程连接的话)用下面的命令启动xterm和OnBoard:

DISPLAY=:1 xterm &
DISPLAY=:1 onboard &

点击xterm窗口后,OnBoard的键盘输入应该就能正常传递到xterm里了!


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 15:37:51