URI:
       dwm statuscmd: support all mouse buttons - sites - public wiki contents of suckless.org
  HTML git clone git://git.suckless.org/sites
   DIR Log
   DIR Files
   DIR Refs
       ---
   DIR commit 4bebceb71fc1d9e78cf1a04a7fd55b4bb5e8680d
   DIR parent 8bc6f6a05b2b3dd67df30a5d5296833a59bce0d5
  HTML Author: Justinas Grigas <dev@jstnas.com>
       Date:   Sat, 24 Jan 2026 16:36:32 +0000
       
       dwm statuscmd: support all mouse buttons
       
       Added support for all mouse buttons.
       Fixed getstatusbarpid declaration.
       Reworded and reformatted the index file.
       
       Diffstat:
         A dwm.suckless.org/patches/statuscmd… |     221 +++++++++++++++++++++++++++++++
         M dwm.suckless.org/patches/statuscmd… |     170 ++++++++++++++++++-------------
       
       2 files changed, 318 insertions(+), 73 deletions(-)
       ---
   DIR diff --git a/dwm.suckless.org/patches/statuscmd/dwm-statuscmd-20260124-a9aa0d8.diff b/dwm.suckless.org/patches/statuscmd/dwm-statuscmd-20260124-a9aa0d8.diff
       @@ -0,0 +1,221 @@
       +From 5c4db00890ed6f967dc14b80e704a0cd89faa209 Mon Sep 17 00:00:00 2001
       +From: Justinas Grigas <dev@jstnas.com>
       +Date: Tue, 8 Oct 2024 17:55:11 +0100
       +Subject: [PATCH] dwm statuscmd: support all mouse buttons
       +
       +This patch enables all mouse buttons to interact with the status bar
       +script.
       +
       +It also fixes getstatusbarpid declaration.
       +---
       + config.def.h |  12 +++++-
       + dwm.c        | 104 ++++++++++++++++++++++++++++++++++++++++++++++++---
       + 2 files changed, 110 insertions(+), 6 deletions(-)
       +
       +diff --git a/config.def.h b/config.def.h
       +index 81c3fc0..dbe798b 100644
       +--- a/config.def.h
       ++++ b/config.def.h
       +@@ -56,6 +56,8 @@ static const Layout layouts[] = {
       + /* helper for spawning shell commands in the pre dwm-5.0 fashion */
       + #define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
       + 
       ++#define STATUSBAR "dwmblocks"
       ++
       + /* commands */
       + static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */
       + static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL };
       +@@ -105,7 +107,15 @@ static const Button buttons[] = {
       +         { ClkLtSymbol,          0,              Button1,        setlayout,      {0} },
       +         { ClkLtSymbol,          0,              Button3,        setlayout,      {.v = &layouts[2]} },
       +         { ClkWinTitle,          0,              Button2,        zoom,           {0} },
       +-        { ClkStatusText,        0,              Button2,        spawn,          {.v = termcmd } },
       ++        { ClkStatusText,        0,              Button1,        sigstatusbar,   {.i = 1} },
       ++        { ClkStatusText,        0,              Button2,        sigstatusbar,   {.i = 2} },
       ++        { ClkStatusText,        0,              Button3,        sigstatusbar,   {.i = 3} },
       ++        { ClkStatusText,        0,              Button4,        sigstatusbar,   {.i = 4} },
       ++        { ClkStatusText,        0,              Button5,        sigstatusbar,   {.i = 5} },
       ++        { ClkStatusText,        0,              6,              sigstatusbar,   {.i = 6} },
       ++        { ClkStatusText,        0,              7,              sigstatusbar,   {.i = 7} },
       ++        { ClkStatusText,        0,              8,              sigstatusbar,   {.i = 8} },
       ++        { ClkStatusText,        0,              9,              sigstatusbar,   {.i = 9} },
       +         { ClkClientWin,         MODKEY,         Button1,        movemouse,      {0} },
       +         { ClkClientWin,         MODKEY,         Button2,        togglefloating, {0} },
       +         { ClkClientWin,         MODKEY,         Button3,        resizemouse,    {0} },
       +diff --git a/dwm.c b/dwm.c
       +index 53b393e..dbaef27 100644
       +--- a/dwm.c
       ++++ b/dwm.c
       +@@ -171,6 +171,7 @@ static void focusstack(const Arg *arg);
       + static Atom getatomprop(Client *c, Atom prop);
       + static int getrootptr(int *x, int *y);
       + static long getstate(Window w);
       ++static pid_t getstatusbarpid(void);
       + static int gettextprop(Window w, Atom atom, char *text, unsigned int size);
       + static void grabbuttons(Client *c, int focused);
       + static void grabkeys(void);
       +@@ -204,6 +205,7 @@ static void setmfact(const Arg *arg);
       + static void setup(void);
       + static void seturgent(Client *c, int urg);
       + static void showhide(Client *c);
       ++static void sigstatusbar(const Arg *arg);
       + static void spawn(const Arg *arg);
       + static void tag(const Arg *arg);
       + static void tagmon(const Arg *arg);
       +@@ -236,6 +238,9 @@ static void zoom(const Arg *arg);
       + /* variables */
       + static const char broken[] = "broken";
       + static char stext[256];
       ++static int statusw;
       ++static int statussig;
       ++static pid_t statuspid = -1;
       + static int screen;
       + static int sw, sh;           /* X display screen geometry width, height */
       + static int bh;               /* bar height */
       +@@ -422,6 +427,7 @@ buttonpress(XEvent *e)
       +         Client *c;
       +         Monitor *m;
       +         XButtonPressedEvent *ev = &e->xbutton;
       ++        char *text, *s, ch;
       + 
       +         click = ClkRootWin;
       +         /* focus monitor if necessary */
       +@@ -440,9 +446,27 @@ buttonpress(XEvent *e)
       +                         arg.ui = 1 << i;
       +                 } else if (ev->x < x + TEXTW(selmon->ltsymbol))
       +                         click = ClkLtSymbol;
       +-                else if (ev->x > selmon->ww - (int)TEXTW(stext))
       ++                else if (ev->x > selmon->ww - statusw) {
       ++                        x = selmon->ww - statusw;
       +                         click = ClkStatusText;
       +-                else
       ++                        statussig = 0;
       ++                        for (text = s = stext; *s && x <= ev->x; s++) {
       ++                                if ((unsigned char)(*s) < ' ') {
       ++                                        ch = *s;
       ++                                        *s = '\0';
       ++                                        x += TEXTW(text) - lrpad;
       ++                                        *s = ch;
       ++                                        text = s + 1;
       ++                                        if (x >= ev->x)
       ++                                                break;
       ++                                        /* End clickable section on a matching signal raw byte */
       ++                                        if (statussig == ch)
       ++                                                statussig = 0;
       ++                                        else
       ++                                                statussig = ch;
       ++                                }
       ++                        }
       ++                } else
       +                         click = ClkWinTitle;
       +         } else if ((c = wintoclient(ev->window))) {
       +                 focus(c);
       +@@ -708,9 +732,24 @@ drawbar(Monitor *m)
       + 
       +         /* draw status first so it can be overdrawn by tags later */
       +         if (m == selmon) { /* status is only drawn on selected monitor */
       ++                char *text, *s, ch;
       +                 drw_setscheme(drw, scheme[SchemeNorm]);
       +-                tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */
       +-                drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0);
       ++
       ++                x = 0;
       ++                for (text = s = stext; *s; s++) {
       ++                        if ((unsigned char)(*s) < ' ') {
       ++                                ch = *s;
       ++                                *s = '\0';
       ++                                tw = TEXTW(text) - lrpad;
       ++                                drw_text(drw, m->ww - statusw + x, 0, tw, bh, 0, text, 0);
       ++                                x += tw;
       ++                                *s = ch;
       ++                                text = s + 1;
       ++                        }
       ++                }
       ++                tw = TEXTW(text) - lrpad + 2;
       ++                drw_text(drw, m->ww - statusw + x, 0, tw, bh, 0, text, 0);
       ++                tw = statusw;
       +         }
       + 
       +         for (c = m->clients; c; c = c->next) {
       +@@ -877,6 +916,30 @@ getatomprop(Client *c, Atom prop)
       +         return atom;
       + }
       + 
       ++pid_t
       ++getstatusbarpid(void)
       ++{
       ++        char buf[32], *str = buf, *c;
       ++        FILE *fp;
       ++
       ++        if (statuspid > 0) {
       ++                snprintf(buf, sizeof(buf), "/proc/%u/cmdline", statuspid);
       ++                if ((fp = fopen(buf, "r"))) {
       ++                        fgets(buf, sizeof(buf), fp);
       ++                        while ((c = strchr(str, '/')))
       ++                                str = c + 1;
       ++                        fclose(fp);
       ++                        if (!strcmp(str, STATUSBAR))
       ++                                return statuspid;
       ++                }
       ++        }
       ++        if (!(fp = popen("pidof -s "STATUSBAR, "r")))
       ++                return -1;
       ++        fgets(buf, sizeof(buf), fp);
       ++        pclose(fp);
       ++        return strtol(buf, NULL, 10);
       ++}
       ++
       + int
       + getrootptr(int *x, int *y)
       + {
       +@@ -1644,6 +1707,20 @@ showhide(Client *c)
       +         }
       + }
       + 
       ++void
       ++sigstatusbar(const Arg *arg)
       ++{
       ++        union sigval sv;
       ++
       ++        if (!statussig)
       ++                return;
       ++        sv.sival_int = arg->i;
       ++        if ((statuspid = getstatusbarpid()) <= 0)
       ++                return;
       ++
       ++        sigqueue(statuspid, SIGRTMIN+statussig, sv);
       ++}
       ++
       + void
       + spawn(const Arg *arg)
       + {
       +@@ -2005,8 +2082,25 @@ updatesizehints(Client *c)
       + void
       + updatestatus(void)
       + {
       +-        if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext)))
       ++        if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) {
       +                 strcpy(stext, "dwm-"VERSION);
       ++                statusw = TEXTW(stext) - lrpad + 2;
       ++        } else {
       ++                char *text, *s, ch;
       ++
       ++                statusw  = 0;
       ++                for (text = s = stext; *s; s++) {
       ++                        if ((unsigned char)(*s) < ' ') {
       ++                                ch = *s;
       ++                                *s = '\0';
       ++                                statusw += TEXTW(text) - lrpad;
       ++                                *s = ch;
       ++                                text = s + 1;
       ++                        }
       ++                }
       ++                statusw += TEXTW(text) - lrpad + 2;
       ++
       ++        }
       +         drawbar(selmon);
       + }
       + 
       +-- 
       +2.52.0
       +
   DIR diff --git a/dwm.suckless.org/patches/statuscmd/index.md b/dwm.suckless.org/patches/statuscmd/index.md
       @@ -1,111 +1,135 @@
       -statuscmd
       -=========
       -
       -Description
       ------------
       -This patch adds the ability to signal a status monitor program such as
       -[dwmblocks](https://github.com/torrinfail/dwmblocks) the location and button
       -when clicking on the status bar. Alternatively, there is a version that
       -executes shell commands defined in config.h instead of using signals.
       -
       -Usage
       ------
       -Both the nosignal version and the dwmblocks version will run their respective
       -shell commands/scripts with the environment variable BUTTON set to the button
       -that was pressed.
       +# statuscmd
       +
       +Add clickable status bar sections.
       +
       +## Description
       +
       +This patch sends mouse button events to a
       +[status monitor](https://dwm.suckless.org/status_monitor/). When clicking or
       +scrolling on a section, dwm detects the section number and mouse button, then
       +sends them to the status monitor via a signal.
       +
       +The nosignal version executes shell commands defined in config.h instead of
       +using signals.
       +
       +## Usage
       +
       +Both the status monitor and nosignal versions will run their respective shell
       +commands/scripts with the environment variable `BUTTON` set to the pressed
       +button.
        
        ### With signals
       +
        Apply the statuscmd patch and set the `STATUSBAR` macro in config.h
        to the name of the status monitor.
        
       -Apply the corresponding statuscmd patch to your status monitor if there is
       -one, or extend the program on your own. Feel free to add patches for other
       -status monitors.
       +Apply the corresponding statuscmd patch to your status monitor if there is one,
       +or create one yourself. Feel free to add patches for other status monitors.
        
        #### Patching status monitors
       -* Associate each section with a signal number in the range of 1-31.
       -* When setting the status text, print each section's respective signal number
       -  as a raw byte before its text.
       -* With the 20241009 patch, printing the raw byte again at the end of block
       -        output will end the clickable region.
       -* Create a signal handler:
       -
       -        void sighandler(int signum, siginfo_t *si, void *ucontext)
       -        {
       -                int signal = signum - SIGRTMIN;
       -                int button = si->si_value.sival_int; /* if button is zero, the signal is not from a button press */
       -                ... /* do whatever you want */
       -        }
       -
       -* Register the signal handler for each section in the following way, with
       -  'signal' being the same signal from the first step:
       -
       -        struct sigaction sa = { .sa_sigaction = sighandler, .sa_flags = SA_SIGINFO };
       -        sigaction(SIGRTMIN+signal, &sa, NULL);
       +
       +1. Associate each section with a signal number in the range of 1-31.
       +2. Print each section's signal number as a raw byte before its text.
       +   * Print the signal number again after the text to end the clickable region.
       +3. Create a signal handler:
       +
       +        void sighandler(int signum, siginfo_t *si, void *ucontext)
       +        {
       +            int signal = signum - SIGRTMIN;
       +            /* if button is zero, the signal is not from a button press */
       +            int button = si->si_value.sival_int;
       +            ... /* do whatever you want */
       +        }
       +
       +4. Register the signal handler for each section, with `signal` set to the
       +   section number:
       +
       +        struct sigaction sa = {
       +            .sa_sigaction = sighandler,
       +            .sa_flags = SA_SIGINFO
       +        };
       +        sigaction(SIGRTMIN+signal, &sa, NULL);
        
        ### Without signals
       +
        Apply the statuscmd-nosignal patch and fill the `statuscmds` array in config.h
        with `StatusCmd` structs, which take a shell command string and an integer
        identifier.
        
       -When setting the status, print the integer identifier as a raw byte before its
       +When setting the status, print the section number as a raw byte before its
        respective text.
        
        For example, with `statuscmds` defined as such:
        
       -        static const StatusCmd statuscmds[] = {
       -                { "volume",  1 },
       -                { "cpu",     2 },
       -                { "battery", 3 },
       -        };
       +    static const StatusCmd statuscmds[] = {
       +        { "volume",  1 },
       +        { "cpu",     2 },
       +        { "battery", 3 },
       +    };
        
        And root name set like this:
        
       -        xsetroot -name "$(printf '\x01 Volume \x01|\x02 CPU \x02|\x03 Battery\x03')"
       +    xsetroot -name "$(printf '\01 Volume \01|\02 CPU \02|\03 Battery \03')"
        
       -Clicking on 'Volume |' would run `volume`, clicking on ' CPU |'
       -would run `cpu` and clicking on ' Battery' would run `battery`.
       +Clicking on ' Volume ' would run `volume`, clicking on ' CPU '
       +would run `cpu` and clicking on ' Battery ' would run `battery`.
       +
       +## Example
        
       -Example
       --------
        A script run from dwm or dwmblocks with this patch might look like this:
        
       -        #!/bin/sh
       +    #!/bin/sh
       +
       +    case $BUTTON in
       +        1) notify-send 'CPU usage' "$(ps axch -o cmd,%cpu --sort=-%cpu | head)" ;;
       +        3) st -e htop ;;
       +    esac
        
       -        case $BUTTON in
       -                1) notify-send "CPU usage" "$(ps axch -o cmd,%cpu --sort=-%cpu | head)" ;;
       -                3) st -e htop ;;
       -        esac
       +    printf '\01Click Me!\01'
        
       -        printf '\x01Click Me!\x01'
       +* Handle mouse buttons in a switch statement:
       +  * `1`: left button
       +  * `2`: middle button (pressing the scroll wheel)
       +  * `3`: right button
       +  * `4`: scrolling up
       +  * `5`: scrolling down
       +  * `6`: scrolling left
       +  * `7`: scrolling right
       +  * `8`: 4th button (backward)
       +  * `9`: 5th button (forwards)
       +* Print the status text surrounded by its section number as a raw byte.
       +  * If using printf, use octal codes as they're POSIX compliant. To represent
       +    signal 30, use `\036`.
        
       -Notes
       ------
       -The signal version is not compatible with OpenBSD since it relies on `sigqueue`.
       +## Notes
        
       -Be careful with newline characters in the status text since '\n' is equal to
       -'\x0a', which is a valid signal number. The problem where having certain
       -undrawable characters in the status bar can make dwm laggy is fixed since dwm
       -will not attempt to draw them with this patch.
       +* The signal patch doesn't work on OpenBSD since it relies on `sigqueue`.
       +* Newline characters (`\n`, `\012`, `\x0a`) get interpreted as a valid signal
       +  number 10.
       +* This patch skips over undrawable characters when rendering the status bar
       +  text, which fixes a problem that makes dwm lag when trying to draw them.
       +
       +## Download
        
       -Download
       ---------
        ### dwm patches
       -* [dwm-statuscmd-20210405-67d76bd.diff](dwm-statuscmd-20210405-67d76bd.diff)
       +
       +* [dwm-statuscmd-20260124-a9aa0d8.diff](./dwm-statuscmd-20260124-a9aa0d8.diff)
        * [dwm-statuscmd-20241009-8933ebc.diff](./dwm-statuscmd-20241009-8933ebc.diff)
       -* [dwm-statuscmd-nosignal-20210402-67d76bd.diff](dwm-statuscmd-nosignal-20210402-67d76bd.diff)
       +* [dwm-statuscmd-20210405-67d76bd.diff](./dwm-statuscmd-20210405-67d76bd.diff)
       +* [dwm-statuscmd-nosignal-20210402-67d76bd.diff](./dwm-statuscmd-nosignal-20210402-67d76bd.diff)
        
       -If using [status2d](https://dwm.suckless.org/patches/status2d/), use these patches instead of the
       -above ones on top of a build already patched with status2d:
       +When using [status2d](https://dwm.suckless.org/patches/status2d/), apply these
       +patches instead after patching status2d:
        
       -* [dwm-statuscmd-status2d-20210405-60bb3df.diff](dwm-statuscmd-status2d-20210405-60bb3df.diff)
       -* [dwm-statuscmd-nosignal-status2d-20210402-60bb3df.diff](dwm-statuscmd-nosignal-status2d-20210402-60bb3df.diff)
       +* [dwm-statuscmd-status2d-20210405-60bb3df.diff](./dwm-statuscmd-status2d-20210405-60bb3df.diff)
       +* [dwm-statuscmd-nosignal-status2d-20210402-60bb3df.diff](./dwm-statuscmd-nosignal-status2d-20210402-60bb3df.diff)
        
        ### Status monitor patches
       -* [dwmblocks-statuscmd-20210402-96cbb45.diff](dwmblocks-statuscmd-20210402-96cbb45.diff)
       +
       +* [dwmblocks-statuscmd-20210402-96cbb45.diff](./dwmblocks-statuscmd-20210402-96cbb45.diff)
        * [gocaudices](https://github.com/LordRusk/gocaudices/tree/master/patches/statuscmd)
        
       -Author
       -------
       +## Authors
       +
        * Daniel Bylinka - <daniel.bylinka@gmail.com>
       -* Justinas Grigas - <dev@jstnas.com> (20241009)
       +* Justinas Grigas - <dev@jstnas.com>