code: drawterm

Download patch

ref: 3b85c767fec3d7c9852f2e86afc89554bfabd02d
parent: 76ddc13f7d22c5ba35c56b9073ec0ca4c85446f0
author: mischief <mischief@offblast.org>
date: Mon Nov 9 13:30:08 EST 2020

gui-x11: handle WM_DELETE_WINDOW messages

previously drawterm did not handle the ICCCM[1] protocol for graceful window
closure. most if not all window managers will send this message, but it is
implemented inconsistently. some will forcibly terminate the X11 client's
connection if it doesn't handle it, and others will just give up. some prompt
for forcible termination. in any case, now we handle it.

to make this change work, we also setup the X11 window in the xkmcon
connection, because otherwise it seems not possible to receive the
ClientMessage in our event handler. without this the event would instead be
sent to the xdisplay connection, which we did not monitor for events.

[1] https://www.x.org/releases/X11R7.6/doc/xorg-docs/specs/ICCCM/icccm.html#window_deletion

--- a/gui-x11/x11.c
+++ b/gui-x11/x11.c
@@ -120,6 +120,7 @@
 static Atom text;
 static Atom compoundtext;
 static Atom wmpid;
+static Atom wmdelete;
 
 static	Drawable	xdrawable;
 static	void		xexpose(XEvent*);
@@ -319,11 +320,11 @@
 	attrs.background_pixel = 0;
 	attrs.border_pixel = 0;
 	/* attrs.override_redirect = 1;*/ /* WM leave me alone! |CWOverrideRedirect */
-	xdrawable = XCreateWindow(xdisplay, rootwin, x, y, Dx(r), Dy(r), 0,
+	xdrawable = XCreateWindow(xkmcon, rootwin, x, y, Dx(r), Dy(r), 0,
 		xscreendepth, InputOutput, xvis, CWBackPixel|CWBorderPixel|CWColormap, &attrs);
 
 	/* load the given bitmap data and create an X pixmap containing it. */
-	icon_pixmap = XCreateBitmapFromData(xdisplay,
+	icon_pixmap = XCreateBitmapFromData(xkmcon,
 		rootwin, (char *)glenda_t_bits,
 		glenda_t_width, glenda_t_height);
 
@@ -348,7 +349,7 @@
 		classhints.res_class = "Drawterm";
 	argv[0] = "drawterm";
 	argv[1] = nil;
-	XSetWMProperties(xdisplay, xdrawable,
+	XSetWMProperties(xkmcon, xdrawable,
 		&name,			/* XA_WM_NAME property for ICCCM */
 		&name,			/* XA_WM_ICON_NAME */
 		argv,			/* XA_WM_COMMAND */
@@ -356,10 +357,10 @@
 		&normalhints,		/* XA_WM_NORMAL_HINTS */
 		&hints,			/* XA_WM_HINTS */
 		&classhints);		/* XA_WM_CLASS */
-	XFlush(xdisplay);
+	XFlush(xkmcon);
 	if ((wmpid = XInternAtom(xdisplay, "_NET_WM_PID", False)) != None) {
 		pid = (unsigned long) getpid();
-		XChangeProperty(xdisplay, xdrawable,
+		XChangeProperty(xkmcon, xdrawable,
 			wmpid, /* Atom property */
 			XA_CARDINAL, /* Atom type */
 			32, /* int format, 32 really is "long" */
@@ -366,14 +367,16 @@
 			PropModeReplace, /* int mode */
 			(uchar *)&pid, /* unsigned char * data */
 			1); /* int nelements */
-		XFlush(xdisplay);
+		XFlush(xkmcon);
 	}
 	
 	/*
 	 * put the window on the screen
 	 */
-	XMapWindow(xdisplay, xdrawable);
-	XFlush(xdisplay);
+	wmdelete = XInternAtom(xkmcon, "WM_DELETE_WINDOW", True);
+	XSetWMProtocols(xkmcon, xdrawable, &wmdelete, 1);
+	XMapWindow(xkmcon, xdrawable);
+	XFlush(xkmcon);
 
 	screensize(r, xscreenchan);
 	if(gscreen == nil)
@@ -541,7 +544,6 @@
 
 	XSelectInput(xkmcon, xdrawable, mask);
 	for(;;) {
-		//XWindowEvent(xkmcon, xdrawable, mask, &event);
 		XNextEvent(xkmcon, &event);
 		xselect(&event, xkmcon);
 		xkeyboard(&event);
@@ -695,11 +697,19 @@
 xdestroy(XEvent *e)
 {
 	XDestroyWindowEvent *xe;
-	if(e->type != DestroyNotify)
-		return;
-	xe = (XDestroyWindowEvent*)e;
-	if(xe->window == xdrawable)
-		exit(0);
+	XClientMessageEvent *ce;
+
+	switch(e->type){
+	case ClientMessage:
+		/* Handle WM_DELETE_WINDOW */
+		ce = (XClientMessageEvent*)e;
+		if(ce->window == xdrawable && ce->data.l[0] == wmdelete)
+			exit(0);
+	case DestroyNotify:
+		xe = (XDestroyWindowEvent*)e;
+		if(xe->window == xdrawable)
+			exit(0);
+	}
 }
 
 static void