[wiki] [sites] [st][patches][background-images] Defer position sync to the draw path || Matthias Schoth

From: <git_AT_suckless.org>
Date: Tue, 18 Aug 2026 19:50:05 +0200

commit 36cf773e954d3abbd244db5dcb1734f73bb3ce16
Author: Matthias Schoth <mschoth_AT_gmail.com>
Date: Tue Aug 18 19:48:40 2026 +0200

    [st][patches][background-images] Defer position sync to the draw path
    
    The pseudotransparency feature blits the root window's pixmap into the
    terminal, anchored to the window's screen position. That origin is only
    ever consumed by xclear() at paint time, yet updatexy() (causing a synchronous
    X server round-trip) was previously invoked from scattered event handlers
    (resize, propnotify, bginit, run). Now we track whether the window may have
    moved (posdirty) and resolve the origin lazily, exactly once, on the first
    draw after a move. The authoritative position is fetched at draw time, so
    coalesced or redundant events are always resolved to the latest true position.
    
    A same-size ConfigureNotify (a pure move) repaints only when posdirty is set,
    via tfulldirt() rather than redraw(). tfulldirt() marks all rows dirty and lets
    the run loop's single draw() do the repaint which means no synchronous draw
    inside the event handler, and no double draw.
    
    As a result steady-state frames incur zero X round-trips and a move now costs
    exactly one round-trip on the first frame after it. A burst of ConfigureNotify
    events (drag across monitors, quick tiling changes) costs one round-trip and
    one coalesced repaint in total instead of one synchronous full-screen repaint
    per event.

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 bbacfacc..9e9d4190 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
_AT_@ -1,6 +1,6 @@
-From 5f070227058901afd7525ffe6363c93faf163ef0 Mon Sep 17 00:00:00 2001
+From b43b4842d62d800da712ef7d6244967daa4ff096 Mon Sep 17 00:00:00 2001
 From: Matthias Schoth <mschoth_AT_gmail.com>
-Date: Fri, 31 Jul 2026 23:50:51 +0200
+Date: Tue, 18 Aug 2026 19:32:18 +0200
 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
_AT_@ -19,9 +19,11 @@ property. It is not built by default; compile separately with:
     cc -o stbg stbg.c -lX11
 ---
  config.def.h | 6 +++
- stbg.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++++
- x.c | 128 +++++++++++++++++++++++++++++++++++++++++++++++----
- 3 files changed, 246 insertions(+), 10 deletions(-)
+ st.c | 2 +-
+ st.h | 1 +
+ stbg.c | 122 +++++++++++++++++++++++++++++++++++++++++++++++++
+ x.c | 127 +++++++++++++++++++++++++++++++++++++++++++++++----
+ 5 files changed, 248 insertions(+), 10 deletions(-)
  create mode 100644 stbg.c
 
 diff --git a/config.def.h b/config.def.h
_AT_@ -41,6 +43,31 @@ index 2cd740a..3d98c87 100644
  /*
   * What program is execed by st depends of these precedence rules:
   * 1: program passed with -e
+diff --git a/st.c b/st.c
+index 8e57991..2663816 100644
+--- a/st.c
++++ b/st.c
+_AT_@ -194,7 +194,7 @@ static void tsetscroll(int, int);
+ static void tswapscreen(void);
+ static void tsetmode(int, int, const int *, int);
+ static int twrite(const char *, int, int);
+-static void tfulldirt(void);
++void tfulldirt(void);
+ static void tcontrolcode(uchar );
+ static void tdectest(char );
+ static void tdefutf8(char);
+diff --git a/st.h b/st.h
+index fd3b0d8..a716a73 100644
+--- a/st.h
++++ b/st.h
+_AT_@ -89,6 +89,7 @@ void toggleprinter(const Arg *);
+ int tattrset(int);
+ void tnew(int, int);
+ void tresize(int, int);
++void tfulldirt(void);
+ void tsetdirtattr(int);
+ void ttyhangup(void);
+ int ttynew(const char *, char *, const char *, char **);
 diff --git a/stbg.c b/stbg.c
 new file mode 100644
 index 0000000..80bc4cf
_AT_@ -170,7 +197,7 @@ index 0000000..80bc4cf
 + return 0;
 +}
 diff --git a/x.c b/x.c
-index d73152b..c0ec757 100644
+index d73152b..bee9d35 100644
 --- a/x.c
 +++ b/x.c
 _AT_@ -81,6 +81,7 @@ typedef XftGlyphFontSpec GlyphFontSpec;
_AT_@ -201,7 +228,15 @@ index d73152b..c0ec757 100644
  static void cresize(int, int);
  static void xresize(int, int);
  static void xhints(void);
-_AT_@ -515,6 +522,18 @@ propnotify(XEvent *e)
+_AT_@ -220,6 +227,7 @@ static DC dc;
+ static XWindow xw;
+ static XSelection xsel;
+ static TermWindow win;
++static int posdirty = 1; /* window may have moved; re-sync tile origin */
+
+ /* Font Ring Cache */
+ enum {
+_AT_@ -515,6 +523,17 @@ propnotify(XEvent *e)
                           xpev->atom == clipboard)) {
                  selnotify(e);
          }
