Hello suckless!

Posting from a different address, since I no longer have access to my older
one.

I found out this bug when testing running the X server at different depths.

When a client picks a visual with masks different to dwm's visual, the
border color's pixels are painted the wrong color.

For example, with stock dwm without the alpha patch, if the root window is
depth 30, dwm will pick a depth 30 visual. If a client picks a depth 32
composite visual, its border color becomes green instead of the default
gray.

If dwm is patched with the alpha patch, then it will pick a depth 32
composite visual, and clients that pick a depth 30 visual have the border
color red.

I have added a patch for this issue as an attachment.
Note that the patch is not written by me.
It is not portable, since it uses builtins.
diff --git a/config.mk b/config.mk
index 982dc21..18ac1c1 100644
--- a/config.mk
+++ b/config.mk
@@ -23,7 +23,7 @@ FREETYPEINC = /usr/include/freetype2
 
 # includes and libs
 INCS = -I${X11INC} -I${FREETYPEINC}
-LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS}
+LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} -lXrender
 
 # flags
 CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
diff --git a/dwm.c b/dwm.c
index ab3a84c..c76ff7b 100644
--- a/dwm.c
+++ b/dwm.c
@@ -96,6 +96,9 @@ struct Client {
 	Client *snext;
 	Monitor *mon;
 	Window win;
+	Visual *visual;
+	Colormap cmap;
+	long saved_pixels[2];
 };
 
 typedef struct {
@@ -151,6 +154,7 @@ static void buttonpress(XEvent *e);
 static void checkotherwm(void);
 static void cleanup(void);
 static void cleanupmon(Monitor *mon);
+static unsigned long clientborderpixel(Client *c, Clr *clr, int type);
 static void clientmessage(XEvent *e);
 static void configure(Client *c);
 static void configurenotify(XEvent *e);
@@ -800,7 +804,7 @@ focus(Client *c)
 		detachstack(c);
 		attachstack(c);
 		grabbuttons(c, 1);
-		XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel);
+		XSetWindowBorder(dpy, c->win, clientborderpixel(c, &scheme[SchemeSel][ColBorder], SchemeSel));
 		setfocus(c);
 	} else {
 		XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
@@ -1037,6 +1041,10 @@ manage(Window w, XWindowAttributes *wa)
 
 	c = ecalloc(1, sizeof(Client));
 	c->win = w;
+
+	c->visual = wa->visual;
+	c->cmap = wa->colormap;
+
 	/* geometry */
 	c->x = c->oldx = wa->x;
 	c->y = c->oldy = wa->y;
@@ -1063,7 +1071,7 @@ manage(Window w, XWindowAttributes *wa)
 
 	wc.border_width = c->bw;
 	XConfigureWindow(dpy, w, CWBorderWidth, &wc);
-	XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel);
+	XSetWindowBorder(dpy, w, clientborderpixel(c, &scheme[SchemeNorm][ColBorder], SchemeNorm));
 	configure(c); /* propagates border_width, if size doesn't change */
 	updatewindowtype(c);
 	updatesizehints(c);
@@ -1768,7 +1776,7 @@ unfocus(Client *c, int setfocus)
 	if (!c)
 		return;
 	grabbuttons(c, 0);
-	XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel);
+	XSetWindowBorder(dpy, c->win, clientborderpixel(c, &scheme[SchemeNorm][ColBorder], SchemeNorm));
 	if (setfocus) {
 		XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
 		XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
@@ -2163,3 +2171,84 @@ main(int argc, char *argv[])
 	XCloseDisplay(dpy);
 	return EXIT_SUCCESS;
 }
+
+static unsigned long
+clientborderpixel(Client *c, Clr *clr, int type)
+{
+	XVisualInfo vi;
+	XRenderPictFormat *fmt;
+	unsigned long pixel = 0;
+	unsigned long max;
+
+	if (c->saved_pixels[type]) {
+		return c->saved_pixels[type];
+	}
+
+	vi.visual = c->visual;
+
+	/* Find the visual's masks. */
+	XVisualInfo *info = XGetVisualInfo(
+		dpy, VisualIDMask, &vi, &(int){0});
+
+	/*
+	 * For TrueColor/DirectColor, the pixel is determined entirely
+	 * by the visual's RGB masks.
+	 */
+	if (info) {
+		vi = *info;
+		XFree(info);
+
+		if (vi.class == TrueColor || vi.class == DirectColor) {
+			max = (1UL << __builtin_popcount(vi.red_mask)) - 1;
+			pixel |= ((unsigned long)clr->color.red *
+			          max + 32767) / 65535
+			         << __builtin_ctzl(vi.red_mask);
+
+			max = (1UL << __builtin_popcount(vi.green_mask)) - 1;
+			pixel |= ((unsigned long)clr->color.green *
+			          max + 32767) / 65535
+			         << __builtin_ctzl(vi.green_mask);
+
+			max = (1UL << __builtin_popcount(vi.blue_mask)) - 1;
+			pixel |= ((unsigned long)clr->color.blue *
+			          max + 32767) / 65535
+			         << __builtin_ctzl(vi.blue_mask);
+
+			/*
+			 * A depth-32 ARGB visual needs an opaque alpha
+			 * value for the border.
+			 */
+			fmt = XRenderFindVisualFormat(dpy, c->visual);
+			if (fmt &&
+			    fmt->type == PictTypeDirect &&
+			    fmt->direct.alphaMask) {
+				max = (1UL << fmt->direct.alphaMask) - 1;
+				pixel |= max << fmt->direct.alpha;
+			}
+
+			c->saved_pixels[type] = pixel;
+			return pixel;
+		}
+	}
+
+	/*
+	 * Non-TrueColor fallback.
+	 */
+	{
+		Colormap cmap;
+		XColor xc = {
+			.red   = clr->color.red,
+			.green = clr->color.green,
+			.blue  = clr->color.blue,
+		};
+
+		cmap = c->cmap ? c->cmap : DefaultColormap(dpy, screen);
+
+		if (XAllocColor(dpy, cmap, &xc)) {
+			c->saved_pixels[type] = xc.pixel;
+			return xc.pixel;
+		}
+	}
+
+	return clr->pixel;
+}

Reply via email to