[st][patches][changealpha]: fix alpha transparency overflow when increasing opacity - sites - public wiki contents of suckless.org
 (HTM) git clone git://git.suckless.org/sites
 (DIR) Log
 (DIR) Files
 (DIR) Refs
       ---
 (DIR) commit f1cb5288fde671367ba818f907d8287999613e1f
 (DIR) parent 39a23421c0017f93ced262bff70a97521c592302
 (HTM) 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:
         M st.suckless.org/patches/changealph… |      35 +++++++++++++++++++-------------
         A st.suckless.org/patches/changealph… |     200 +++++++++++++++++++++++++++++++
         A st.suckless.org/patches/changealph… |      86 ++++++++++++++++++++++++++++++
       
       3 files changed, 307 insertions(+), 14 deletions(-)
       ---
 (DIR) 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)
 (DIR) 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;
 (DIR) 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)
       + {