如何优雅地异步停止 X11 事件循环 [英] How to stop an X11 event loop gracefully asynchronously

查看:71
本文介绍了如何优雅地异步停止 X11 事件循环的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个有两个线程的小型 X11 应用程序.在一个线程中,我正在使用 XGrabKey() 监听 X11 事件,然后在循环中使用 XNextEvent().另一个线程正在做其他事情,与 X11 无关.

I have a small X11 application which has two threads. In one thread, I am listening to X11 events using XGrabKey() and then in a loop XNextEvent(). The other thread is doing other stuff and is not related to X11.

这是相关线程的代码:

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/XF86keysym.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>

volatile bool loop = true;

void keyGrab(void)
{
    Display *display = XOpenDisplay(0);
    Window root = DefaultRootWindow(display);
    int keycode = XKeysymToKeycode(display, XF86XK_AudioPlay);

    XGrabKey(display, keycode, AnyModifier, root, False, GrabModeAsync, GrabModeAsync);
    XSelectInput(display, root, KeyPressMask);

    while (loop) {
        XEvent event;
        XNextEvent(display, &event);
        switch (event.type) {
        case KeyPress: puts("Play key pressed"); break;
        }
    }

    XUngrabKey(display, keycode, AnyModifier, root);

    XCloseDisplay(display);
}

目标是另一个线程可以告诉这个线程停止.

The goal is that the other thread can tell this thread to stop.

现在的问题是,在另一个线程中设置loop = false当然不会终止这个线程,至少不会立即终止.该线程卡在 XNextEvent() 中,因为这是一个阻塞调用.所以这是我的问题:如何让 XNextEvent() 返回的标准模式是什么?

Now the problem is that setting loop = false in the other thread will of course not terminate this thread, at least not immediately. This thread is stuck in XNextEvent() because that's a blocking call. So here's my question: What is the standard pattern how to get XNextEvent() to return?

我想我需要另一个线程来使用 XSendEvent(),但我找不到有关如何执行此操作的任何提示.我什至不知道哪种消息类型合适.会是 ClientMessage 吗?还有什么?我实际上尝试从另一个线程发送 ClientMessage,但我收到以下错误消息:

I guess I need the other Thread to use XSendEvent(), but I couldn't find any hints on how to do that. I wouldn't even know which message type would be appropriate. Would it be ClientMessage? Something else? I actually tried sending a ClientMessage from the other thread, but I got the following error message:

X Error of failed request:  BadValue (integer parameter out of range for operation)
  Major opcode of failed request:  25 (X_SendEvent)
  Value in failed request:  0x0
  Serial number of failed request:  12
  Current serial number in output stream:  12

这是我尝试并触发错误的另一个线程的相关代码片段(displayroot 由第一个线程初始化):

Here's the relevant code snippet from the other thread that I tried and triggered the error (display and root are initialized by the first thread):

XEvent event;
memset(&event, 0, sizeof(event));
event.type = ClientMessage;
XSendEvent(display, root, False,  0, &event);

请注意,另一个线程本身不需要任何 X11 代码.另一个线程使用 X11 的唯一目的是告诉这个线程终止.

Note that the other thread doesn't need any X11 code by itself. The only purpose why the other thread would use X11 is to tell this thread to terminate.

请记住,上下文中没有窗口.根窗口当然不算数,因为这仅用于全局捕获键.所以,销毁窗口不是解决方案.

Please keep in mind that there is no window in context. The root window of course does not count, as this is only for globally catching keys. So, destroying the window is not a solution.

推荐答案

根据这些页面

最好的解决方案是在 X 事件队列套接字上执行选择获取套接字是通过

The best solution would be perform a select on the X event queue socket getting the socket is achieved by

#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
        if (select(x11_fd+1, &in_fds, 0, 0, &tv))
            printf("Event Received!\n");
        else
            // Handle timer here
            printf("Timer Fired!\n");

        // Handle XEvents and flush the input 
        while(XPending(dis))
            XNextEvent(dis, &ev);
    }
    return(0);
}

这篇关于如何优雅地异步停止 X11 事件循环的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