从 X11 应用程序干净退出?

标签 c linux x11

这是一个使用 X11 打开和关闭窗口的简约 Linux 应用程序。当用户在终端中按下 ctrl-c 或单击 Windows 管理器上的“关闭”按钮时,我想要一个干净的退出(根据 valgrind)。找到这些信息花了一段时间,所以我想我会发布工作代码并提出一些问题。我使用信号来捕获“ctrl-c”,并使用 Atom 来捕获关闭按钮的点击。

问题:
1) 这是从 X11 应用程序完成干净退出的正确/最佳方法吗?
2) 我可以在信号处理程序代码中发送事件吗?有人说不...
3) 除了 Atoms 之外,还有其他捕获窗口管理器事件的方法吗?

// 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;
}

最佳答案

首先,您的信号处理程序有问题。 仔细阅读 signal(7) (特别是关于异步信号安全函数的部分)& POSIX signal.h文档。实际上,信号处理程序应该设置一些 volatile sig_atomic_t 标志以在其他地方进行测试,因此将 done 字段声明为 volatile sig_atomic_t done; 并且您的信号处理程序只是设置该标志,什么都不做(特别是,将 XSendEvent 移到信号处理程序之外)。您也可以从 Qt recommendations about Unix signals 中得到启发(例如,使用一个 pipe 给自己,从信号处理程序中 write ,并在 poll 周围有一个事件循环,它可以 read 该管道和/或 X11 套接字)。但是信号处理程序不能调用(以可靠的方式)XSendEvent,这不是异步信号安全的。另见 syscalls(2)Advanced Linux Programming .

此外,您想关注 EWMH约定详解here .这非常复杂和乏味,实际上足以证明使用一些现有的 X11 toolkit喜欢QtGTK (或者可能是其他东西,比如 libsdllibsfmlfox-toolkitfltk ……)。顺便说一句,这些工具包(至少是 Qt 和 GTK)可能正在过渡到 Wayland .值得一提的是,X11 应用程序正在成为遗留问题,并且很难正确编码(没有一些尊重 X11 约定的工具包的支持,例如 EWMH)。

顺便说一句,如今,很少有应用程序使用 X11 核心协议(protocol)及其绘图原语(例如 XDrawLineXDrawArcXDrawText、....)。大多数工具包都在客户端像素图中绘制(这是 Wayland 也在使用的模型)。

在没有工具包的情况下编写 X11 应用程序(符合 EwMH)是非常困难的(将花费您数年;到您完成时,X11 可能已被 Wayland 取代)。

关于从 X11 应用程序干净退出?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36014153/

相关文章:

c - 检测影子密码文件是否正在使用的最佳方法

c++ - 在基于 Linux 的 WAGO PFC200 PLC 上与 CoDeSys 程序通信

linux - 是否有所有 XFixes 游标类型的列表?

linux - 在 Xvfb 服务器中获取窗口列表

c - 头文件的排除是否是语法错误的一部分?

c - 尝试从命令行获取星号 * 作为对 main 的输入

linux - 用适当数量的 "*"替换偶数词

c - 将这个简单的 C 代码转换为 VBA

c++ - 将多态 C++ 对象转换为非多态 C 对象

c++ - X11 资源释放(特别是 Screen 对象)