_AT_@ -214,7 +249,6 @@ index d73152b..c0ec757 100644
 +
 + if (pseudotransparency &&
 + e->xproperty.atom == xw.netwmstate_atom) {
-+ updatexy();
 + redraw();
 + }
  }
_AT_@ -257,7 +291,7 @@ index d73152b..c0ec757 100644
  }
  
  void
-_AT_@ -1242,6 +1264,78 @@ xinit(int cols, int rows)
+_AT_@ -1242,6 +1264,77 @@ xinit(int cols, int rows)
                  xsel.xtarget = XA_STRING;
  }
  
_AT_@ -323,7 +357,6 @@ index d73152b..c0ec757 100644
 + }
 +
 + if (pseudotransparency) {
-+ updatexy();
 + MODBIT(xw.attrs.event_mask, 1, PropertyChangeMask);
 + XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs);
 + }
_AT_@ -336,7 +369,7 @@ index d73152b..c0ec757 100644
  int
  xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y)
  {
-_AT_@ -1482,7 +1576,10 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
+_AT_@ -1482,7 +1575,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. */
_AT_@ -348,37 +381,38 @@ index d73152b..c0ec757 100644
  
          /* Set the clip region because Xft is sometimes dirty. */
          r.x = 0;
-_AT_@ -1914,9 +2011,17 @@ cmessage(XEvent *e)
+_AT_@ -1651,6 +1747,12 @@ xsettitle(char *p)
+ int
+ xstartdraw(void)
+ {
++ if (pseudotransparency && IS_SET(MODE_VISIBLE)) {
++ if (posdirty) {
++ updatexy();
++ posdirty = 0;
++ }
++ }
+ return IS_SET(MODE_VISIBLE);
+ }
+
+_AT_@ -1914,9 +2016,15 @@ cmessage(XEvent *e)
  void
  resize(XEvent *e)
  {
 - if (e->xconfigure.width == win.w && e->xconfigure.height == win.h)
 - return;
--
-+	if (pseudotransparency) {
-+		if (e->xconfigure.width == win.w &&
-+		    e->xconfigure.height == win.h &&
-+		    e->xconfigure.x == win.x && e->xconfigure.y == win.y)
-+			return;
-+		updatexy();
-+	} else {
-+		if (e->xconfigure.width == win.w &&
-+		    e->xconfigure.height == win.h)
-+			return;
++	if (pseudotransparency &&
++	    (e->xconfigure.x != win.x || e->xconfigure.y != win.y))
++		posdirty = 1;
+ 
++	if (e->xconfigure.width == win.w && e->xconfigure.height == win.h) {
++		if (pseudotransparency && posdirty)
++			tfulldirt();
++		return;
 +	}
  	cresize(e->xconfigure.width, e->xconfigure.height);
  }
  
-_AT_@ -1947,6 +2052,8 @@ run(void)
- 	} while (ev.type != MapNotify);
- 
- 	ttyfd = ttynew(opt_line, shell, opt_io, opt_cmd);
-+	if (pseudotransparency)
-+		updatexy();
- 	cresize(w, h);
- 
- 	for (timeout = -1, drawing = 0, lastblink = (struct timespec){0};;) {
-_AT_@ -2100,6 +2207,7 @@ run:
+_AT_@ -2100,6 +2208,7 @@ run:
  	rows = MAX(rows, 1);
  	tnew(cols, rows);
  	xinit(cols, rows);
Received on Tue Aug 18 2026 - 19:50:05 CEST

This archive was generated by hypermail 2.3.0 : Tue Aug 18 2026 - 20:00:50 CEST