Xlib: Closing window always causes fatal IO error?
Asked Answered
G

1

7

I'm not sure why this happens, but any window I create using Xlib in C++ gives outputs an error to the terminal when I try to close is using the X button. I can close it programmatically with no errors, it's just the X button that does it.

The error is the following:

XIO:  fatal IO error 11 (Resource temporarily unavailable) on X server ":0"
after 483 requests (483 known processed) with 0 events remaining.

The number of requests is different every time, but there's always 0 events remaining. Why does this happen? The cause doesn't seem to be my code, since it does this no matter what and sends no close events to the queue. I've tried intercepting the Atom WM_WINDOW_DELETE, and it doesn't run over the expected code when I close the window.

Edit: Added event loop code.

while(XPending(display)) {
    XNextEvent(display, &event);

    pthread_mutex_unlock(&mutex);

    if(event.type == Expose) {
        XWindowAttributes getWindowAttributes;

        pthread_mutex_lock(&mutex);

        XGetWindowAttributes(display, window, &getWindowAttributes);

        if(state.currentState == STATE_NORMAL) {
            state.normX = getWindowAttributes.x;
            state.normY = getWindowAttributes.y;
            state.normWidth = getWindowAttributes.width;
            state.normHeight = getWindowAttributes.height;
        }

        pthread_mutex_unlock(&mutex);

        glViewport(0, 0, getWindowAttributes.width, getWindowAttributes.height);
    } else if(event.type == KeyPress) {
        return false;
    } else if(event.type == ClientMessage) {
        std::cout<<"X Button pressed"<<std::endl; //Never run when X-ing window
        if(event.xclient.message_type == XInternAtom(display, "WM_DELETE_WINDOW", True)) {
            return false;
        }
    } else if(event.type == ButtonPress) {
        if(state.currentState != STATE_FULLSCREEN) {
            fullscreen();
        } else {
            normalize();
        }
    } else if(!handleEvent(event)){
        return false;
    }

    pthread_mutex_lock(&mutex);
}
Greaseball answered 10/10, 2013 at 20:51 Comment(4)
Any window, even popups or transients?Plectrum
In addition to WM_WINDOW_DELETE you need to listen for and handle the ClientMessage event. Show your code wrt. WM_WINDOW_DELETE.Tenterhook
Added event loop code - I'm already looking for ClientMessage. It's the act of closing the window that closes the error, so no message is ever received.Greaseball
I've only used normal XWindows so far, so maybe not popups or transients.Greaseball
T
4

In addition to WM_WINDOW_DELETE you need to listen for and handle the ClientMessage event.

Modifying the example from Rosetta Code to illustrate:

#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void) {
    Display *d;
    Window w;
    XEvent e;
    const char *msg = "Hello, World!";
    int s;

    d = XOpenDisplay(NULL);
    if (d == NULL) {
        fprintf(stderr, "Cannot open display\n");
        exit(1);
    }

    s = DefaultScreen(d);
    w = XCreateSimpleWindow(d, RootWindow(d, s), 10, 10, 100, 100, 1, BlackPixel(d, s), WhitePixel(d, s));
    XSelectInput(d, w, ExposureMask | KeyPressMask);
    XMapWindow(d, w);

    // I support the WM_DELETE_WINDOW protocol
    Atom WM_DELETE_WINDOW = XInternAtom(d, "WM_DELETE_WINDOW", False); 
    XSetWMProtocols(d, w, &WM_DELETE_WINDOW, 1);

    while (1) {
        XNextEvent(d, &e);
        if (e.type == Expose) {
            XFillRectangle(d, w, DefaultGC(d, s), 20, 20, 10, 10);
            XDrawString(d, w, DefaultGC(d, s), 10, 50, msg, strlen(msg));
        }
        else if (e.type == KeyPress)
            break;
        else if (e.type == ClientMessage)
            // TODO Should check here for other client message types - 
            // however as the only protocol registered above is WM_DELETE_WINDOW
            // it is safe for this small example.
            break;
    }

    XCloseDisplay(d);
    return 0;
}
Tenterhook answered 11/10, 2013 at 0:11 Comment(6)
I added SetWMProtocols to my code, it seems that's what was missing. Why do you have to do this by default? Weird...Greaseball
Also I noticed std::cout isn't behaving like I'd expect it to. Sometimes it works correctly, other times it just waits until the program is closed to output to the terminal or waits for a random period of time. Does this have something to do with Xlib?Greaseball
SetWMProtocols is required because without some explicit notification the window manager cannot make assumptions about the capabilities of any particular client. Just requesting the Atom doesn't actually do anything. Alone an atom is just a handle to a resource.Tenterhook
std::cout is a buffered stream. std::cerr acts as an unbuffered stream. You could also continue to use std::cout and call std::flush when you want to flush the output stream.Tenterhook
@mythagel: Just a quick question: what is Atom?Hydrography
@Hydrography Briefly, an Atom is an integer value that represents a string stored in the X11 server.Tenterhook

© 2022 - 2024 — McMap. All rights reserved.