Hello,

I sent this patch two months ago but got no response. I send it
again just in case.
Some kind of response like, "ok, we have seen it but dont want to
merge or mess with this", would be nice.

When running cwm you can destroy the client window while
you are moving or resizing it, leading to crash of the WM
when the prop window is accessed (the one at the top-left that
indicates the position/scale).

I include a demonstration for the crash and also a patch that
fixes it. Hopefully someone with more experience here can implement
a better solution.

Demonstration code for the crash:

#include <X11/Xlib.h>

#include <err.h>
#include <unistd.h>

int
main(void)
{
        Display *dpy;
        Window win;
        int scr;
        XEvent ev;

        dpy = XOpenDisplay(NULL);
        if (!dpy)
                err(1, "XOpenDisplay");

        scr = DefaultScreen(dpy);

        win = XCreateSimpleWindow(dpy, RootWindow(dpy, scr), 0, 0, 500, 500, 0, 
0, WhitePixel(dpy, scr));
        if (!win)
                err(1, "XCreateSimpleWindow");

        XSelectInput(dpy, win, StructureNotifyMask | SubstructureNotifyMask | 
ExposureMask);
        XMapRaised(dpy, win);

        while (1) {
                XNextEvent(dpy, &ev);
                switch (ev.type) {
                case CreateNotify:
                        goto end;
                case Expose:
                        XClearWindow(dpy, win);
                        break;
                default:
                        break;
                }
        }
end:
        XDestroyWindow(dpy, win);

        XCloseDisplay(dpy);

        return 0;
}

Here is the patch:

