我有一个小的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
以下是我尝试过并触发错误的其他线程的相关代码段(display
和root
由第一个线程初始化):
XEvent event;
memset(&event, 0, sizeof(event));
event.type = ClientMessage;
XSendEvent(display, root, False, 0, &event);
请注意,其他线程本身并不需要任何X11代码。另一个线程使用X11的唯一目的是告诉该线程终止。
请记住,上下文中没有窗口。根窗口当然不计,因为这仅适用于全局捕获键。因此,破坏窗口不是解决方案。
答案 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。如果它超时,它将检查另一个事件,然后继续等待你的线程。