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

时间:2015-03-12 03:01:45

标签: c multithreading x11

我有一个小的X11应用程序,它有两个线程。 在一个线程中,我正在使用XGrabKey()收听X11事件,然后在循环XNextEvent()中收听。另一个线程正在做其他事情并且与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);
}

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

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

我想我需要另一个线程来使用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的唯一目的是告诉该线程终止。

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

2 个答案:

答案 0 :(得分:3)

根据这些页面

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

实现了套接字
#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);
}

答案 1 :(得分:2)

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