Intercept WM_DELETE_WINDOW on X11?

Michael Trausch picture Michael Trausch · Jul 21, 2009 · Viewed 12.5k times · Source

I'd like to intercept the WM_DELETE_WINDOW message that is posted to a certain selection of windows that an application I'm writing (AllTray), so that I can act on it instead of the application receiving it. I'm currently looking at trying this at the GDK level via gdk_display_add_client_message_filter if possible, but I'd be happy with an Xlib solution if there is one as well; it seems to be possible, but I just don't seem to be understanding how I am to do it successfully.

Currently, I have two programs (written in C) that I am trying to use to get this figured out, the first one does nothing but create a window and register that it knows about WM_DELETE_WINDOW, and the second one attempts to catch that message, but seems to fail in doing so; it appears to do precisely nothing. Am I understanding the documentation wrong on this, or is there something additional that I need to be doing (or do I need to avoid using GDK entirely for this)?

The background is this: Prior to my re-write of AllTray, the way it would do things appears to be to try to intercept a mouse-click on the X button itself. For some window managers, this worked properly, for others it didn't work at all, and for others, the user had to configure it manually and instruct AllTray where the button for closing the window was. What I am looking for is a solution that doesn't involve a LD_LIBRARY_PRELOAD and will work for any window manager/application combination that conforms to the current standards and sends a WM_DELETE_WINDOW ClientMessage when the window is closed.

UPDATE: I'm still looking for an answer. The route that I am taking at the moment is to try to reparent the window and manage it myself, but I just cannot make it work. Upon reparenting, I don't seem to be able to get it back in any way. I may be missing something very fundamental, but I can't figure out how to actually make it appear it my own window again, to bring it back on the screen.

UPDATE 2: Alright, so I've hit another brick wall. The X server documentation says to set the StructureNotifyMask on the window's event mask to receive both MapNotify and ReparentNotify events. I'd be interested in receiving either. My current thinking was to create a window that served just as an event receiver, and then when I get events for interesting things, act on them by creating and reparenting. However, this simply doesn't seem to be working. The only events I actually receive are PropertyNotify events. So, this route doesn't seem to be doing very much good, either.

Answer

Eugene Yokota picture Eugene Yokota · Jul 27, 2009

I don't know X11, but I googled using "Intercept WM_DELETE_WINDOW X11" as keywords. Found 17k - MarkMail and Mplayer-commits r154 - trunk/libvo. In both cases they are doing the same thing.

 /* This is used to intercept window closing requests.  */
 static Atom wm_delete_window;

within static void x11_init(),

XMapWindow(display, win);
wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", False);
XSetWMProtocols(display, win, &wm_delete_window, 1);

then, within static int x11_check_events(),

XEvent Event;
while (XPending(display)) {
    XNextEvent(display, &Event);
    if (Event.type == ClientMessage) {
        if ((Atom)Event.xclient.data.l[0] == wm_delete_window) {
            /* your code here */
        }
    }
}

See XInternAtom, XSetWMProtocols and XNextEvent.

After I wrote the above, I found Handling window close in an X11 app:

When a user clicks the close button [x] on our X11 application we want it to pop a a dialog asking “do you really want to quit?”. This is a plain X app. No fancy GTK or QT widgets here. So how to catch the “window is being closed” message?

The answer is to tell the Window Manager we are interested in these event by calling XSetWMProtocols and registering a WM_DELETE_WINDOW message with it. Then we’ll get a client message from the Window Manager if someone tries to close the window, and it won’t close it, it’ll leave that us up to us. Here’s an example….

// example.cpp
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <iostream>

int main()
{
   Display* display = XOpenDisplay(NULL);
   Window window = XCreateSimpleWindow(display,
                                       DefaultRootWindow(display),
                                       0, 0,
                                       500, 400,
                                       0,
                                       0, 0);

   // register interest in the delete window message
   Atom wmDeleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", False);
   XSetWMProtocols(display, window, &wmDeleteMessage, 1);

   std::cout << "Starting up..." << std::endl;
   XMapWindow(display, window);

   while (true) {
      XEvent event;
      XNextEvent(display, &event);

      if (event.type == ClientMessage &&
          event.xclient.data.l[0] == wmDeleteMessage) {
         std::cout << "Shutting down now!!!" << std::endl;
         break;
      }
   }

   XCloseDisplay(display);
   return 0;
}