Index: calmwm.h
===================================================================
RCS file: /cvs/xenocara/app/cwm/calmwm.h,v
retrieving revision 1.379
diff -u -p -r1.379 calmwm.h
--- calmwm.h    20 Jul 2023 14:39:34 -0000      1.379
+++ calmwm.h    6 Apr 2024 15:26:37 -0000
@@ -481,7 +481,7 @@ struct geom          screen_area(struct screen_
 struct screen_ctx      *screen_find(Window);
 void                    screen_init(int);
 void                    screen_prop_win_create(struct screen_ctx *, Window);
-void                    screen_prop_win_destroy(struct screen_ctx *);
+void                    screen_prop_win_destroy(struct screen_ctx *, int);
 void                    screen_prop_win_draw(struct screen_ctx *,
                             const char *, ...)
                            __attribute__((__format__ (printf, 2, 3)))
@@ -558,6 +558,7 @@ void                         conf_screen(struct screen_ctx 
*)
 void                    conf_group(struct screen_ctx *);
 
 void                    xev_process(void);
+void                    xev_process_ev(XEvent *);
 
 int                     xu_get_prop(Window, Atom, Atom, long, unsigned char 
**);
 int                     xu_get_strprop(Window, Atom, char **);
Index: kbfunc.c
===================================================================
RCS file: /cvs/xenocara/app/cwm/kbfunc.c,v
retrieving revision 1.174
diff -u -p -r1.174 kbfunc.c
--- kbfunc.c    20 Jul 2023 14:39:34 -0000      1.174
+++ kbfunc.c    6 Apr 2024 15:26:37 -0000
@@ -169,8 +169,8 @@ kbfunc_client_move_mb(void *ctx, struct 
 
        screen_prop_win_create(sc, cc->win);
        screen_prop_win_draw(sc, "%+5d%+5d", cc->geom.x, cc->geom.y);
-       while (move) {
-               XMaskEvent(X_Dpy, MOUSEMASK, &ev);
+       while (move > 0) {
+               XMaskEvent(X_Dpy, MOUSEMASK | SubstructureNotifyMask, &ev);
                switch (ev.type) {
                case MotionNotify:
                        /* not more than 60 times / second */
@@ -197,11 +197,27 @@ kbfunc_client_move_mb(void *ctx, struct 
                case ButtonRelease:
                        move = 0;
                        break;
+               /* check for destroy events, in case the client window
+                * gets destroyed, which forcefully closes the prop window.
+                */
+               case DestroyNotify:
+                       /* set move to -1 to specify abrupt exit */
+                       if (ev.xdestroywindow.window == cc->win) {
+                               screen_prop_win_destroy(sc, 1);
+                               move = -1; 
+                       } else if (ev.xdestroywindow.window == sc->prop.win) {
+                               screen_prop_win_destroy(sc, 0);
+                               move = -1; 
+                       }
+               default: /* process event anyway */
+                       xev_process_ev(&ev);
                }
        }
-       if (ltime)
-               client_move(cc);
-       screen_prop_win_destroy(sc);
+       if (move != -1) {
+               if (ltime)
+                       client_move(cc);
+               screen_prop_win_destroy(sc, 1);
+       }
        XUngrabPointer(X_Dpy, CurrentTime);
 }
 
@@ -258,7 +274,7 @@ kbfunc_client_resize_mb(void *ctx, struc
 
        screen_prop_win_create(sc, cc->win);
        screen_prop_win_draw(sc, "%4d x %-4d", cc->dim.w, cc->dim.h);
-       while (resize) {
+       while (resize > 0) {
                XMaskEvent(X_Dpy, MOUSEMASK, &ev);
                switch (ev.type) {
                case MotionNotify:
@@ -277,11 +293,26 @@ kbfunc_client_resize_mb(void *ctx, struc
                case ButtonRelease:
                        resize = 0;
                        break;
+               /* check for destroy events, in case the client window
+                * gets destroyed, which forcefully closes the prop window.
+                */
+               case DestroyNotify:
+                       if (ev.xdestroywindow.window == cc->win) {
+                               screen_prop_win_destroy(sc, 1);
+                               resize = -1; 
+                       } else if (ev.xdestroywindow.window == sc->prop.win) {
+                               screen_prop_win_destroy(sc, 0);
+                               resize = -1; 
+                       }
+               default: /* process event anyway */
+                       xev_process_ev(&ev);
                }
        }
-       if (ltime)
-               client_resize(cc, 1);
-       screen_prop_win_destroy(sc);
+       if (resize != -1) {
+               if (ltime)
+                       client_resize(cc, 1);
+               screen_prop_win_destroy(sc, 1);
+       }
        XUngrabPointer(X_Dpy, CurrentTime);
 
        /* Make sure the pointer stays within the window. */
Index: screen.c
===================================================================
RCS file: /cvs/xenocara/app/cwm/screen.c,v
retrieving revision 1.98
diff -u -p -r1.98 screen.c
--- screen.c    27 Jan 2022 18:45:10 -0000      1.98
+++ screen.c    6 Apr 2024 15:26:37 -0000
@@ -278,10 +278,15 @@ screen_prop_win_create(struct screen_ctx
 }
 
 void
-screen_prop_win_destroy(struct screen_ctx *sc)
+screen_prop_win_destroy(struct screen_ctx *sc, int destroy_window)
 {
        XftDrawDestroy(sc->prop.xftdraw);
-       XDestroyWindow(X_Dpy, sc->prop.win);
+       /*
+        * In case the window was already destroyed
+        * (i.e. DestroyNotify event)
+        */
+       if (destroy_window)
+               XDestroyWindow(X_Dpy, sc->prop.win);
 }
 
 void
Index: xevents.c
===================================================================
RCS file: /cvs/xenocara/app/cwm/xevents.c,v
retrieving revision 1.150
diff -u -p -r1.150 xevents.c
--- xevents.c   24 Mar 2020 14:47:29 -0000      1.150
+++ xevents.c   6 Apr 2024 15:26:37 -0000
@@ -485,9 +485,15 @@ xev_process(void)
 
        while (XPending(X_Dpy)) {
                XNextEvent(X_Dpy, &e);
-               if ((e.type - Conf.xrandr_event_base) == RRScreenChangeNotify)
-                       xev_handle_randr(&e);
-               else if ((e.type < LASTEvent) && (xev_handlers[e.type] != NULL))
-                       (*xev_handlers[e.type])(&e);
+               xev_process_ev(&e);
        }
+}
+
+void
+xev_process_ev(XEvent *ev)
+{
+       if ((ev->type - Conf.xrandr_event_base) == RRScreenChangeNotify)
+               xev_handle_randr(ev);
+       else if ((ev->type < LASTEvent) && (xev_handlers[ev->type] != NULL))
+               (*xev_handlers[ev->type])(ev);
 }

Reply via email to