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

如何检测GtkSocket焦点?父窗口与Plug窗口间键盘焦点切换检测

解决Gtk Socket/Plug跨进程焦点变化监听问题

这确实是Gtk Socket/Plug架构下的一个常见痛点——因为Plug运行在独立进程中,常规的窗口事件会被进程边界隔离,直接在Socket或子窗口上绑定focus-in-event自然拿不到回调。不过有几个可靠的方法能帮你追踪跨进程的焦点变化:

1. 监听GdkDisplay的全局焦点窗口变化

整个显示的焦点窗口是全局追踪的,不管归属哪个进程,GdkDisplay都会同步这个状态。你可以给它绑定notify::focus-window信号,每次焦点窗口切换时,检查目标窗口是否属于你的Plug进程:

// 获取当前默认显示对象
GdkDisplay *display = gdk_display_get_default();
// 绑定焦点窗口变化信号,传入自定义数据
g_signal_connect(display, "notify::focus-window", G_CALLBACK(on_focus_window_changed), your_app_data);

// 回调函数实现
static void on_focus_window_changed(GdkDisplay *display, GParamSpec *pspec, gpointer data) {
    GdkWindow *focus_window = gdk_display_get_focus(display);
    if (!focus_window) return;

    YourAppData *app_data = (YourAppData*)data;
    // 检查焦点窗口的顶层窗口是否是Plug的顶层窗口
    if (gdk_window_get_toplevel(focus_window) == app_data->plug_toplevel) {
        g_print("焦点已切换到Plug窗口内的控件!\n");
    } else if (gdk_window_get_toplevel(focus_window) == app_data->parent_toplevel) {
        g_print("焦点回到父窗口控件!\n");
    }
}

关键点:需要在Plug连接到Socket后,保存它的顶层窗口指针(可以用下面提到的plug-added信号实现),方便后续判断。

2. 通过Socket的plug-added信号获取Plug窗口引用

当Plug成功连接到Socket时,Socket会触发plug-added信号,这时你可以直接获取Plug对应的GdkWindow,并缓存它的顶层窗口:

// 给Socket绑定plug-added信号
g_signal_connect(your_gtk_socket, "plug-added", G_CALLBACK(on_plug_connected), your_app_data);

static void on_plug_connected(GtkSocket *socket, gpointer data) {
    GdkWindow *plug_window = gtk_socket_get_plug_window(socket);
    if (plug_window) {
        // 缓存Plug的顶层窗口,供焦点判断使用
        ((YourAppData*)data)->plug_toplevel = gdk_window_get_toplevel(plug_window);
    }
}

3. 全局事件过滤器(可选,细粒度场景用)

如果需要追踪更细粒度的键盘焦点事件(比如具体是哪个Plug内控件获得焦点),可以添加全局事件过滤器拦截所有事件,再判断事件归属:

// 全局事件过滤器回调
GdkFilterReturn global_event_filter(GdkXEvent *xevent, GdkEvent *event, gpointer data) {
    XEvent *xev = (XEvent*)xevent;
    // 只处理焦点相关事件
    if (xev->type == FocusIn || xev->type == FocusOut) {
        GdkWindow *target_window = gdk_event_get_window(event);
        YourAppData *app_data = (YourAppData*)data;
        if (target_window && gdk_window_get_toplevel(target_window) == app_data->plug_toplevel) {
            if (xev->type == FocusIn) {
                g_print("Plug内控件获得键盘焦点!\n");
            }
        }
    }
    // 让事件继续传递,不阻塞其他逻辑
    return GDK_FILTER_CONTINUE;
}

// 在应用初始化时添加过滤器
gdk_display_add_global_event_filter(gdk_display_get_default(), global_event_filter, your_app_data);

注意:全局过滤器会拦截所有进程的事件,要注意性能开销,只在必要时使用。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 09:39:14