commit bc7b57379bce0e7f7b3a974724e0f1a1c754a190
parent 24e1c6729e3c24b178b16fccba5dcb52b6e99388
Author: Matthias Schoth <mschoth@gmail.com>
Date: Sat, 31 Oct 2026 22:45:35 +0100
[st][patches][background_image] Fix 0.9.3 X client and 0.8.x leaks
- 0.9.3: publish the new pixmap before killing the previous owner; only
one retained client/pixmap remains, so repeated updates no longer clog
the X connection or exhaust client slots.
- 0.8.4/0.8.5: free the previous GC and pixmap on reload; failed loads
keep the current background, and loader error paths no longer leak.
- USR1 reload: use sigaction() and a self-pipe, reloading from the main
loop instead of calling Xlib in the signal handler. Applies to 0.8.4
and 0.8.5.
Diffstat:
4 files changed, 320 insertions(+), 124 deletions(-)
diff --git a/st.suckless.org/patches/background_image/st-background-image-0.8.4.diff b/st.suckless.org/patches/background_image/st-background-image-0.8.4.diff
@@ -1,12 +1,12 @@
-From 2c984d74ca15806dcfa174b7e75f48c0d01a49bf Mon Sep 17 00:00:00 2001
+From ac472ae8b588b8ef6be7cbdd4487210f07349a3c Mon Sep 17 00:00:00 2001
From: Matthias Schoth <mschoth@gmail.com>
Date: Thu, 17 Feb 2022 00:23:23 +0100
Subject: [PATCH] Implements background image and pseudo transparancy support.
---
config.def.h | 8 +++
- x.c | 141 +++++++++++++++++++++++++++++++++++++++++++++++----
- 2 files changed, 139 insertions(+), 10 deletions(-)
+ x.c | 168 ++++++++++++++++++++++++++++++++++++++++++++++++---
+ 2 files changed, 166 insertions(+), 10 deletions(-)
diff --git a/config.def.h b/config.def.h
index 6f05dce..3d352db 100644
@@ -28,7 +28,7 @@ index 6f05dce..3d352db 100644
* What program is execed by st depends of these precedence rules:
* 1: program passed with -e
diff --git a/x.c b/x.c
-index 210f184..5ecb8e5 100644
+index 210f184..a5fc827 100644
--- a/x.c
+++ b/x.c
@@ -14,6 +14,7 @@
@@ -47,15 +47,16 @@ index 210f184..5ecb8e5 100644
int ch; /* char height */
int cw; /* char width */
int mode; /* window state/mode flags */
-@@ -101,6 +103,7 @@ typedef struct {
+@@ -101,6 +103,8 @@ typedef struct {
XVaNestedList spotlist;
} ime;
Draw draw;
+ GC bggc; /* Graphics Context for background */
++ Pixmap bgpmap; /* background image pixmap */
Visual *vis;
XSetWindowAttributes attrs;
int scr;
-@@ -151,6 +154,9 @@ static void ximinstantiate(Display *, XPointer, XPointer);
+@@ -151,6 +155,9 @@ static void ximinstantiate(Display *, XPointer, XPointer);
static void ximdestroy(XIM, XPointer, XPointer);
static int xicdestroy(XIC, XPointer, XPointer);
static void xinit(int, int);
@@ -65,7 +66,7 @@ index 210f184..5ecb8e5 100644
static void cresize(int, int);
static void xresize(int, int);
static void xhints(void);
-@@ -502,6 +508,12 @@ propnotify(XEvent *e)
+@@ -502,6 +509,12 @@ propnotify(XEvent *e)
xpev->atom == clipboard)) {
selnotify(e);
}
@@ -78,7 +79,7 @@ index 210f184..5ecb8e5 100644
}
void
-@@ -532,7 +544,8 @@ selnotify(XEvent *e)
+@@ -532,7 +545,8 @@ selnotify(XEvent *e)
return;
}
@@ -88,7 +89,7 @@ index 210f184..5ecb8e5 100644
/*
* If there is some PropertyNotify with no data, then
* this is the signal of the selection owner that all
-@@ -550,9 +563,11 @@ selnotify(XEvent *e)
+@@ -550,9 +564,11 @@ selnotify(XEvent *e)
* when the selection owner does send us the next
* chunk of data.
*/
@@ -102,7 +103,7 @@ index 210f184..5ecb8e5 100644
/*
* Deleting the property is the transfer start signal.
-@@ -820,9 +835,9 @@ xsetcolorname(int x, const char *name)
+@@ -820,9 +836,9 @@ xsetcolorname(int x, const char *name)
void
xclear(int x1, int y1, int x2, int y2)
{
@@ -115,7 +116,7 @@ index 210f184..5ecb8e5 100644
}
void
-@@ -1207,6 +1222,100 @@ xinit(int cols, int rows)
+@@ -1207,6 +1223,126 @@ xinit(int cols, int rows)
xsel.xtarget = XA_STRING;
}
@@ -134,44 +135,53 @@ index 210f184..5ecb8e5 100644
+loadff(const char *filename)
+{
+ uint32_t i, hdr[4], w, h, size;
-+ uint64_t *data;
-+ FILE *f = fopen(filename, "rb");
++ uint64_t npixels, *data;
++ FILE *f;
++ XImage *xi;
+
-+ if (f == NULL) {
++ if (!(f = fopen(filename, "rb"))) {
+ fprintf(stderr, "could not load background image.\n");
+ return NULL;
+ }
+
-+ if (fread(hdr, sizeof(*hdr), LEN(hdr), f) != LEN(hdr))
-+ if (ferror(f)) {
-+ fprintf(stderr, "fread:");
-+ return NULL;
-+ }
-+ else {
-+ fprintf(stderr, "fread: Unexpected end of file\n");
-+ return NULL;
-+ }
++ if (fread(hdr, sizeof(*hdr), LEN(hdr), f) != LEN(hdr)) {
++ fprintf(stderr, "fread: %s\n", ferror(f) ? "read error" :
++ "unexpected end of file");
++ fclose(f);
++ return NULL;
++ }
+
+ if (memcmp("farbfeld", hdr, sizeof("farbfeld") - 1)) {
-+ fprintf(stderr, "Invalid magic value");
++ fprintf(stderr, "Invalid magic value\n");
++ fclose(f);
+ return NULL;
+ }
+
+ w = ntohl(hdr[2]);
+ h = ntohl(hdr[3]);
++ npixels = (uint64_t)w * h;
++ if (w == 0 || h == 0 || w > INT_MAX / 8 ||
++ npixels > UINT32_MAX || npixels > SIZE_MAX / sizeof(*data)) {
++ fprintf(stderr, "Invalid image dimensions\n");
++ fclose(f);
++ return NULL;
++ }
+ size = w * h;
-+ data = malloc(size * sizeof(uint64_t));
+
-+ if (fread(data, sizeof(uint64_t), size, f) != size)
-+ if (ferror(f)) {
-+ fprintf(stderr, "fread:");
-+ return NULL;
-+ }
-+ else {
-+ fprintf(stderr, "fread: Unexpected end of file");
-+ return NULL;
-+ }
++ data = malloc(size * sizeof(*data));
++ if (!data) {
++ fprintf(stderr, "malloc: %s\n", strerror(errno));
++ fclose(f);
++ return NULL;
++ }
+
++ if (fread(data, sizeof(*data), size, f) != size) {
++ fprintf(stderr, "fread: %s\n", ferror(f) ? "read error" :
++ "unexpected end of file");
++ free(data);
++ fclose(f);
++ return NULL;
++ }
+ fclose(f);
+
+ for (i = 0; i < size; i++)
@@ -179,9 +189,13 @@ index 210f184..5ecb8e5 100644
+ (data[i] & 0x0000000000FF0000) >> 8 |
+ (data[i] & 0x000000FF00000000) >> 32;
+
-+ XImage *xi = XCreateImage(xw.dpy, DefaultVisual(xw.dpy, xw.scr),
-+ DefaultDepth(xw.dpy, xw.scr), ZPixmap, 0,
-+ (char *)data, w, h, 32, w * 8);
++ xi = XCreateImage(xw.dpy, DefaultVisual(xw.dpy, xw.scr),
++ DefaultDepth(xw.dpy, xw.scr), ZPixmap, 0,
++ (char *)data, w, h, 32, w * 8);
++ if (!xi) {
++ free(data);
++ return NULL;
++ }
+ xi->bits_per_pixel = 64;
+ return xi;
+}
@@ -193,18 +207,31 @@ index 210f184..5ecb8e5 100644
+bginit()
+{
+ XGCValues gcvalues;
-+ Drawable bgimg;
+ XImage *bgxi = loadff(bgfile);
+
++ if (!bgxi) {
++ if (xw.bggc)
++ return;
++ memset(&gcvalues, 0, sizeof(gcvalues));
++ xw.bggc = XCreateGC(xw.dpy, xw.win, 0, &gcvalues);
++ XSetForeground(xw.dpy, xw.bggc, dc.col[defaultbg].pixel);
++ XSetFillStyle(xw.dpy, xw.bggc, FillSolid);
++ return;
++ }
++
++ if (xw.bggc)
++ XFreeGC(xw.dpy, xw.bggc);
++ if (xw.bgpmap)
++ XFreePixmap(xw.dpy, xw.bgpmap);
++
+ memset(&gcvalues, 0, sizeof(gcvalues));
+ xw.bggc = XCreateGC(xw.dpy, xw.win, 0, &gcvalues);
-+ if (!bgxi) return;
-+ bgimg = XCreatePixmap(xw.dpy, xw.win, bgxi->width, bgxi->height,
-+ DefaultDepth(xw.dpy, xw.scr));
-+ XPutImage(xw.dpy, bgimg, dc.gc, bgxi, 0, 0, 0, 0, bgxi->width,
++ xw.bgpmap = XCreatePixmap(xw.dpy, xw.win, bgxi->width, bgxi->height,
++ DefaultDepth(xw.dpy, xw.scr));
++ XPutImage(xw.dpy, xw.bgpmap, dc.gc, bgxi, 0, 0, 0, 0, bgxi->width,
+ bgxi->height);
+ XDestroyImage(bgxi);
-+ XSetTile(xw.dpy, xw.bggc, bgimg);
++ XSetTile(xw.dpy, xw.bggc, xw.bgpmap);
+ XSetFillStyle(xw.dpy, xw.bggc, FillTiled);
+ if (pseudotransparency) {
+ updatexy();
@@ -216,7 +243,7 @@ index 210f184..5ecb8e5 100644
int
xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y)
{
-@@ -1447,7 +1556,10 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
+@@ -1447,7 +1583,10 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
xclear(winx, winy + win.ch, winx + width, win.h);
/* Clean up the region we want to draw to. */
@@ -228,7 +255,7 @@ index 210f184..5ecb8e5 100644
/* Set the clip region because Xft is sometimes dirty. */
r.x = 0;
-@@ -1855,9 +1967,17 @@ cmessage(XEvent *e)
+@@ -1855,9 +1994,17 @@ cmessage(XEvent *e)
void
resize(XEvent *e)
{
@@ -249,7 +276,7 @@ index 210f184..5ecb8e5 100644
cresize(e->xconfigure.width, e->xconfigure.height);
}
-@@ -2041,6 +2161,7 @@ run:
+@@ -2041,6 +2188,7 @@ run:
rows = MAX(rows, 1);
tnew(cols, rows);
xinit(cols, rows);
@@ -258,5 +285,5 @@ index 210f184..5ecb8e5 100644
selinit();
run();
--
-2.35.1
+2.55.0
diff --git a/st.suckless.org/patches/background_image/st-background-image-0.8.5.diff b/st.suckless.org/patches/background_image/st-background-image-0.8.5.diff
@@ -1,12 +1,12 @@
-From 4ce17bbc340ca1f93f9be686f4a796797f9ca6d5 Mon Sep 17 00:00:00 2001
+From d9f5d917ac2016f3f375f4ada575e6b6fc5e2d1b Mon Sep 17 00:00:00 2001
From: Matthias Schoth <mschoth@gmail.com>
Date: Sat, 16 Apr 2022 14:21:22 +0200
Subject: [PATCH] Implements background image and pseudo transparancy support.
---
config.def.h | 8 +++
- x.c | 141 +++++++++++++++++++++++++++++++++++++++++++++++----
- 2 files changed, 139 insertions(+), 10 deletions(-)
+ x.c | 168 ++++++++++++++++++++++++++++++++++++++++++++++++---
+ 2 files changed, 166 insertions(+), 10 deletions(-)
diff --git a/config.def.h b/config.def.h
index 91ab8ca..ad19e3d 100644
@@ -28,7 +28,7 @@ index 91ab8ca..ad19e3d 100644
* What program is execed by st depends of these precedence rules:
* 1: program passed with -e
diff --git a/x.c b/x.c
-index 8a16faa..fedef53 100644
+index 8a16faa..cab0058 100644
--- a/x.c
+++ b/x.c
@@ -14,6 +14,7 @@
@@ -47,15 +47,16 @@ index 8a16faa..fedef53 100644
int ch; /* char height */
int cw; /* char width */
int mode; /* window state/mode flags */
-@@ -101,6 +103,7 @@ typedef struct {
+@@ -101,6 +103,8 @@ typedef struct {
XVaNestedList spotlist;
} ime;
Draw draw;
+ GC bggc; /* Graphics Context for background */
++ Pixmap bgpmap; /* background image pixmap */
Visual *vis;
XSetWindowAttributes attrs;
int scr;
-@@ -151,6 +154,9 @@ static void ximinstantiate(Display *, XPointer, XPointer);
+@@ -151,6 +155,9 @@ static void ximinstantiate(Display *, XPointer, XPointer);
static void ximdestroy(XIM, XPointer, XPointer);
static int xicdestroy(XIC, XPointer, XPointer);
static void xinit(int, int);
@@ -65,7 +66,7 @@ index 8a16faa..fedef53 100644
static void cresize(int, int);
static void xresize(int, int);
static void xhints(void);
-@@ -504,6 +510,12 @@ propnotify(XEvent *e)
+@@ -504,6 +511,12 @@ propnotify(XEvent *e)
xpev->atom == clipboard)) {
selnotify(e);
}
@@ -78,7 +79,7 @@ index 8a16faa..fedef53 100644
}
void
-@@ -534,7 +546,8 @@ selnotify(XEvent *e)
+@@ -534,7 +547,8 @@ selnotify(XEvent *e)
return;
}
@@ -88,7 +89,7 @@ index 8a16faa..fedef53 100644
/*
* If there is some PropertyNotify with no data, then
* this is the signal of the selection owner that all
-@@ -552,9 +565,11 @@ selnotify(XEvent *e)
+@@ -552,9 +566,11 @@ selnotify(XEvent *e)
* when the selection owner does send us the next
* chunk of data.
*/
@@ -102,7 +103,7 @@ index 8a16faa..fedef53 100644
/*
* Deleting the property is the transfer start signal.
-@@ -835,9 +850,9 @@ xsetcolorname(int x, const char *name)
+@@ -835,9 +851,9 @@ xsetcolorname(int x, const char *name)
void
xclear(int x1, int y1, int x2, int y2)
{
@@ -115,7 +116,7 @@ index 8a16faa..fedef53 100644
}
void
-@@ -1223,6 +1238,100 @@ xinit(int cols, int rows)
+@@ -1223,6 +1239,126 @@ xinit(int cols, int rows)
xsel.xtarget = XA_STRING;
}
@@ -134,44 +135,53 @@ index 8a16faa..fedef53 100644
+loadff(const char *filename)
+{
+ uint32_t i, hdr[4], w, h, size;
-+ uint64_t *data;
-+ FILE *f = fopen(filename, "rb");
++ uint64_t npixels, *data;
++ FILE *f;
++ XImage *xi;
+
-+ if (f == NULL) {
++ if (!(f = fopen(filename, "rb"))) {
+ fprintf(stderr, "Can not open background image file\n");
+ return NULL;
+ }
+
-+ if (fread(hdr, sizeof(*hdr), LEN(hdr), f) != LEN(hdr))
-+ if (ferror(f)) {
-+ fprintf(stderr, "fread:");
-+ return NULL;
-+ }
-+ else {
-+ fprintf(stderr, "fread: Unexpected end of file\n");
-+ return NULL;
-+ }
++ if (fread(hdr, sizeof(*hdr), LEN(hdr), f) != LEN(hdr)) {
++ fprintf(stderr, "fread: %s\n", ferror(f) ? "read error" :
++ "unexpected end of file");
++ fclose(f);
++ return NULL;
++ }
+
+ if (memcmp("farbfeld", hdr, sizeof("farbfeld") - 1)) {
+ fprintf(stderr, "Invalid magic value\n");
++ fclose(f);
+ return NULL;
+ }
+
+ w = ntohl(hdr[2]);
+ h = ntohl(hdr[3]);
++ npixels = (uint64_t)w * h;
++ if (w == 0 || h == 0 || w > INT_MAX / 8 ||
++ npixels > UINT32_MAX || npixels > SIZE_MAX / sizeof(*data)) {
++ fprintf(stderr, "Invalid image dimensions\n");
++ fclose(f);
++ return NULL;
++ }
+ size = w * h;
-+ data = malloc(size * sizeof(uint64_t));
+
-+ if (fread(data, sizeof(uint64_t), size, f) != size)
-+ if (ferror(f)) {
-+ fprintf(stderr, "fread:");
-+ return NULL;
-+ }
-+ else {
-+ fprintf(stderr, "fread: Unexpected end of file\n");
-+ return NULL;
-+ }
++ data = malloc(size * sizeof(*data));
++ if (!data) {
++ fprintf(stderr, "malloc: %s\n", strerror(errno));
++ fclose(f);
++ return NULL;
++ }
+
++ if (fread(data, sizeof(*data), size, f) != size) {
++ fprintf(stderr, "fread: %s\n", ferror(f) ? "read error" :
++ "unexpected end of file");
++ free(data);
++ fclose(f);
++ return NULL;
++ }
+ fclose(f);
+
+ for (i = 0; i < size; i++)
@@ -179,9 +189,13 @@ index 8a16faa..fedef53 100644
+ (data[i] & 0x0000000000FF0000) >> 8 |
+ (data[i] & 0x000000FF00000000) >> 32;
+
-+ XImage *xi = XCreateImage(xw.dpy, DefaultVisual(xw.dpy, xw.scr),
-+ DefaultDepth(xw.dpy, xw.scr), ZPixmap, 0,
-+ (char *)data, w, h, 32, w * 8);
++ xi = XCreateImage(xw.dpy, DefaultVisual(xw.dpy, xw.scr),
++ DefaultDepth(xw.dpy, xw.scr), ZPixmap, 0,
++ (char *)data, w, h, 32, w * 8);
++ if (!xi) {
++ free(data);
++ return NULL;
++ }
+ xi->bits_per_pixel = 64;
+ return xi;
+}
@@ -193,18 +207,31 @@ index 8a16faa..fedef53 100644
+bginit()
+{
+ XGCValues gcvalues;
-+ Drawable bgimg;
+ XImage *bgxi = loadff(bgfile);
+
++ if (!bgxi) {
++ if (xw.bggc)
++ return;
++ memset(&gcvalues, 0, sizeof(gcvalues));
++ xw.bggc = XCreateGC(xw.dpy, xw.win, 0, &gcvalues);
++ XSetForeground(xw.dpy, xw.bggc, dc.col[defaultbg].pixel);
++ XSetFillStyle(xw.dpy, xw.bggc, FillSolid);
++ return;
++ }
++
++ if (xw.bggc)
++ XFreeGC(xw.dpy, xw.bggc);
++ if (xw.bgpmap)
++ XFreePixmap(xw.dpy, xw.bgpmap);
++
+ memset(&gcvalues, 0, sizeof(gcvalues));
+ xw.bggc = XCreateGC(xw.dpy, xw.win, 0, &gcvalues);
-+ if (!bgxi) return;
-+ bgimg = XCreatePixmap(xw.dpy, xw.win, bgxi->width, bgxi->height,
-+ DefaultDepth(xw.dpy, xw.scr));
-+ XPutImage(xw.dpy, bgimg, dc.gc, bgxi, 0, 0, 0, 0, bgxi->width,
++ xw.bgpmap = XCreatePixmap(xw.dpy, xw.win, bgxi->width, bgxi->height,
++ DefaultDepth(xw.dpy, xw.scr));
++ XPutImage(xw.dpy, xw.bgpmap, dc.gc, bgxi, 0, 0, 0, 0, bgxi->width,
+ bgxi->height);
+ XDestroyImage(bgxi);
-+ XSetTile(xw.dpy, xw.bggc, bgimg);
++ XSetTile(xw.dpy, xw.bggc, xw.bgpmap);
+ XSetFillStyle(xw.dpy, xw.bggc, FillTiled);
+ if (pseudotransparency) {
+ updatexy();
@@ -216,7 +243,7 @@ index 8a16faa..fedef53 100644
int
xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y)
{
-@@ -1463,7 +1572,10 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
+@@ -1463,7 +1599,10 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
xclear(winx, winy + win.ch, winx + width, win.h);
/* Clean up the region we want to draw to. */
@@ -228,7 +255,7 @@ index 8a16faa..fedef53 100644
/* Set the clip region because Xft is sometimes dirty. */
r.x = 0;
-@@ -1886,9 +1998,17 @@ cmessage(XEvent *e)
+@@ -1886,9 +2025,17 @@ cmessage(XEvent *e)
void
resize(XEvent *e)
{
@@ -249,7 +276,7 @@ index 8a16faa..fedef53 100644
cresize(e->xconfigure.width, e->xconfigure.height);
}
-@@ -2072,6 +2192,7 @@ run:
+@@ -2072,6 +2219,7 @@ run:
rows = MAX(rows, 1);
tnew(cols, rows);
xinit(cols, rows);
@@ -258,5 +285,5 @@ index 8a16faa..fedef53 100644
selinit();
run();
--
-2.35.1
+2.55.0
diff --git a/st.suckless.org/patches/background_image/st-background-image-0.9.3.diff b/st.suckless.org/patches/background_image/st-background-image-0.9.3.diff
@@ -1,6 +1,6 @@
-From b43b4842d62d800da712ef7d6244967daa4ff096 Mon Sep 17 00:00:00 2001
+From c2ae9bfcb131b13435398922b1baf88c87df8908 Mon Sep 17 00:00:00 2001
From: Matthias Schoth <mschoth@gmail.com>
-Date: Tue, 18 Aug 2026 19:32:18 +0200
+Date: Sat, 31 Oct 2026 22:41:01 +0100
Subject: [PATCH] Add background image support via root window pixmap
Add a background image to st by tiling a server-side pixmap set as the
@@ -17,13 +17,19 @@ stbg reads a farbfeld image from a file or stdin (using -) and sets the
property. It is not built by default; compile separately with:
cc -o stbg stbg.c -lX11
+
+A new image is decoded and published before the previous owner is
+released, so a failed read leaves the current background untouched.
+Only the current pixmap is kept alive; replacing the background kills
+the client that owns the previous one, so periodic updates do not
+accumulate X clients or server-side image memory.
---
- config.def.h | 6 +++
+ config.def.h | 6 ++
st.c | 2 +-
st.h | 1 +
- stbg.c | 122 +++++++++++++++++++++++++++++++++++++++++++++++++
- x.c | 127 +++++++++++++++++++++++++++++++++++++++++++++++----
- 5 files changed, 248 insertions(+), 10 deletions(-)
+ stbg.c | 179 +++++++++++++++++++++++++++++++++++++++++++++++++++
+ x.c | 127 +++++++++++++++++++++++++++++++++---
+ 5 files changed, 305 insertions(+), 10 deletions(-)
create mode 100644 stbg.c
diff --git a/config.def.h b/config.def.h
@@ -70,13 +76,14 @@ index fd3b0d8..a716a73 100644
int ttynew(const char *, char *, const char *, char **);
diff --git a/stbg.c b/stbg.c
new file mode 100644
-index 0000000..80bc4cf
+index 0000000..ed9c2fa
--- /dev/null
+++ b/stbg.c
-@@ -0,0 +1,122 @@
+@@ -0,0 +1,179 @@
+/* See LICENSE file for copyright and license details. */
+/* Build: cc -o stbg stbg.c -lX11 */
+#include <errno.h>
++#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
@@ -88,6 +95,7 @@ index 0000000..80bc4cf
+#include <X11/Xatom.h>
+
+static char *argv0;
++static int xerror(Display *dpy, XErrorEvent *e) { (void)dpy; (void)e; return 0; }
+
+static void
+die(const char *errstr, ...)
@@ -107,15 +115,52 @@ index 0000000..80bc4cf
+ die("usage: %s file.ff\n %s - < file.ff\n", argv0, argv0);
+}
+
++static Pixmap
++getprev(Display *dpy, Window root)
++{
++ Atom prop = XInternAtom(dpy, "_ST_BACKGROUND_IMAGE", True);
++ Atom actual_type;
++ int actual_format;
++ unsigned long nitems, bytes_after;
++ unsigned char *prop_data = NULL;
++ Pixmap prev = None;
++
++ if (prop == None)
++ return None;
++ if (XGetWindowProperty(dpy, root, prop, 0, 1, False, XA_PIXMAP,
++ &actual_type, &actual_format, &nitems,
++ &bytes_after, &prop_data) != Success)
++ return None;
++ if (prop_data && nitems == 1 && actual_type == XA_PIXMAP && actual_format == 32)
++ prev = *(Pixmap *)prop_data;
++ if (prop_data)
++ XFree(prop_data);
++ return prev;
++}
++
++static void
++killprev(Display *dpy, Pixmap prev)
++{
++ XErrorHandler oldh;
++
++ if (prev == None)
++ return;
++ oldh = XSetErrorHandler(xerror);
++ XKillClient(dpy, prev);
++ XSync(dpy, False);
++ XSetErrorHandler(oldh);
++}
++
+int
+main(int argc, char *argv[])
+{
+ uint32_t i, hdr[4], w, h, size;
++ uint64_t npixels;
+ uint64_t *data;
+ uint32_t *data32;
+ FILE *f;
+ int fromstdin;
-+ Pixmap pmap;
++ Pixmap pmap, prev;
+ Display *dpy;
+ Window root;
+ int scr;
@@ -148,7 +193,9 @@ index 0000000..80bc4cf
+
+ w = ntohl(hdr[2]);
+ h = ntohl(hdr[3]);
-+ if (w == 0 || h == 0)
++ npixels = (uint64_t)w * h;
++ if (w == 0 || h == 0 || w > INT_MAX / 4 ||
++ npixels > UINT32_MAX || npixels > SIZE_MAX / sizeof(uint64_t))
+ die("invalid farbfeld dimensions\n");
+ size = w * h;
+
@@ -156,14 +203,18 @@ index 0000000..80bc4cf
+ if (!data)
+ die("malloc: %s\n", strerror(errno));
+
-+ if (fread(data, sizeof(uint64_t), size, f) != size)
++ if (fread(data, sizeof(uint64_t), size, f) != size) {
++ free(data);
+ die("fread: unexpected end of file\n");
++ }
+ if (!fromstdin)
+ fclose(f);
+
+ data32 = malloc(size * sizeof(uint32_t));
-+ if (!data32)
++ if (!data32) {
++ free(data);
+ die("malloc: %s\n", strerror(errno));
++ }
+
+ for (i = 0; i < size; i++) {
+ uint64_t p = data[i];
@@ -176,8 +227,16 @@ index 0000000..80bc4cf
+ xi = XCreateImage(dpy, DefaultVisual(dpy, scr),
+ DefaultDepth(dpy, scr), ZPixmap, 0,
+ (char *)data32, w, h, 32, w * 4);
++ if (!xi) {
++ free(data32);
++ die("XCreateImage failed\n");
++ }
+
+ pmap = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, scr));
++ if (!pmap) {
++ XDestroyImage(xi);
++ die("XCreatePixmap failed\n");
++ }
+
+ memset(&gcvalues, 0, sizeof(gcvalues));
+ gcvalues.graphics_exposures = False;
@@ -187,12 +246,16 @@ index 0000000..80bc4cf
+ XDestroyImage(xi);
+ XFreeGC(dpy, gc);
+
++ prev = getprev(dpy, root);
++
++ XSetCloseDownMode(dpy, RetainPermanent);
+ atom_stbg = XInternAtom(dpy, "_ST_BACKGROUND_IMAGE", False);
+ XChangeProperty(dpy, root, atom_stbg, XA_PIXMAP, 32,
+ PropModeReplace, (unsigned char *)&pmap, 1);
-+ XSetCloseDownMode(dpy, RetainPermanent);
++ killprev(dpy, prev);
+
+ XSync(dpy, False);
++ XCloseDisplay(dpy);
+
+ return 0;
+}
diff --git a/st.suckless.org/patches/background_image/st-background-image-signal-reload-0.8.5.diff b/st.suckless.org/patches/background_image/st-background-image-signal-reload-0.8.5.diff
@@ -1,51 +1,130 @@
-From 1317d64817460aee9321e29e5e31c072d59139c6 Mon Sep 17 00:00:00 2001
+From b56c9081dc4c62b7de5357cc73bc10a310f011b8 Mon Sep 17 00:00:00 2001
From: Matthias Schoth <mschoth@gmail.com>
Date: Sun, 17 Apr 2022 04:01:59 +0200
Subject: [PATCH] Reload background image on USR1 signal.
+Queue reload requests from the signal handler and service them from the
+main loop. Xlib is not async-signal-safe, so calling it directly from the
+handler can crash st or leak X resources when signals arrive while st is
+drawing.
---
- x.c | 14 ++++++++++++++
- 1 file changed, 14 insertions(+)
+ x.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 55 insertions(+), 1 deletion(-)
diff --git a/x.c b/x.c
-index fedef53..ac79ac7 100644
+index cab0058..7c9076c 100644
--- a/x.c
+++ b/x.c
-@@ -156,6 +156,7 @@ static int xicdestroy(XIC, XPointer, XPointer);
+@@ -1,5 +1,6 @@
+ /* See LICENSE for license details. */
+ #include <errno.h>
++#include <fcntl.h>
+ #include <math.h>
+ #include <limits.h>
+ #include <locale.h>
+@@ -157,6 +158,8 @@ static int xicdestroy(XIC, XPointer, XPointer);
static void xinit(int, int);
static void updatexy(void);
static XImage *loadff(const char *);
-+static void bgreload();
++static void bgreload(int);
++static void bgreloadinit(void);
static void bginit();
static void cresize(int, int);
static void xresize(int, int);
-@@ -1305,6 +1306,18 @@ loadff(const char *filename)
+@@ -1319,6 +1322,46 @@ loadff(const char *filename)
return xi;
}
++static int bgreloadpipe[2] = {-1, -1};
++
+/*
-+ * reload background image on USR1 signal
++ * Xlib is not async-signal-safe, so just wake up the main loop and let it
++ * reload the image there.
+ */
-+void
-+bgreload()
++static void
++bgreload(int sig)
+{
-+ XFreeGC(xw.dpy, xw.bggc);
-+ bginit();
-+ redraw();
-+ signal(SIGUSR1, bgreload);
++ char c = 0;
++
++ (void)sig;
++ if (bgreloadpipe[1] != -1)
++ (void)write(bgreloadpipe[1], &c, 1);
++}
++
++static void
++bgreloadinit(void)
++{
++ struct sigaction sa;
++ int flags;
++
++ if (pipe(bgreloadpipe) < 0)
++ die("pipe: %s\n", strerror(errno));
++
++ flags = fcntl(bgreloadpipe[0], F_GETFL);
++ fcntl(bgreloadpipe[0], F_SETFL, flags | O_NONBLOCK);
++ flags = fcntl(bgreloadpipe[1], F_GETFL);
++ fcntl(bgreloadpipe[1], F_SETFL, flags | O_NONBLOCK);
++ fcntl(bgreloadpipe[0], F_SETFD, FD_CLOEXEC);
++ fcntl(bgreloadpipe[1], F_SETFD, FD_CLOEXEC);
++
++ memset(&sa, 0, sizeof(sa));
++ sa.sa_handler = bgreload;
++ sigemptyset(&sa.sa_mask);
++ sa.sa_flags = SA_RESTART;
++ if (sigaction(SIGUSR1, &sa, NULL) < 0)
++ die("sigaction: %s\n", strerror(errno));
+}
+
/*
* initialize background image
*/
-@@ -2193,6 +2206,7 @@ run:
+@@ -2046,6 +2089,8 @@ run(void)
+ int w = win.w, h = win.h;
+ fd_set rfd;
+ int xfd = XConnectionNumber(xw.dpy), ttyfd, xev, drawing;
++ int bgrfd = bgreloadpipe[0];
++ char c;
+ struct timespec seltv, *tv, now, lastblink, trigger;
+ double timeout;
+
+@@ -2072,6 +2117,7 @@ run(void)
+ FD_ZERO(&rfd);
+ FD_SET(ttyfd, &rfd);
+ FD_SET(xfd, &rfd);
++ FD_SET(bgrfd, &rfd);
+
+ if (XPending(xw.dpy))
+ timeout = 0; /* existing events might not set xfd */
+@@ -2080,13 +2126,20 @@ run(void)
+ seltv.tv_nsec = 1E6 * (timeout - 1E3 * seltv.tv_sec);
+ tv = timeout >= 0 ? &seltv : NULL;
+
+- if (pselect(MAX(xfd, ttyfd)+1, &rfd, NULL, NULL, tv, NULL) < 0) {
++ if (pselect(MAX(xfd, MAX(ttyfd, bgrfd))+1, &rfd, NULL, NULL, tv, NULL) < 0) {
+ if (errno == EINTR)
+ continue;
+ die("select failed: %s\n", strerror(errno));
+ }
+ clock_gettime(CLOCK_MONOTONIC, &now);
+
++ if (FD_ISSET(bgrfd, &rfd)) {
++ while (read(bgrfd, &c, 1) > 0)
++ ;
++ bginit();
++ redraw();
++ }
++
+ if (FD_ISSET(ttyfd, &rfd))
+ ttyread();
+
+@@ -2219,6 +2272,7 @@ run:
+ rows = MAX(rows, 1);
tnew(cols, rows);
xinit(cols, rows);
++ bgreloadinit();
bginit();
-+ signal(SIGUSR1, bgreload);
xsetenv();
selinit();
- run();
--
-2.35.3
+2.55.0