commit bc7b57379bce0e7f7b3a974724e0f1a1c754a190
Author: Matthias Schoth <[email protected]>
Date:   Sat Oct 31 22:45:35 2026 +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.

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
index f32c9476..d7ac5acf 100644
--- 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 <[email protected]>
 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.
");
 +              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
");
-+                      return NULL;
-+              }
++      if (fread(hdr, sizeof(*hdr), LEN(hdr), f) != LEN(hdr)) {
++              fprintf(stderr, "fread: %s
", 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
");
++              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
");
++              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
", strerror(errno));
++              fclose(f);
++              return NULL;
++      }
 +
++      if (fread(data, sizeof(*data), size, f) != size) {
++              fprintf(stderr, "fread: %s
", 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
index 9f6c5495..67b630a0 100644
--- 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 <[email protected]>
 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
");
 +              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
");
-+                      return NULL;
-+              }
++      if (fread(hdr, sizeof(*hdr), LEN(hdr), f) != LEN(hdr)) {
++              fprintf(stderr, "fread: %s
", ferror(f) ? "read error" :
++                      "unexpected end of file");
++              fclose(f);
++              return NULL;
++      }
 +
 +      if (memcmp("farbfeld", hdr, sizeof("farbfeld") - 1)) {
 +              fprintf(stderr, "Invalid magic value
");
++              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
");
++              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
", strerror(errno));
++              fclose(f);
++              return NULL;
++      }
 +
++      if (fread(data, sizeof(*data), size, f) != size) {
++              fprintf(stderr, "fread: %s
", 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
index 9e9d4190..4478fcd4 100644
--- 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 <[email protected]>
-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
       %s - < file.ff
", 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
");
 +      size = w * h;
 +
@@ -156,14 +203,18 @@ index 0000000..80bc4cf
 +      if (!data)
 +              die("malloc: %s
", 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
");
++      }
 +      if (!fromstdin)
 +              fclose(f);
 +
 +      data32 = malloc(size * sizeof(uint32_t));
-+      if (!data32)
++      if (!data32) {
++              free(data);
 +              die("malloc: %s
", 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
");
++      }
 +
 +      pmap = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, scr));
++      if (!pmap) {
++              XDestroyImage(xi);
++              die("XCreatePixmap failed
");
++      }
 +
 +      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
index d323efc9..072cae0a 100644
--- 
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 <[email protected]>
 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
", 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
", 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
", 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
 


Reply via email to