sites

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

commit b2bcc0bde65b019274bfbd64d1a1a290ba6dfb62
parent bc7b57379bce0e7f7b3a974724e0f1a1c754a190
Author: Matthias Schoth <mschoth@gmail.com>
Date:   Sun,  8 Nov 2026 14:53:53 +0100

[st][patches][background_image] Fix double paste with pseudotransparency

pseudotransparency keeps PropertyChangeMask enabled on the st window to
track _NET_WM_STATE. This also delivered ordinary selection PropertyNotify
events to selnotify(), so a paste was processed twice (an extra empty
bracketed paste).

Solution: Track the property of an in-progress INCR transfer and only route
selection PropertyNotify events while that transfer is active.
Behavior without pseudotransparency is unchanged.

Diffstat:
Mst.suckless.org/patches/background_image/st-background-image-0.9.3.diff | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
1 file changed, 62 insertions(+), 30 deletions(-)

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 c2ae9bfcb131b13435398922b1baf88c87df8908 Mon Sep 17 00:00:00 2001 +From d910857f5b63dbfca4299a0daa7ce3155c880543 Mon Sep 17 00:00:00 2001 From: Matthias Schoth <mschoth@gmail.com> -Date: Sat, 31 Oct 2026 22:41:01 +0100 +Date: Sun, 8 Nov 2026 14:15:23 +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,19 +17,13 @@ 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 ++ st.c | 2 +- st.h | 1 + stbg.c | 179 +++++++++++++++++++++++++++++++++++++++++++++++++++ - x.c | 127 +++++++++++++++++++++++++++++++++--- - 5 files changed, 305 insertions(+), 10 deletions(-) + x.c | 147 ++++++++++++++++++++++++++++++++++++++---- + 5 files changed, 322 insertions(+), 13 deletions(-) create mode 100644 stbg.c diff --git a/config.def.h b/config.def.h @@ -260,7 +254,7 @@ index 0000000..ed9c2fa + return 0; +} diff --git a/x.c b/x.c -index d73152b..bee9d35 100644 +index d73152b..dad569f 100644 --- a/x.c +++ b/x.c @@ -81,6 +81,7 @@ typedef XftGlyphFontSpec GlyphFontSpec; @@ -281,7 +275,15 @@ index d73152b..bee9d35 100644 Visual *vis; XSetWindowAttributes attrs; int scr; -@@ -151,6 +155,9 @@ static void ximinstantiate(Display *, XPointer, XPointer); +@@ -111,6 +115,7 @@ typedef struct { + + typedef struct { + Atom xtarget; ++ Atom incrprop; /* property of an in-progress INCR transfer */ + char *primary, *clipboard; + struct timespec tclick1; + struct timespec tclick2; +@@ -151,6 +156,9 @@ static void ximinstantiate(Display *, XPointer, XPointer); static void ximdestroy(XIM, XPointer, XPointer); static int xicdestroy(XIC, XPointer, XPointer); static void xinit(int, int); @@ -291,7 +293,7 @@ index d73152b..bee9d35 100644 static void cresize(int, int); static void xresize(int, int); static void xhints(void); -@@ -220,6 +227,7 @@ static DC dc; +@@ -220,6 +228,7 @@ static DC dc; static XWindow xw; static XSelection xsel; static TermWindow win; @@ -299,8 +301,21 @@ index d73152b..bee9d35 100644 /* Font Ring Cache */ enum { -@@ -515,6 +523,17 @@ propnotify(XEvent *e) - xpev->atom == clipboard)) { +@@ -510,11 +519,29 @@ propnotify(XEvent *e) + Atom clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); + + xpev = &e->xproperty; ++ /* ++ * propnotify is only used for incremental transfers. With ++ * pseudotransparency PropertyChangeMask is kept enabled all the ++ * time, so ordinary selection property changes arrive here too; ++ * processing them would paste the selection a second time. ++ */ + if (xpev->state == PropertyNewValue && + (xpev->atom == XA_PRIMARY || +- xpev->atom == clipboard)) { ++ xpev->atom == clipboard) && ++ xsel.incrprop == xpev->atom) { selnotify(e); } + @@ -317,22 +332,39 @@ index d73152b..bee9d35 100644 } void -@@ -545,7 +564,8 @@ selnotify(XEvent *e) - return; +@@ -536,6 +563,9 @@ selnotify(XEvent *e) + if (property == None) + return; + ++ if (e->type == SelectionNotify) ++ xsel.incrprop = None; ++ + do { + if (XGetWindowProperty(xw.dpy, xw.win, property, ofs, + BUFSIZ/4, False, AnyPropertyType, +@@ -552,9 +582,12 @@ selnotify(XEvent *e) + * data has been transferred. We won't need to receive + * PropertyNotify events anymore. + */ +- MODBIT(xw.attrs.event_mask, 0, PropertyChangeMask); +- XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, +- &xw.attrs); ++ xsel.incrprop = None; ++ if (!pseudotransparency) { ++ MODBIT(xw.attrs.event_mask, 0, PropertyChangeMask); ++ XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, ++ &xw.attrs); ++ } } -- if (e->type == PropertyNotify && nitems == 0 && rem == 0) { -+ if (e->type == PropertyNotify && nitems == 0 && rem == 0 && -+ !pseudotransparency) { - /* - * If there is some PropertyNotify with no data, then - * this is the signal of the selection owner that all -@@ -563,9 +583,11 @@ selnotify(XEvent *e) + if (type == incratom) { +@@ -563,9 +596,12 @@ selnotify(XEvent *e) * when the selection owner does send us the next * chunk of data. */ - MODBIT(xw.attrs.event_mask, 1, PropertyChangeMask); - XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, ++ xsel.incrprop = property; + if (!pseudotransparency) { + MODBIT(xw.attrs.event_mask, 1, PropertyChangeMask); + XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, @@ -341,7 +373,7 @@ index d73152b..bee9d35 100644 /* * Deleting the property is the transfer start signal. -@@ -851,9 +873,9 @@ xsetcolorname(int x, const char *name) +@@ -851,9 +887,9 @@ xsetcolorname(int x, const char *name) void xclear(int x1, int y1, int x2, int y2) { @@ -354,7 +386,7 @@ index d73152b..bee9d35 100644 } void -@@ -1242,6 +1264,77 @@ xinit(int cols, int rows) +@@ -1242,6 +1278,77 @@ xinit(int cols, int rows) xsel.xtarget = XA_STRING; } @@ -432,7 +464,7 @@ index d73152b..bee9d35 100644 int xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y) { -@@ -1482,7 +1575,10 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i +@@ -1482,7 +1589,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. */ @@ -444,7 +476,7 @@ index d73152b..bee9d35 100644 /* Set the clip region because Xft is sometimes dirty. */ r.x = 0; -@@ -1651,6 +1747,12 @@ xsettitle(char *p) +@@ -1651,6 +1761,12 @@ xsettitle(char *p) int xstartdraw(void) { @@ -457,7 +489,7 @@ index d73152b..bee9d35 100644 return IS_SET(MODE_VISIBLE); } -@@ -1914,9 +2016,15 @@ cmessage(XEvent *e) +@@ -1914,9 +2030,15 @@ cmessage(XEvent *e) void resize(XEvent *e) { @@ -475,7 +507,7 @@ index d73152b..bee9d35 100644 cresize(e->xconfigure.width, e->xconfigure.height); } -@@ -2100,6 +2208,7 @@ run: +@@ -2100,6 +2222,7 @@ run: rows = MAX(rows, 1); tnew(cols, rows); xinit(cols, rows);