commit f1cb5288fde671367ba818f907d8287999613e1f
parent 39a23421c0017f93ced262bff70a97521c592302
Author: Abhishek Kumar <abhiiishekparmar@gmail.com>
Date: Mon, 27 Oct 2025 14:40:59 +0530
[st][patches][changealpha]: fix alpha transparency overflow when increasing opacity
Fixes a bug where increasing opacity from certain alpha values
(e.g. 0.85 → 0.95 → 1.05) caused the terminal to become fully
transparent instead of opaque. The patch clamps the alpha value
between 0.1 and 1.0 to prevent overflow and compositor misbehavior.
2025-10-27
Diffstat:
3 files changed, 307 insertions(+), 14 deletions(-)
diff --git a/st.suckless.org/patches/changealpha/index.md b/st.suckless.org/patches/changealpha/index.md
@@ -4,12 +4,12 @@ A patch that allows for updating terminal transparency natively.
### Description
-This patch is an extension to the [alpha](https://st.suckless.org/patches/alpha) patch,
-which _must_ be applied prior.
+This patch is an extension to the [alpha](https://st.suckless.org/patches/alpha) patch,
+which _must_ be applied prior.
### Notes
-* This patch assumes that the alpha variable is a float, which was only updated in alpha
+* This patch assumes that the alpha variable is a float, which was only updated in alpha
patch version 0.8.2 i.e., 0.8.2 is the minimum compatible version.
* Don't forget to append "config.h" to the end of the rm command under the clean rule in
Makefile if you are having issues running make install.
@@ -18,25 +18,32 @@ Makefile if you are having issues running make install.
Later versions of the default alpha patch provide a method of changing window opacity via
the -A flag, however, with no method of querying the current alpha level, it is difficult
-to write scripts which increment or decrement this value. It is typically possible to update
-the opacity via your compositor, but not all compositors support this, and even if they do,
-your setup is still less portable at the end of the day. The
-[transset-df](https://aur.archlinux.org/packages/transset-df) package
-located in the AUR is the closest I've come to changing opacity on a per-window basis,
-however, it still requires writing a script and binding that script to some keys using a
-keybinding manager of some sort. Not to mention, it's fatal flaw - you cannot increase the
+to write scripts which increment or decrement this value. It is typically possible to update
+the opacity via your compositor, but not all compositors support this, and even if they do,
+your setup is still less portable at the end of the day. The
+[transset-df](https://aur.archlinux.org/packages/transset-df) package
+located in the AUR is the closest I've come to changing opacity on a per-window basis,
+however, it still requires writing a script and binding that script to some keys using a
+keybinding manager of some sort. Not to mention, it's fatal flaw - you cannot increase the
opacity beyond the default value set with the alpha patch.
+### Changelog
+* **20251027-0.9.3**: Fixes a bug where increasing opacity from certain alpha values (e.g. 0.85 → 0.95 → 1.05) caused the terminal to become fully transparent instead of opaque.
+The patch clamps alpha between 0.1 and 1.0 to prevent overflow and compositor misbehavior.
+
### Download
#### Patch: alpha (0.8.5) + changealpha
-[st-alpha-changealpha-0.1.diff](st-alpha-changealpha-20230519-b44f2ad.diff)
+* [st-alpha-changealpha-0.1.diff](st-alpha-changealpha-20230519-b44f2ad.diff)
+* [st-alpha-changealpha-20251027-0.9.3.diff](st-alpha-changealpha-20251027-0.9.3.diff)
#### Patch: changealpha
-[st-changealpha-0.1.diff](st-changealpha-20230519-b44f2ad.diff)
+* [st-changealpha-0.1.diff](st-changealpha-20230519-b44f2ad.diff)
+* [st-changealpha-20251027-0.9.3.diff](st-changealpha-20251027-0.9.3.diff)
-### Author
+### Author
-Neil Kingdom - <neil@neilkingdom.xyz>
+* Neil Kingdom - <neil@neilkingdom.xyz>
+* Abhishek Kumar - <abhiiishekparmar@gmail.com> (fixed transparency overflow issue)
diff --git a/st.suckless.org/patches/changealpha/st-alpha-changealpha-20251027-0.9.3.diff b/st.suckless.org/patches/changealpha/st-alpha-changealpha-20251027-0.9.3.diff
@@ -0,0 +1,200 @@
+diff --git a/st/config.def.h b/st_patched/config.def.h
+index 91ab8ca..59fc2a9 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -93,6 +93,10 @@ char *termname = "st-256color";
+ */
+ unsigned int tabspaces = 8;
+
++/* bg opacity */
++float alpha = 0.8;
++float alpha_def;
++
+ /* Terminal colors (16 first used in escape sequence) */
+ static const char *colorname[] = {
+ /* 8 normal colors */
+@@ -201,6 +205,9 @@ static Shortcut shortcuts[] = {
+ { TERMMOD, XK_Y, selpaste, {.i = 0} },
+ { ShiftMask, XK_Insert, selpaste, {.i = 0} },
+ { TERMMOD, XK_Num_Lock, numlock, {.i = 0} },
++ { MODKEY, XK_bracketleft, chgalpha, {.f = -1} }, /* Decrease opacity */
++ { MODKEY|ShiftMask, XK_braceright, chgalpha, {.f = +1} }, /* Increase opacity */
++ { MODKEY, XK_bracketright,chgalpha, {.f = 0} }, /* Reset opacity */
+ };
+
+ /*
+diff --git a/st/st.h b/st_patched/st.h
+index fd3b0d8..cda8c13 100644
+--- a/st.h
++++ b/st.h
+@@ -124,3 +124,4 @@ extern unsigned int tabspaces;
+ extern unsigned int defaultfg;
+ extern unsigned int defaultbg;
+ extern unsigned int defaultcs;
++extern float alpha, alpha_def;
+diff --git a/st/config.mk b/st_patched/config.mk
+index 1e306f8..47c615e 100644
+--- a/config.mk
++++ b/config.mk
+@@ -16,7 +16,7 @@ PKG_CONFIG = pkg-config
+ INCS = -I$(X11INC) \
+ `$(PKG_CONFIG) --cflags fontconfig` \
+ `$(PKG_CONFIG) --cflags freetype2`
+-LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \
++LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\
+ `$(PKG_CONFIG) --libs fontconfig` \
+ `$(PKG_CONFIG) --libs freetype2`
+
+diff --git a/st/x.c b/st_patched/x.c
+index aa09997..3b05a55 100644
+--- a/x.c
++++ b/x.c
+@@ -59,6 +59,7 @@ static void zoom(const Arg *);
+ static void zoomabs(const Arg *);
+ static void zoomreset(const Arg *);
+ static void ttysend(const Arg *);
++static void chgalpha(const Arg *);
+
+ /* config.h for applying patches and the configuration. */
+ #include "config.h"
+@@ -105,6 +106,7 @@ typedef struct {
+ XSetWindowAttributes attrs;
+ int scr;
+ int isfixed; /* is fixed geometry? */
++ int depth; /* bit depth */
+ int l, t; /* left and top offset */
+ int gm; /* geometry mask */
+ } XWindow;
+@@ -243,6 +245,7 @@ static char *usedfont = NULL;
+ static double usedfontsize = 0;
+ static double defaultfontsize = 0;
+
++static char *opt_alpha = NULL;
+ static char *opt_class = NULL;
+ static char **opt_cmd = NULL;
+ static char *opt_embed = NULL;
+@@ -752,7 +755,7 @@ xresize(int col, int row)
+
+ XFreePixmap(xw.dpy, xw.buf);
+ xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h,
+- DefaultDepth(xw.dpy, xw.scr));
++ xw.depth);
+ XftDrawChange(xw.draw, xw.buf);
+ xclear(0, 0, win.w, win.h);
+
+@@ -812,6 +815,13 @@ xloadcols(void)
+ else
+ die("could not allocate color %d\n", i);
+ }
++
++ /* set alpha value of bg color */
++ if (opt_alpha)
++ alpha = strtof(opt_alpha, NULL);
++ dc.col[defaultbg].color.alpha = (unsigned short)(0xffff * alpha);
++ dc.col[defaultbg].pixel &= 0x00FFFFFF;
++ dc.col[defaultbg].pixel |= (unsigned char)(0xff * alpha) << 24;
+ loaded = 1;
+ }
+
+@@ -1134,11 +1144,23 @@ xinit(int cols, int rows)
+ Window parent;
+ pid_t thispid = getpid();
+ XColor xmousefg, xmousebg;
++ XWindowAttributes attr;
++ XVisualInfo vis;
+
+ if (!(xw.dpy = XOpenDisplay(NULL)))
+ die("can't open display\n");
+ xw.scr = XDefaultScreen(xw.dpy);
+- xw.vis = XDefaultVisual(xw.dpy, xw.scr);
++
++ if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0)))) {
++ parent = XRootWindow(xw.dpy, xw.scr);
++ xw.depth = 32;
++ } else {
++ XGetWindowAttributes(xw.dpy, parent, &attr);
++ xw.depth = attr.depth;
++ }
++
++ XMatchVisualInfo(xw.dpy, xw.scr, xw.depth, TrueColor, &vis);
++ xw.vis = vis.visual;
+
+ /* font */
+ if (!FcInit())
+@@ -1147,8 +1169,11 @@ xinit(int cols, int rows)
+ usedfont = (opt_font == NULL)? font : opt_font;
+ xloadfonts(usedfont, 0);
+
++ /* Backup default alpha value */
++ alpha_def = alpha;
++
+ /* colors */
+- xw.cmap = XDefaultColormap(xw.dpy, xw.scr);
++ xw.cmap = XCreateColormap(xw.dpy, parent, xw.vis, None);
+ xloadcols();
+
+ /* adjust fixed window geometry */
+@@ -1168,19 +1193,15 @@ xinit(int cols, int rows)
+ | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask;
+ xw.attrs.colormap = xw.cmap;
+
+- if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0))))
+- parent = XRootWindow(xw.dpy, xw.scr);
+ xw.win = XCreateWindow(xw.dpy, parent, xw.l, xw.t,
+- win.w, win.h, 0, XDefaultDepth(xw.dpy, xw.scr), InputOutput,
++ win.w, win.h, 0, xw.depth, InputOutput,
+ xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity
+ | CWEventMask | CWColormap, &xw.attrs);
+
+ memset(&gcvalues, 0, sizeof(gcvalues));
+ gcvalues.graphics_exposures = False;
+- dc.gc = XCreateGC(xw.dpy, parent, GCGraphicsExposures,
+- &gcvalues);
+- xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h,
+- DefaultDepth(xw.dpy, xw.scr));
++ xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, xw.depth);
++ dc.gc = XCreateGC(xw.dpy, xw.buf, GCGraphicsExposures, &gcvalues);
+ XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel);
+ XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
+
+@@ -1371,6 +1392,24 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
+ return numspecs;
+ }
+
++void
++chgalpha(const Arg *arg)
++{
++ if (arg->f == -1.0f && alpha >= 0.1f)
++ alpha -= 0.1f;
++ else if (arg->f == 1.0f && alpha < 1.0f)
++ alpha += 0.1f;
++ else if (arg->f == 0.0f)
++ alpha = alpha_def;
++ else
++ return;
++
++ /* Clamp alpha so it never exceeds valid range */
++ if (alpha < 0.1f)
++ alpha = 0.1f;
++ if (alpha > 1.0f)
++ alpha = 1.0f;
++
++ dc.col[defaultbg].color.alpha = (unsigned short)(0xFFFF * alpha);
++ /* Required to remove artifacting from borderpx */
++ cresize(0, 0);
++ redraw();
++}
++
+ void
+ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y)
+ {
+@@ -2038,6 +2077,9 @@ main(int argc, char *argv[])
+ case 'a':
+ allowaltscreen = 0;
+ break;
++ case 'A':
++ opt_alpha = EARGF(usage());
++ break;
+ case 'c':
+ opt_class = EARGF(usage());
+ break;
diff --git a/st.suckless.org/patches/changealpha/st-changealpha-20251027-0.9.3.diff b/st.suckless.org/patches/changealpha/st-changealpha-20251027-0.9.3.diff
@@ -0,0 +1,86 @@
+diff --git a/config.def.h b/config.def.h
+index 91ab8ca..8a06176 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -93,6 +93,9 @@ char *termname = "st-256color";
+ */
+ unsigned int tabspaces = 8;
+
++/* Background opacity */
++float alpha_def;
++
+ /* Terminal colors (16 first used in escape sequence) */
+ static const char *colorname[] = {
+ /* 8 normal colors */
+@@ -201,6 +204,9 @@ static Shortcut shortcuts[] = {
+ { TERMMOD, XK_Y, selpaste, {.i = 0} },
+ { ShiftMask, XK_Insert, selpaste, {.i = 0} },
+ { TERMMOD, XK_Num_Lock, numlock, {.i = 0} },
++ { MODKEY, XK_bracketleft, chgalpha, {.f = -1} }, /* Decrease opacity */
++ { MODKEY|ShiftMask, XK_braceright, chgalpha, {.f = +1} }, /* Increase opacity */
++ { MODKEY, XK_bracketright,chgalpha, {.f = 0} }, /* Reset opacity */
+ };
+
+ /*
+diff --git a/st.h b/st.h
+index fd3b0d8..3bb587e 100644
+--- a/st.h
++++ b/st.h
+@@ -124,3 +124,4 @@ extern unsigned int tabspaces;
+ extern unsigned int defaultfg;
+ extern unsigned int defaultbg;
+ extern unsigned int defaultcs;
++extern float alpha_def;
+diff --git a/x.c b/x.c
+index aa09997..f8c8c1a 100644
+--- a/x.c
++++ b/x.c
+@@ -59,6 +59,7 @@ static void zoom(const Arg *);
+ static void zoomabs(const Arg *);
+ static void zoomreset(const Arg *);
+ static void ttysend(const Arg *);
++static void chgalpha(const Arg *);
+
+ /* config.h for applying patches and the configuration. */
+ #include "config.h"
+@@ -1147,6 +1148,9 @@ xinit(int cols, int rows)
+ usedfont = (opt_font == NULL)? font : opt_font;
+ xloadfonts(usedfont, 0);
+
++ /* Backup default alpha value */
++ alpha_def = alpha;
++
+ /* colors */
+ xw.cmap = XDefaultColormap(xw.dpy, xw.scr);
+ xloadcols();
+@@ -1371,6 +1375,24 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
+ return numspecs;
+ }
+
++void
++chgalpha(const Arg *arg)
++{
++ if (arg->f == -1.0f && alpha >= 0.1f)
++ alpha -= 0.1f;
++ else if (arg->f == 1.0f && alpha < 1.0f)
++ alpha += 0.1f;
++ else if (arg->f == 0.0f)
++ alpha = alpha_def;
++ else
++ return;
++
++ /* Clamp alpha so it never exceeds valid range */
++ if (alpha < 0.1f)
++ alpha = 0.1f;
++ if (alpha > 1.0f)
++ alpha = 1.0f;
++
++ dc.col[defaultbg].color.alpha = (unsigned short)(0xFFFF * alpha);
++ /* Required to remove artifacting from borderpx */
++ cresize(0, 0);
++ redraw();
++}
++
+ void
+ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y)
+ {