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
相关产品推荐
相关产品推荐

