sites

public wiki contents of suckless.org
git clone git://git.suckless.org/sites
Log | Files | Refs

st-background-image-0.9.3.diff (17093B)


      1 From 58b00ef34d4f8c34070f1544537f64b69bb91aab Mon Sep 17 00:00:00 2001
      2 From: Matthias Schoth <mschoth@gmail.com>
      3 Date: Sun, 8 Nov 2026 17:03:18 +0100
      4 Subject: [PATCH] Add background image support via root window pixmap
      5 
      6 Add a background image to st by tiling a server-side pixmap set as the
      7 _ST_BACKGROUND_IMAGE root window property. st watches the root window
      8 for property changes, so re-running the stbg setter updates all running
      9 st instances immediately.
     10 
     11 The pixmap is shared between all instances and no composite manager is
     12 required. A pseudotransparency config option fixes the tile coordinates
     13 to the screen origin so the terminal backgrounds line up with the
     14 wallpaper behind the window.
     15 
     16 stbg reads a farbfeld image from a file or stdin (using -) and sets the
     17 property. It is not built by default; compile separately with:
     18 
     19     cc -o stbg stbg.c -lX11
     20 ---
     21  config.def.h |   6 ++
     22  st.c         |   2 +-
     23  st.h         |   1 +
     24  stbg.c       | 264 +++++++++++++++++++++++++++++++++++++++++++++++++++
     25  x.c          | 204 ++++++++++++++++++++++++++++++++++++---
     26  5 files changed, 464 insertions(+), 13 deletions(-)
     27  create mode 100644 stbg.c
     28 
     29 diff --git a/config.def.h b/config.def.h
     30 index 2cd740a..3d98c87 100644
     31 --- a/config.def.h
     32 +++ b/config.def.h
     33 @@ -8,6 +8,12 @@
     34  static char *font = "Liberation Mono:pixelsize=12:antialias=true:autohint=true";
     35  static int borderpx = 2;
     36  
     37 +/*
     38 + * pseudo transparency fixes coordinates to the screen origin
     39 + * requires _ST_BACKGROUND_IMAGE root window property (set via stbg)
     40 + */
     41 +static const int pseudotransparency = 0;
     42 +
     43  /*
     44   * What program is execed by st depends of these precedence rules:
     45   * 1: program passed with -e
     46 diff --git a/st.c b/st.c
     47 index 8e57991..2663816 100644
     48 --- a/st.c
     49 +++ b/st.c
     50 @@ -194,7 +194,7 @@ static void tsetscroll(int, int);
     51  static void tswapscreen(void);
     52  static void tsetmode(int, int, const int *, int);
     53  static int twrite(const char *, int, int);
     54 -static void tfulldirt(void);
     55 +void tfulldirt(void);
     56  static void tcontrolcode(uchar );
     57  static void tdectest(char );
     58  static void tdefutf8(char);
     59 diff --git a/st.h b/st.h
     60 index fd3b0d8..a716a73 100644
     61 --- a/st.h
     62 +++ b/st.h
     63 @@ -89,6 +89,7 @@ void toggleprinter(const Arg *);
     64  int tattrset(int);
     65  void tnew(int, int);
     66  void tresize(int, int);
     67 +void tfulldirt(void);
     68  void tsetdirtattr(int);
     69  void ttyhangup(void);
     70  int ttynew(const char *, char *, const char *, char **);
     71 diff --git a/stbg.c b/stbg.c
     72 new file mode 100644
     73 index 0000000..b984aa1
     74 --- /dev/null
     75 +++ b/stbg.c
     76 @@ -0,0 +1,264 @@
     77 +/* See LICENSE file for copyright and license details. */
     78 +/* Build: cc -o stbg stbg.c -lX11 */
     79 +#include <errno.h>
     80 +#include <limits.h>
     81 +#include <stdarg.h>
     82 +#include <stdio.h>
     83 +#include <stdlib.h>
     84 +#include <stdint.h>
     85 +#include <string.h>
     86 +#include <sys/stat.h>
     87 +#include <unistd.h>
     88 +#include <arpa/inet.h>
     89 +#include <X11/Xlib.h>
     90 +#include <X11/Xutil.h>
     91 +#include <X11/Xatom.h>
     92 +
     93 +#define MAX_PIXELS (1u << 27)
     94 +
     95 +static char *argv0;
     96 +static int xerr;
     97 +static int xerror(Display *dpy, XErrorEvent *e) { (void)dpy; xerr = e->error_code; return 0; }
     98 +
     99 +static void
    100 +die(const char *errstr, ...)
    101 +{
    102 +	va_list ap;
    103 +
    104 +	fprintf(stderr, "%s: ", argv0);
    105 +	va_start(ap, errstr);
    106 +	vfprintf(stderr, errstr, ap);
    107 +	va_end(ap);
    108 +	exit(1);
    109 +}
    110 +
    111 +static void
    112 +usage(void)
    113 +{
    114 +	die("usage: %s file.ff\n       %s - < file.ff\n", argv0, argv0);
    115 +}
    116 +
    117 +static Pixmap
    118 +getprev(Display *dpy, Window root)
    119 +{
    120 +	Atom prop = XInternAtom(dpy, "_ST_BACKGROUND_IMAGE", True);
    121 +	Atom actual_type;
    122 +	int actual_format;
    123 +	unsigned long nitems, bytes_after;
    124 +	unsigned char *prop_data = NULL;
    125 +	Pixmap prev = None;
    126 +
    127 +	if (prop == None)
    128 +		return None;
    129 +	if (XGetWindowProperty(dpy, root, prop, 0, 1, False, XA_PIXMAP,
    130 +	                       &actual_type, &actual_format, &nitems,
    131 +	                       &bytes_after, &prop_data) != Success)
    132 +		return None;
    133 +	if (prop_data && nitems == 1 && actual_type == XA_PIXMAP && actual_format == 32)
    134 +		prev = *(Pixmap *)prop_data;
    135 +	if (prop_data)
    136 +		XFree(prop_data);
    137 +	return prev;
    138 +}
    139 +
    140 +static int
    141 +ispixmap(Display *dpy, Pixmap pm)
    142 +{
    143 +	XWindowAttributes wa;
    144 +	Window root;
    145 +	XErrorHandler oldh;
    146 +	int x, y, ok;
    147 +	unsigned int w, h, bw, depth;
    148 +
    149 +	oldh = XSetErrorHandler(xerror);
    150 +	if (XGetWindowAttributes(dpy, pm, &wa)) {
    151 +		XSetErrorHandler(oldh);
    152 +		return 0;
    153 +	}
    154 +	ok = XGetGeometry(dpy, pm, &root, &x, &y, &w, &h, &bw, &depth);
    155 +	XSync(dpy, False);
    156 +	XSetErrorHandler(oldh);
    157 +	return ok;
    158 +}
    159 +
    160 +static void
    161 +killprev(Display *dpy, Pixmap prev)
    162 +{
    163 +	XErrorHandler oldh;
    164 +
    165 +	if (prev == None || !ispixmap(dpy, prev))
    166 +		return;
    167 +	oldh = XSetErrorHandler(xerror);
    168 +	XKillClient(dpy, prev);
    169 +	XSync(dpy, False);
    170 +	XSetErrorHandler(oldh);
    171 +}
    172 +
    173 +static unsigned long
    174 +channel(unsigned int v, unsigned long mask)
    175 +{
    176 +	unsigned long m = mask;
    177 +	int shift = 0, bits = 0;
    178 +
    179 +	if (!mask)
    180 +		return 0;
    181 +	while (!(m & 1)) {
    182 +		m >>= 1;
    183 +		shift++;
    184 +	}
    185 +	while (m & 1) {
    186 +		bits++;
    187 +		m >>= 1;
    188 +	}
    189 +	return (((unsigned long)v * ((1UL << bits) - 1) + 32767) / 65535) << shift;
    190 +}
    191 +
    192 +int
    193 +main(int argc, char *argv[])
    194 +{
    195 +	uint32_t hdr[4], w, h, size, x, y;
    196 +	uint64_t npixels;
    197 +	uint64_t *data;
    198 +	unsigned char *p;
    199 +	unsigned long pixel;
    200 +	FILE *f;
    201 +	int fromstdin;
    202 +	Pixmap pmap, prev;
    203 +	Display *dpy;
    204 +	Visual *vis;
    205 +	Window root;
    206 +	int scr, depth;
    207 +	Atom atom_stbg;
    208 +	GC gc;
    209 +	XGCValues gcvalues;
    210 +	XImage *xi;
    211 +	XErrorHandler oldh;
    212 +
    213 +	argv0 = argv[0];
    214 +
    215 +	if (argc != 2)
    216 +		usage();
    217 +
    218 +	if (!(dpy = XOpenDisplay(NULL)))
    219 +		die("can't open display\n");
    220 +
    221 +	scr = DefaultScreen(dpy);
    222 +	root = RootWindow(dpy, scr);
    223 +	vis = DefaultVisual(dpy, scr);
    224 +	depth = DefaultDepth(dpy, scr);
    225 +
    226 +	if (vis->class != TrueColor)
    227 +		die("unsupported visual class\n");
    228 +
    229 +	fromstdin = !strcmp(argv[1], "-");
    230 +	f = fromstdin ? stdin : fopen(argv[1], "rb");
    231 +	if (!fromstdin && !f)
    232 +		die("can't open %s\n", argv[1]);
    233 +
    234 +	if (fread(hdr, sizeof(*hdr), 4, f) != 4)
    235 +		die("fread: unexpected end of file\n");
    236 +
    237 +	if (memcmp("farbfeld", hdr, sizeof("farbfeld") - 1))
    238 +		die("invalid farbfeld magic\n");
    239 +
    240 +	w = ntohl(hdr[2]);
    241 +	h = ntohl(hdr[3]);
    242 +	npixels = (uint64_t)w * h;
    243 +	if (w == 0 || h == 0 || w > INT_MAX / 4 ||
    244 +	    npixels > MAX_PIXELS || npixels > UINT32_MAX ||
    245 +	    npixels > SIZE_MAX / sizeof(uint64_t))
    246 +		die("invalid farbfeld dimensions\n");
    247 +	size = w * h;
    248 +
    249 +	if (!fromstdin) {
    250 +		struct stat st;
    251 +
    252 +		if (fstat(fileno(f), &st) == 0 && S_ISREG(st.st_mode) &&
    253 +		    (uint64_t)st.st_size < sizeof(hdr) + npixels * sizeof(uint64_t))
    254 +			die("fread: unexpected end of file\n");
    255 +	}
    256 +
    257 +	data = malloc(size * sizeof(uint64_t));
    258 +	if (!data)
    259 +		die("malloc: %s\n", strerror(errno));
    260 +
    261 +	if (fread(data, sizeof(uint64_t), size, f) != size) {
    262 +		free(data);
    263 +		die("fread: unexpected end of file\n");
    264 +	}
    265 +	if (!fromstdin)
    266 +		fclose(f);
    267 +
    268 +	xi = XCreateImage(dpy, vis, depth, ZPixmap, 0, NULL, w, h, 32, 0);
    269 +	if (!xi) {
    270 +		free(data);
    271 +		die("XCreateImage failed\n");
    272 +	}
    273 +	xi->data = malloc((size_t)xi->bytes_per_line * h);
    274 +	if (!xi->data) {
    275 +		XDestroyImage(xi);
    276 +		free(data);
    277 +		die("malloc: %s\n", strerror(errno));
    278 +	}
    279 +	p = (unsigned char *)data;
    280 +	for (y = 0; y < h; y++) {
    281 +		for (x = 0; x < w; x++, p += 8) {
    282 +			pixel = channel(((unsigned int)p[0] << 8) | p[1], vis->red_mask) |
    283 +			        channel(((unsigned int)p[2] << 8) | p[3], vis->green_mask) |
    284 +			        channel(((unsigned int)p[4] << 8) | p[5], vis->blue_mask);
    285 +			XPutPixel(xi, x, y, pixel);
    286 +		}
    287 +	}
    288 +	free(data);
    289 +
    290 +	pmap = XCreatePixmap(dpy, root, w, h, depth);
    291 +	if (!pmap) {
    292 +		XDestroyImage(xi);
    293 +		die("XCreatePixmap failed\n");
    294 +	}
    295 +
    296 +	memset(&gcvalues, 0, sizeof(gcvalues));
    297 +	gcvalues.graphics_exposures = False;
    298 +	gc = XCreateGC(dpy, pmap, GCGraphicsExposures, &gcvalues);
    299 +	if (!gc) {
    300 +		XDestroyImage(xi);
    301 +		die("XCreateGC failed\n");
    302 +	}
    303 +
    304 +	oldh = XSetErrorHandler(xerror);
    305 +	xerr = 0;
    306 +	XPutImage(dpy, pmap, gc, xi, 0, 0, 0, 0, w, h);
    307 +	XSync(dpy, False);
    308 +	XSetErrorHandler(oldh);
    309 +	if (xerr) {
    310 +		XDestroyImage(xi);
    311 +		XFreeGC(dpy, gc);
    312 +		die("XPutImage failed\n");
    313 +	}
    314 +	XDestroyImage(xi);
    315 +	XFreeGC(dpy, gc);
    316 +
    317 +	prev = getprev(dpy, root);
    318 +
    319 +	XSetCloseDownMode(dpy, RetainPermanent);
    320 +	atom_stbg = XInternAtom(dpy, "_ST_BACKGROUND_IMAGE", False);
    321 +	oldh = XSetErrorHandler(xerror);
    322 +	xerr = 0;
    323 +	XChangeProperty(dpy, root, atom_stbg, XA_PIXMAP, 32,
    324 +	                PropModeReplace, (unsigned char *)&pmap, 1);
    325 +	XSync(dpy, False);
    326 +	XSetErrorHandler(oldh);
    327 +	if (xerr) {
    328 +		XSetCloseDownMode(dpy, DestroyAll);
    329 +		XFreePixmap(dpy, pmap);
    330 +		die("XChangeProperty failed\n");
    331 +	}
    332 +	if (prev != None) {
    333 +		usleep(200000);
    334 +		killprev(dpy, prev);
    335 +	}
    336 +	XSync(dpy, False);
    337 +	XCloseDisplay(dpy);
    338 +
    339 +	return 0;
    340 +}
    341 diff --git a/x.c b/x.c
    342 index d73152b..f75f2aa 100644
    343 --- a/x.c
    344 +++ b/x.c
    345 @@ -81,6 +81,7 @@ typedef XftGlyphFontSpec GlyphFontSpec;
    346  typedef struct {
    347  	int tw, th; /* tty width and height */
    348  	int w, h; /* window width and height */
    349 +	int x, y; /* window location */
    350  	int ch; /* char height */
    351  	int cw; /* char width  */
    352  	int mode; /* window state/mode flags */
    353 @@ -101,6 +102,9 @@ typedef struct {
    354  		XVaNestedList spotlist;
    355  	} ime;
    356  	Draw draw;
    357 +	GC bggc;          /* Graphics Context for background */
    358 +	Atom stbg_atom;   /* _ST_BACKGROUND_IMAGE atom */
    359 +	Atom netwmstate_atom; /* _NET_WM_STATE atom */
    360  	Visual *vis;
    361  	XSetWindowAttributes attrs;
    362  	int scr;
    363 @@ -111,6 +115,7 @@ typedef struct {
    364  
    365  typedef struct {
    366  	Atom xtarget;
    367 +	Atom incrprop; /* property of an in-progress INCR transfer */
    368  	char *primary, *clipboard;
    369  	struct timespec tclick1;
    370  	struct timespec tclick2;
    371 @@ -151,6 +156,9 @@ static void ximinstantiate(Display *, XPointer, XPointer);
    372  static void ximdestroy(XIM, XPointer, XPointer);
    373  static int xicdestroy(XIC, XPointer, XPointer);
    374  static void xinit(int, int);
    375 +static void bginit(void);
    376 +static Pixmap bginitfromroot(void);
    377 +static void updatexy(void);
    378  static void cresize(int, int);
    379  static void xresize(int, int);
    380  static void xhints(void);
    381 @@ -220,6 +228,44 @@ static DC dc;
    382  static XWindow xw;
    383  static XSelection xsel;
    384  static TermWindow win;
    385 +static int posdirty = 1; /* window may have moved; re-sync tile origin */
    386 +static int bggone;
    387 +static int xprobe;
    388 +static int xprobeerr;
    389 +static XErrorHandler oldxerror;
    390 +
    391 +static int
    392 +ispixmap(Pixmap pm, Window *root, unsigned int *depth)
    393 +{
    394 +	XWindowAttributes wa;
    395 +	int x, y, ok;
    396 +	unsigned int w, h, bw;
    397 +
    398 +	xprobe = 1;
    399 +	if (XGetWindowAttributes(xw.dpy, pm, &wa)) {
    400 +		xprobe = 0;
    401 +		return 0;
    402 +	}
    403 +	xprobeerr = 0;
    404 +	ok = XGetGeometry(xw.dpy, pm, root, &x, &y, &w, &h, &bw, depth);
    405 +	xprobe = 0;
    406 +	return ok && !xprobeerr;
    407 +}
    408 +
    409 +static int
    410 +xerror(Display *dpy, XErrorEvent *e)
    411 +{
    412 +	if (xprobe) {
    413 +		xprobeerr = 1;
    414 +		return 0;
    415 +	}
    416 +	if (e->error_code == BadPixmap || e->error_code == BadDrawable ||
    417 +	    e->error_code == BadMatch) {
    418 +		bggone = 1;
    419 +		return 0;
    420 +	}
    421 +	return oldxerror ? oldxerror(dpy, e) : 0;
    422 +}
    423  
    424  /* Font Ring Cache */
    425  enum {
    426 @@ -510,11 +556,29 @@ propnotify(XEvent *e)
    427  	Atom clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
    428  
    429  	xpev = &e->xproperty;
    430 +	/*
    431 +	 * propnotify is only used for incremental transfers. With
    432 +	 * pseudotransparency PropertyChangeMask is kept enabled all the
    433 +	 * time, so ordinary selection property changes arrive here too;
    434 +	 * processing them would paste the selection a second time.
    435 +	 */
    436  	if (xpev->state == PropertyNewValue &&
    437  			(xpev->atom == XA_PRIMARY ||
    438 -			 xpev->atom == clipboard)) {
    439 +			 xpev->atom == clipboard) &&
    440 +			xsel.incrprop == xpev->atom) {
    441  		selnotify(e);
    442  	}
    443 +
    444 +	if (xpev->atom == xw.stbg_atom) {
    445 +		bginit();
    446 +		redraw();
    447 +		return;
    448 +	}
    449 +
    450 +	if (pseudotransparency &&
    451 +	    e->xproperty.atom == xw.netwmstate_atom) {
    452 +		redraw();
    453 +	}
    454  }
    455  
    456  void
    457 @@ -536,6 +600,9 @@ selnotify(XEvent *e)
    458  	if (property == None)
    459  		return;
    460  
    461 +	if (e->type == SelectionNotify)
    462 +		xsel.incrprop = None;
    463 +
    464  	do {
    465  		if (XGetWindowProperty(xw.dpy, xw.win, property, ofs,
    466  					BUFSIZ/4, False, AnyPropertyType,
    467 @@ -552,9 +619,12 @@ selnotify(XEvent *e)
    468  			 * data has been transferred. We won't need to receive
    469  			 * PropertyNotify events anymore.
    470  			 */
    471 -			MODBIT(xw.attrs.event_mask, 0, PropertyChangeMask);
    472 -			XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask,
    473 -					&xw.attrs);
    474 +			xsel.incrprop = None;
    475 +			if (!pseudotransparency) {
    476 +				MODBIT(xw.attrs.event_mask, 0, PropertyChangeMask);
    477 +				XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask,
    478 +						&xw.attrs);
    479 +			}
    480  		}
    481  
    482  		if (type == incratom) {
    483 @@ -563,9 +633,12 @@ selnotify(XEvent *e)
    484  			 * when the selection owner does send us the next
    485  			 * chunk of data.
    486  			 */
    487 -			MODBIT(xw.attrs.event_mask, 1, PropertyChangeMask);
    488 -			XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask,
    489 +			xsel.incrprop = property;
    490 +			if (!pseudotransparency) {
    491 +				MODBIT(xw.attrs.event_mask, 1, PropertyChangeMask);
    492 +				XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask,
    493  					&xw.attrs);
    494 +			}
    495  
    496  			/*
    497  			 * Deleting the property is the transfer start signal.
    498 @@ -851,9 +924,13 @@ xsetcolorname(int x, const char *name)
    499  void
    500  xclear(int x1, int y1, int x2, int y2)
    501  {
    502 -	XftDrawRect(xw.draw,
    503 -			&dc.col[IS_SET(MODE_REVERSE)? defaultfg : defaultbg],
    504 -			x1, y1, x2-x1, y2-y1);
    505 +	if (IS_SET(MODE_REVERSE)) {
    506 +		XftDrawRect(xw.draw, &dc.col[defaultfg], x1, y1, x2-x1, y2-y1);
    507 +		return;
    508 +	}
    509 +	if (pseudotransparency)
    510 +		XSetTSOrigin(xw.dpy, xw.bggc, -win.x, -win.y);
    511 +	XFillRectangle(xw.dpy, xw.buf, xw.bggc, x1, y1, x2-x1, y2-y1);
    512  }
    513  
    514  void
    515 @@ -1137,6 +1214,7 @@ xinit(int cols, int rows)
    516  
    517  	if (!(xw.dpy = XOpenDisplay(NULL)))
    518  		die("can't open display\n");
    519 +	oldxerror = XSetErrorHandler(xerror);
    520  	xw.scr = XDefaultScreen(xw.dpy);
    521  	xw.vis = XDefaultVisual(xw.dpy, xw.scr);
    522  
    523 @@ -1242,6 +1320,87 @@ xinit(int cols, int rows)
    524  		xsel.xtarget = XA_STRING;
    525  }
    526  
    527 +void
    528 +bginit(void)
    529 +{
    530 +	XGCValues gcvalues;
    531 +	Pixmap bgpmap;
    532 +
    533 +	if (xw.bggc)
    534 +		XFreeGC(xw.dpy, xw.bggc);
    535 +
    536 +	memset(&gcvalues, 0, sizeof(gcvalues));
    537 +	xw.bggc = XCreateGC(xw.dpy, xw.win, 0, &gcvalues);
    538 +
    539 +	bgpmap = bginitfromroot();
    540 +
    541 +	if (bgpmap == None) {
    542 +		XSetForeground(xw.dpy, xw.bggc, dc.col[defaultbg].pixel);
    543 +		XSetFillStyle(xw.dpy, xw.bggc, FillSolid);
    544 +	}
    545 +
    546 +	if (pseudotransparency) {
    547 +		MODBIT(xw.attrs.event_mask, 1, PropertyChangeMask);
    548 +		XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs);
    549 +	}
    550 +
    551 +	xw.stbg_atom = XInternAtom(xw.dpy, "_ST_BACKGROUND_IMAGE", False);
    552 +	xw.netwmstate_atom = XInternAtom(xw.dpy, "_NET_WM_STATE", False);
    553 +	XSelectInput(xw.dpy, DefaultRootWindow(xw.dpy), PropertyChangeMask);
    554 +}
    555 +
    556 +/*
    557 + * initialize background image from root window _ST_BACKGROUND_IMAGE
    558 + */
    559 +static Pixmap
    560 +bginitfromroot(void)
    561 +{
    562 +	Atom prop, atom_type;
    563 +	int format;
    564 +	unsigned long nitems, after;
    565 +	unsigned char *data;
    566 +	Pixmap pmap;
    567 +
    568 +	prop = XInternAtom(xw.dpy, "_ST_BACKGROUND_IMAGE", True);
    569 +	if (prop == None)
    570 +		return None;
    571 +
    572 +	if (XGetWindowProperty(xw.dpy, DefaultRootWindow(xw.dpy), prop,
    573 +	                       0, 1, False, AnyPropertyType, &atom_type,
    574 +	                       &format, &nitems, &after, &data) != Success)
    575 +		return None;
    576 +
    577 +	if (atom_type != XA_PIXMAP || format != 32 || nitems < 1) {
    578 +		XFree(data);
    579 +		return None;
    580 +	}
    581 +
    582 +	pmap = *(Pixmap *)data;
    583 +	XFree(data);
    584 +
    585 +	{
    586 +		Window rr;
    587 +		unsigned int dep;
    588 +
    589 +		if (!ispixmap(pmap, &rr, &dep) ||
    590 +		    rr != DefaultRootWindow(xw.dpy) ||
    591 +		    dep != DefaultDepth(xw.dpy, xw.scr))
    592 +			return None;
    593 +	}
    594 +
    595 +	XSetTile(xw.dpy, xw.bggc, pmap);
    596 +	XSetFillStyle(xw.dpy, xw.bggc, FillTiled);
    597 +	return pmap;
    598 +}
    599 +
    600 +void
    601 +updatexy(void)
    602 +{
    603 +	Window child;
    604 +	XTranslateCoordinates(xw.dpy, xw.win, DefaultRootWindow(xw.dpy), 0, 0,
    605 +	                      &win.x, &win.y, &child);
    606 +}
    607 +
    608  int
    609  xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y)
    610  {
    611 @@ -1482,7 +1641,10 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
    612  		xclear(winx, winy + win.ch, winx + width, win.h);
    613  
    614  	/* Clean up the region we want to draw to. */
    615 -	XftDrawRect(xw.draw, bg, winx, winy, width, win.ch);
    616 +	if (bg == &dc.col[defaultbg])
    617 +		xclear(winx, winy, winx + width, winy + win.ch);
    618 +	else
    619 +		XftDrawRect(xw.draw, bg, winx, winy, width, win.ch);
    620  
    621  	/* Set the clip region because Xft is sometimes dirty. */
    622  	r.x = 0;
    623 @@ -1651,6 +1813,17 @@ xsettitle(char *p)
    624  int
    625  xstartdraw(void)
    626  {
    627 +	if (bggone && IS_SET(MODE_VISIBLE)) {
    628 +		bggone = 0;
    629 +		bginit();
    630 +		tfulldirt();
    631 +	}
    632 +	if (pseudotransparency && IS_SET(MODE_VISIBLE)) {
    633 +		if (posdirty) {
    634 +			updatexy();
    635 +			posdirty = 0;
    636 +		}
    637 +	}
    638  	return IS_SET(MODE_VISIBLE);
    639  }
    640  
    641 @@ -1914,9 +2087,15 @@ cmessage(XEvent *e)
    642  void
    643  resize(XEvent *e)
    644  {
    645 -	if (e->xconfigure.width == win.w && e->xconfigure.height == win.h)
    646 -		return;
    647 +	if (pseudotransparency &&
    648 +	    (e->xconfigure.x != win.x || e->xconfigure.y != win.y))
    649 +		posdirty = 1;
    650  
    651 +	if (e->xconfigure.width == win.w && e->xconfigure.height == win.h) {
    652 +		if (pseudotransparency && posdirty)
    653 +			tfulldirt();
    654 +		return;
    655 +	}
    656  	cresize(e->xconfigure.width, e->xconfigure.height);
    657  }
    658  
    659 @@ -2100,6 +2279,7 @@ run:
    660  	rows = MAX(rows, 1);
    661  	tnew(cols, rows);
    662  	xinit(cols, rows);
    663 +	bginit();
    664  	xsetenv();
    665  	selinit();
    666  	run();
    667 -- 
    668 2.55.0
    669