这是一个使用X11启动和关闭窗口的简约linux应用程序。当用户在终端中按ctrl-c或点击"关闭"时,我想要一个干净的退出(按照valgrind)。 Windows管理器上的按钮。我花了一段时间才找到这些信息,所以我想我会发布工作代码并提出几个问题。我使用信号来捕捉" ctrl-c"并使用Atom捕获关闭按钮单击。
问题:
1)这是完成X11应用程序干净退出的正确/最佳方式吗?
2)我是否允许在信号处理程序代码中发送事件?有人说没有...
3)除了Atoms之外,还有其他方法可以捕获Windows管理器事件吗?
// use "gcc main.c -lX11" to compile
#include <string.h>
#include <signal.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
// global data
struct {
Display* display;
Window* window;
char done;
} global;
void SignalHandler(int n) {
switch(n) {
case SIGINT: // user pressed ctrl-c from terminal
global.done = 1; // tell event loop to exit
XClientMessageEvent event; // dummy event to wake up XNextEvent
memset(&event, 0, sizeof(XClientMessageEvent));
event.type = ClientMessage;
event.format = 32; // not used but cannot be zero
XSendEvent(global.display, *global.window, 0, 0, (XEvent*)&event);
XFlush(global.display); // make event happen immediately
}
}
void RegisterSignals() { signal(SIGINT, SignalHandler); }
int main () {
memset(&global, 0, sizeof(global));
RegisterSignals();
Display* display = XOpenDisplay(NULL);
global.display = display;
Visual* visual = DefaultVisual(display, 0);
int depth = DefaultDepth(display, 0);
XSetWindowAttributes frame_attr;
frame_attr.background_pixel = XWhitePixel(display, 0);
Window window = XCreateWindow(display, XRootWindow(display, 0),
0, 0, 400, 300, 5, depth, InputOutput, visual, CWBackPixel, &frame_attr);
global.window = &window;
XStoreName(display, window, "Title");
XSelectInput(display, window, 0xFFFF);
XFontStruct* font = XLoadQueryFont(display, "10x20");
XGCValues gc_values;
gc_values.font = font->fid;
gc_values.foreground = XBlackPixel(display, 0);
GC gc = XCreateGC(display, window, GCFont + GCForeground, &gc_values);
// Windows Manager Stuff (like clicking close button)
Atom wmDeleteWindow = XInternAtom(display, "WM_DELETE_WINDOW", True);
XSetWMProtocols(display, window, &wmDeleteWindow, 1);
XMapWindow(display, window);
XEvent event;
while (!global.done) {
XNextEvent(display, (XEvent *)&event); // blocks until event
switch (event.type) {
// other events go here...
case ClientMessage:
// user clicked close button on window
if (event.xclient.data.l[0] == wmDeleteWindow) { global.done = 1; }
}
}
// Cleanup
XFreeGC(display, gc);
XFreeFont(display, font);
XCloseDisplay(display);
return 0;
}
答案 0 :(得分:3)
首先,你的信号处理程序是错误的。仔细阅读 signal(7)(尤其是关于异步信号安全功能的部分)&amp; POSIX signal.h文档。在实践中,信号处理程序应该设置一些class myViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, InstatunesAppDelegate {
标志以在其他地方进行测试,因此将volatile sig_atomic_t
字段声明为done
并让您的信号处理程序只设置该标志而不再执行任何操作(在特别是,将volatile sig_atomic_t done;
移到信号处理程序之外。你也可能受到Qt recommendations about Unix signals的启发(例如,从信号处理程序中使用XSendEvent
自我,pipe
,并在write
周围有一个事件循环poll
read
1}}那个管道和/或X11插座)。但是信号处理程序无法调用(以可靠的方式)XSendEvent
不异步信号安全。另请参阅syscalls(2)和Advanced Linux Programming。
此外,您还要遵循详细EWMH详细here约定。这很复杂,很无聊,几乎可以证明使用某些现有的 X11 toolkit,例如Qt或GTK(或者其他类似libsdl, libsfml,fox-toolkit,fltk,...)。顺便说一句,这些工具包(至少Qt&amp; GTK)可能已转换为Wayland。为了具有挑衅性,X11应用程序正在成为传统,并且很难正确编码(没有支持某些工具包,尊重X11惯例,如EWMH)。
BTW,如今,很少有应用程序使用X11核心协议及其绘图原语(例如XDrawLine,XDrawArc,XDrawText,....)。大多数工具包都在客户端pixmaps中绘制客户端(这是Wayland也在使用的模型)。编写一个没有工具包的X11应用程序(符合EwMH)非常困难(需要几年时间;当你完成X11时,可能会被Wayland取代)。
答案 1 :(得分:1)
3)是否有其他方法可以捕获Windows管理器事件 除了Atoms?
没有。原子用于“注册”这些事件(在这种情况下只是字符串)并为它们分配一个数字。这可以防止大量硬编码,并使系统非常灵活。
答案 2 :(得分:0)
版本2.效果很好......感谢Basil和datenwolf的建议。任何评论都会很棒。
// Minimal X11 window with clean exit
// 1) "ctrl-c" from terminal or "kill -s SIGINT" exits app
// 2) user clicks close button to exit app
// Use -lX11 to comile with gcc
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
struct {
int pip[2]; // extra pipe for event loop
sig_atomic_t done; // flag to stop the event loop
} global;
void SignalHandler(int n) {
write(global.pip[1], &global.done, 1); // wake up select in X11 event loop
switch(n) {
case SIGINT: // user pressed ctrl-c from terminal (or kill -s SIGINT)
global.done = 1; // tell event loop to exit
}
}
void Initialize() {
struct sigaction sigact; // no race conditions like signal()
memset(&sigact, 0, sizeof(sigact));
sigact.sa_handler = &SignalHandler;
sigaction(SIGINT, &sigact, NULL);
memset(&global, 0, sizeof(global));
pipe(global.pip);
write(global.pip[1], &global.done, 1); // wake up select on first iteration of X11 event loop
}
int main () {
Initialize();
Display* display = XOpenDisplay(NULL);
int xfd = ConnectionNumber(display);
Visual* visual = DefaultVisual(display, 0);
int depth = DefaultDepth(display, 0);
XSetWindowAttributes frame_attr;
frame_attr.background_pixel = XWhitePixel(display, 0);
Window window = XCreateWindow(display, XRootWindow(display, 0),
0, 0, 400, 300, 5, depth, InputOutput, visual, CWBackPixel, &frame_attr);
XStoreName(display, window, "Title");
XSelectInput(display, window, 0xFFFF);
XFontStruct* font = XLoadQueryFont(display, "10x20");
XGCValues gc_values;
gc_values.font = font->fid;
gc_values.foreground = XBlackPixel(display, 0);
GC gc = XCreateGC(display, window, GCFont + GCForeground, &gc_values);
// Windows Manager Stuff (like clicking close button)
Atom wmDeleteWindow = XInternAtom(display, "WM_DELETE_WINDOW", True);
XSetWMProtocols(display, window, &wmDeleteWindow, 1);
XMapWindow(display, window);
fd_set set_read, set_save;
FD_ZERO(&set_save); // select modifies fd_set, re-initialize
FD_SET(xfd, &set_save);
FD_SET(global.pip[0], &set_save);
int max_fd = xfd > global.pip[0] ? xfd : global.pip[0];
XEvent event;
while (!global.done) {
//printf("Blocking on select...\n");
memcpy(&set_read, &set_save, sizeof(set_save));
select(max_fd+1, &set_read, NULL, NULL, NULL); // block on X11 and pipe
if(!XPending(display)) {
// this code only executes once on startup and once on exit
char a[1]; read(global.pip[0], a, 1); continue;
}
XNextEvent(display, (XEvent *)&event); // doesn't block because we ensure an event
//printf("Processing event...\n");
switch ( event.type ) {
// don't forget your other events...
case ClientMessage:
// user clicked close button on window
if (event.xclient.data.l[0] == wmDeleteWindow) { global.done = 1; }
}
}
//printf("Cleaning up...\n");
close(global.pip[0]);
close(global.pip[1]);
XFreeGC(display, gc);
XFreeFont(display, font);
XCloseDisplay(display);
return 0;
}
答案 3 :(得分:0)
这是在ctrl-c
或更高版本中与事件循环结合正常处理c++11
的一种方法:
...
volatile sig_atomic_t running = true;
void stop(int x)
{
running = false;
// Output a newline after the "^C" output
std::cout << std::endl;
}
...
XEvent e;
signal(SIGINT, stop);
while (running) {
// This check exists so that event-checking can be non-blocking if the program is
// interrupted by SIGINT
if (XPending(display) == 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(50));
continue;
}
XNextEvent(display, &e);
switch (e.type) {
...
XPending
用于在处理事件之前检查是否有排队的事件。