Hello,
I am new to the mailing lists, so please forgive me if something
is not correct. I sent this in the misc list and they pointed
me that it was ok to send this inside this list.
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 7 Feb 2024 20:20:41 -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 7 Feb 2024 20:20:41 -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,28 @@ 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;
+ }
+ xev_process_ev(&ev);
+ 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 +275,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 +294,27 @@ 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);
+ break;
}
}
- 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 7 Feb 2024 20:20:41 -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 7 Feb 2024 20:20:41 -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);
}