[wiki] [sites] dwm statuscmd: support all mouse buttons || Justinas Grigas

From: <git_AT_suckless.org>
Date: Sat, 24 Jan 2026 17:38:59 +0100

commit 4bebceb71fc1d9e78cf1a04a7fd55b4bb5e8680d
Author: Justinas Grigas <dev_AT_jstnas.com>
Date: Sat Jan 24 16:36:32 2026 +0000

    dwm statuscmd: support all mouse buttons
    
    Added support for all mouse buttons.
    Fixed getstatusbarpid declaration.
    Reworded and reformatted the index file.

diff --git a/dwm.suckless.org/patches/statuscmd/dwm-statuscmd-20260124-a9aa0d8.diff b/dwm.suckless.org/patches/statuscmd/dwm-statuscmd-20260124-a9aa0d8.diff
new file mode 100644
index 00000000..09a962e7
--- /dev/null
+++ b/dwm.suckless.org/patches/statuscmd/dwm-statuscmd-20260124-a9aa0d8.diff
_AT_@ -0,0 +1,221 @@
+From 5c4db00890ed6f967dc14b80e704a0cd89faa209 Mon Sep 17 00:00:00 2001
+From: Justinas Grigas <dev_AT_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
+_AT_@ -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 };
+_AT_@ -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
+_AT_@ -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);
+_AT_@ -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);
+_AT_@ -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 */
+_AT_@ -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 */
+_AT_@ -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 = '++ 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);
+_AT_@ -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 = '++ 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) {
+_AT_@ -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)
+ {
+_AT_@ -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)
+ {
+_AT_@ -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 = '++ statusw += TEXTW(text) - lrpad;
++ *s = ch;
++ text = s + 1;
++ }
++ }
++ statusw += TEXTW(text) - lrpad + 2;
++
++ }
+ drawbar(selmon);
+ }
+
+--
+2.52.0
+
diff --git a/dwm.suckless.org/patches/statuscmd/index.md b/dwm.suckless.org/patches/statuscmd/index.md
index 830e96c5..f7b9d3b8 100644
--- a/dwm.suckless.org/patches/statuscmd/index.md
+++ b/dwm.suckless.org/patches/statuscmd/index.md
_AT_@ -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 ' Volume | CPU | Battery ')"
 
-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 'Click Me!'
 
- 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 ``.
 
-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 '
' 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 (`
`, `
`, `\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_AT_gmail.com>
-* Justinas Grigas - <dev_AT_jstnas.com> (20241009)
+* Justinas Grigas - <dev_AT_jstnas.com>
Received on Sat Jan 24 2026 - 17:38:59 CET

This archive was generated by hypermail 2.3.0 : Sat Jan 24 2026 - 17:48:43 CET