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);
}
NmdMystery
  • 2,778
  • 3
  • 32
  • 60

1 Answers1

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;
}
mythagel
  • 1,789
  • 19
  • 15
  • I added SetWMProtocols to my code, it seems that's what was missing. Why do you have to do this by default? Weird... – NmdMystery Oct 11 '13 at 03:49
  • 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? – NmdMystery Oct 11 '13 at 03:50
  • `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. – mythagel Oct 11 '13 at 04:39
  • 1
    `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. – mythagel Oct 11 '13 at 04:44
  • @mythagel: Just a quick question: what is `Atom`? – SasQ Mar 24 '14 at 13:28
  • @SasQ Briefly, an `Atom` is an integer value that represents a string stored in the X11 server. – mythagel Mar 26 '14 at 03:27