You need to enable JavaScript to run this app. X11如何监听窗口移动过程中的实时位置变化事件
优惠活动
大模型
产品
解决方案
定价
更多

X11如何监听窗口移动过程中的实时位置变化事件

问题根因

你当前代码只能收到移动结束后的事件,原因是:默认情况下窗口由系统窗口管理器(WM)托管,用户拖动标题栏移动窗口时,WM会独占鼠标事件,仅在拖动结束后才给应用推送一次ConfigureNotify事件,拖动过程中不会主动推送位置变化通知。


解决方案1:自定义窗口拖动逻辑(最稳定,无兼容性问题)

不依赖WM的标题栏拖动,自己监听鼠标事件实现窗口移动,全程可以拿到实时位置,修改后代码如下:

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

int main(int argc, char **argv)
{
    Display *dpy = XOpenDisplay(NULL);
    Window root = RootWindow(dpy, DefaultScreen(dpy));
    Window w = XCreateSimpleWindow(dpy, root, 10, 10, 800, 400, 1, BlackPixel(dpy, DefaultScreen(dpy)), WhitePixel(dpy, DefaultScreen(dpy)));
    // 新增鼠标按下、鼠标移动事件掩码
    XSelectInput(dpy, w, StructureNotifyMask | ButtonPressMask | PointerMotionMask);
    XMapWindow(dpy, w);

    XEvent event;
    int is_dragging = 0;
    int drag_start_x, drag_start_y; // 鼠标按下时的相对窗口坐标
    int win_start_x, win_start_y;   // 鼠标按下时的窗口左上角坐标

    for (;;)
    {
        XNextEvent(dpy, &event);

        if(event.type == ConfigureNotify)
        {
            XConfigureEvent x = event.xconfigure;
            printf("实时位置: {%d,%d} 大小: {%d,%d}\n", x.x, x.y, x.width, x.height);
        }
        // 处理鼠标按下,开始拖动
        else if(event.type == ButtonPress)
        {
            is_dragging = 1;
            drag_start_x = event.xbutton.x;
            drag_start_y = event.xbutton.y;
            XWindowAttributes attr;
            XGetWindowAttributes(dpy, w, &attr);
            win_start_x = attr.x;
            win_start_y = attr.y;
        }
        // 处理鼠标释放,结束拖动
        else if(event.type == ButtonRelease)
        {
            is_dragging = 0;
        }
        // 处理鼠标移动,实时更新窗口位置
        else if(event.type == MotionNotify && is_dragging)
        {
            int new_x = event.xmotion.x_root - drag_start_x;
            int new_y = event.xmotion.y_root - drag_start_y;
            XMoveWindow(dpy, w, new_x, new_y);
        }
    }

    XDestroyWindow(dpy, w);
    XCloseDisplay(dpy);
    return 0;
}

注意:这个方案默认点击窗口任意区域即可拖动,如果仅需要自定义标题栏拖动,只需要在ButtonPress事件里判断点击坐标是否在你自己绘制的标题栏范围内即可。


解决方案2:保留系统标题栏,轮询获取实时位置

如果你不想修改拖动逻辑,要保留系统WM的标题栏交互,可以通过定时查询窗口属性的方式拿到实时位置,把事件循环修改为非阻塞模式即可,示例逻辑如下:

#include <sys/select.h>
// 其余头文件和初始化逻辑不变,仅修改事件循环
struct timeval tv;
for (;;)
{
    // 有未处理事件就优先处理
    if (XPending(dpy)) {
        XNextEvent(dpy, &event);
        if(event.type == ConfigureNotify)
        {
            XConfigureEvent x = event.xconfigure;
            printf("位置更新: {%d,%d,%d,%d}\n", x.x, x.y, x.width, x.height);
        }
        // 其余事件处理逻辑
    } else {
        // 无事件时主动查询窗口坐标
        XWindowAttributes attr;
        XGetWindowAttributes(dpy, w, &attr);
        printf("实时位置: {%d,%d}\n", attr.x, attr.y);
        // 休眠16ms,保证60Hz更新频率,降低CPU占用
        tv.tv_sec = 0;
        tv.tv_usec = 16000;
        select(0, NULL, NULL, NULL, &tv);
    }
}

注意:轮询间隔可以根据你的精度需求调整,16ms的间隔CPU占用可以忽略。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.16 06:15:04