如何异步地异步停止X11事件循环

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

这是相关线程的代码:

 #include  #include  #include  #include  #include  #include  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); } 

目标是另一个线程可以告诉该线程停止。

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

我想我需要另一个Thread来使用XSendEvent() ,但我找不到任何关于如何做到这一点的提示。 我甚至不知道哪种消息类型是合适的。 是ClientMessage吗? 别的什么? 我实际上尝试从另一个线程发送ClientMessage ,但我收到以下错误消息:

 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由第一个线程初始化):

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

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

请记住,上下文中没有窗口。 根窗口当然不计,因为这仅适用于全局捕获键。 因此,破坏窗口不是解决方案。

根据这些页面

最好的解决方案是在X事件队列套接字上执行select,以获得套接字

 #include  #include  #include  #include  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); } 

在消息循环中使用XCheckWindowEvent来查看是否有任何消息(如果存在,则后跟XNextEvent),并且由于这是非阻塞的,因此您可以继续使用pthread_cond_timedwait或您正在使用的线程库中可能存在的任何等效项。 这样阻塞就在你手中,而不是xlib。 如果它超时,它将检查另一个事件,然后继续等待你的线程。