【问题标题】:how to quit the blocking of xlib's XNextEvent如何退出对 xlib 的 XNextEvent 的阻塞
【发布时间】:2012-01-25 09:51:37
【问题描述】:

windows下,GUI线程通常调用GetMessage来等待消息, 当另一个线程使用 PoseMessage 将消息放入队列时,则 GUI 线程将返回 GetMessage(退出阻塞)。

有谁能告诉我,当我在 XWindows 下使用 XNextEvent 等待 事件,我怎样才能“唤醒”另一个线程中的 GUI 线程。有没有一些 我可以使用像 PoseMessage 这样的 API 吗?

【问题讨论】:

    标签: c user-interface xlib x11


    【解决方案1】:

    没有。这就是大多数 UI 框架(Gtk、KDE ​​等)使用自定义主循环来监听更多事件源的原因。

    在内部,XNextEvent 使用一个套接字,因此它调用select() 以了解输入何时可用。调用ConnectionNumber(display)获取需要传递的文件描述符select()

    这允许您监听多个文件描述符。

    来自http://www.linuxquestions.org/questions/showthread.php?p=2431345#post2431345的示例代码

    #include <stdio.h>
    #include <stdlib.h>
    #include <X11/Xlib.h>
    #include <X11/Xutil.h>
    
    Display *dis;
    Window win;
    int x11_fd;
    fd_set in_fds;
    
    struct timeval tv;
    XEvent ev;
    
    int main() {
        dis = XOpenDisplay(NULL);
        win = XCreateSimpleWindow(dis, RootWindow(dis, 0), 1, 1, 256, 256, \
            0, BlackPixel (dis, 0), BlackPixel(dis, 0));
    
        // You don't need all of these. Make the mask as you normally would.
        XSelectInput(dis, win, 
            ExposureMask | KeyPressMask | KeyReleaseMask | PointerMotionMask |
            ButtonPressMask | ButtonReleaseMask  | StructureNotifyMask 
            );
    
        XMapWindow(dis, win);
        XFlush(dis);
    
        // This returns the FD of the X11 display (or something like that)
        x11_fd = ConnectionNumber(dis);
    
        // Main loop
        while(1) {
            // Create a File Description Set containing x11_fd
            FD_ZERO(&in_fds);
            FD_SET(x11_fd, &in_fds);
    
            // Set our timer.  One second sounds good.
            tv.tv_usec = 0;
            tv.tv_sec = 1;
    
            // Wait for X Event or a Timer
            int num_ready_fds = select(x11_fd + 1, &in_fds, NULL, NULL, &tv);
            if (num_ready_fds > 0)
                printf("Event Received!\n");
            else if (num_ready_fds == 0)
                // Handle timer here
                printf("Timer Fired!\n");
            else
                printf("An error occured!\n");
    
            // Handle XEvents and flush the input 
            while(XPending(dis))
                XNextEvent(dis, &ev);
        }
        return(0);
    }
    

    【讨论】:

    • 嗨亚伦,这是一个很棒的解决方案。这只是在该窗口中捕获事件,是否可以全局监听鼠标事件?并且可以用这种方法来阻止鼠标事件吗?
    • @Noitidart:也许可以,但我无法在评论中回答这个问题。请提出一个新问题,并且不要忘记提供更多详细信息,您究竟需要实现什么。
    • 啊,非常感谢我在这里发布了问题:stackoverflow.com/questions/32262767/mouse-events-callback
    • select() 的第一个参数应该是 x11_fd + 1,而不是 1。从 man 2 select: > nfds 是三组中编号最高的文件描述符,加 1。
    • 首选pselect()poll() 而不是select(),因为pselect() 具有更好的信号处理能力,并且可以避免select() 可能发生的信号竞争条件。
    【解决方案2】:

    您可以通过向自己发送一个虚拟事件来退出阻塞的 XNextEvent。

    Window interClientCommunicationWindow;
    Bool x11EventLoopActive = True;
    
    // create a dummy window, that we can use to end the blocking XNextEvent call
    interClientCommunicationWindow = XCreateSimpleWindow(dpy, root, 10, 10, 10, 10, 0, 0, 0);
    XSelectInput(dpy, interClientCommunicationWindow, StructureNotifyMask);
    
    XEvent event;
    while(x11EventLoopActive) {
      XNextEvent(dpy, &event);
      ...
    }
    

    在另一个线程中你可以这样做来结束循环:

    x11EventLoopActive = False;
    // push a dummy event into the queue so that the event loop has a chance to stop
    XClientMessageEvent dummyEvent;
    memset(&dummyEvent, 0, sizeof(XClientMessageEvent));
    dummyEvent.type = ClientMessage;
    dummyEvent.window = interClientCommunicationWindow;
    dummyEvent.format = 32;
    XSendEvent(dpy, interClientCommunicationWindow, 0, 0, (XEvent*)&dummyEvent);
    XFlush(dpy);
    

    【讨论】:

    • 危险,威尔罗宾逊! Xlib 不是线程安全的!上面的代码看起来可以正常工作,但实际上会以非常难以调试的方式随机且不频繁地崩溃。不要这样做!
    • 这将使它成为线程安全的,但它仍然会有竞争条件。
    • 是的,我尝试在中断(信号处理程序)上填充队列。我可以保证它有间歇性故障。
    【解决方案3】:

    你应该使用: Bool XCheckMaskEvent(Display*, long, XEvent)

    XCheckMaskEvent 函数首先搜索事件队列,然后在服务器连接上的所有可用事件中查找与指定掩码匹配的第一个事件。

    如果找到匹配项,XCheckMaskEvent 将删除该事件,将其复制到指定的 XEvent 结构中,并返回 True。队列中存储的其他事件不会被丢弃。

    如果您请求的事件不可用,XCheckMaskEvent 返回False,并且输出缓冲区将被刷新。

    【讨论】:

    • 我无法让它工作。我在等待 PointerBarrier 事件,但不确定哪种掩码合适?起作用的是使用 if (XPending(_display) > 0);然后是 XNextEvent;否则继续;
    • 感谢您的回答。 @kevinf 这对我有用: while(XCheckMaskEvent(display -1, &event)) { /*do things*/ } 一些实现掩码是无符号的,所以你可能需要强制转换。在 Ubuntu 18 上,掩码是 32 位签名的,因此只需传递 -1 即可覆盖任何标志。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-15
    • 2016-12-20
    • 2016-12-29
    • 2015-06-26
    • 2015-10-23
    相关资源
    最近更新 更多