如何优雅地异步停止 X11 事件循环
How to stop an X11 event loop gracefully asynchronously
我有一个有两个线程的小型 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()
到 return 的标准模式是什么?
我想我需要另一个线程才能使用 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 的唯一目的是告诉该线程终止。
请记住上下文中没有 window。根 window 当然不算数,因为这仅用于全局捕获密钥。所以,摧毁window不是解决办法。
在你的消息循环中使用 XCheckWindowEvent
来查看是否有任何消息(如果存在则跟随 XNextEvent),因为这是非阻塞的你可以继续使用 pthread_cond_timedwait
或其他您正在使用的线程库中可能存在等效项。这样阻塞就在你手中而不是 xlib 的手中。如果超时,它将检查另一个事件,然后继续等待您的线程。
根据这些页面
- how to quit the blocking of xlib's XNextEvent
- http://www.linuxquestions.org/questions/programming-9/xnextevent-select-409355/#post2431460
最好的解决方案是在 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);
}
我有一个有两个线程的小型 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()
到 return 的标准模式是什么?
我想我需要另一个线程才能使用 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 的唯一目的是告诉该线程终止。
请记住上下文中没有 window。根 window 当然不算数,因为这仅用于全局捕获密钥。所以,摧毁window不是解决办法。
在你的消息循环中使用 XCheckWindowEvent
来查看是否有任何消息(如果存在则跟随 XNextEvent),因为这是非阻塞的你可以继续使用 pthread_cond_timedwait
或其他您正在使用的线程库中可能存在等效项。这样阻塞就在你手中而不是 xlib 的手中。如果超时,它将检查另一个事件,然后继续等待您的线程。
根据这些页面
- how to quit the blocking of xlib's XNextEvent
- http://www.linuxquestions.org/questions/programming-9/xnextevent-select-409355/#post2431460
最好的解决方案是在 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);
}