commit 1bcd796d6092c06317a620a97d96d865de22293e
Author: MrJensK <jens.se@icloud.com>
AuthorDate: Thu May 14 12:19:13 2026 +0200
Commit: MrJensK <jens.se@icloud.com>
CommitDate: Thu May 14 12:19:13 2026 +0200
Add scroll command functionality to modules in configuration
---
default_sxbarc | 8 ++++++++
src/defs.h | 2 ++
src/parser.c | 24 ++++++++++++++++--------
src/sxbar.c | 11 +++++++++--
4 files changed, 35 insertions(+), 10 deletions(-)
diff --git a/default_sxbarc b/default_sxbarc
index c612c61..4036cef 100644
--- a/default_sxbarc
+++ b/default_sxbarc
@@ -47,6 +47,14 @@ module : cpu : false : 3
# click : clock : "xclock"
# click : battery : "xterm -e 'upower -i /org/freedesktop/UPower/devices/battery_BAT0; read'"
# click : network : "xterm -e nmtui"
+#
+# Scroll wheel — scroll up/down over a module to run a command
+# scroll_up : module_name : "command"
+# scroll_down : module_name : "command"
+#
+# Examples:
+# scroll_up : volume : "wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+"
+# scroll_down : volume : "wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"
# Media controller example (requires playerctl)
# playerctl uses the MPRIS2 protocol and works with most players automatically:
diff --git a/src/defs.h b/src/defs.h
index 03533a2..01d4168 100644
--- a/src/defs.h
+++ b/src/defs.h
@@ -13,6 +13,8 @@ typedef struct Module {
char *name;
char *command;
char *click_command;
+ char *scroll_up_command;
+ char *scroll_down_command;
int enabled;
int refresh_interval;
time_t last_update;
diff --git a/src/parser.c b/src/parser.c
index 2c0ae2e..0c56eea 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -258,11 +258,11 @@ int parse_config(Config *cfg)
m->refresh_interval = interval;
m->last_update = 0;
m->cached_output = NULL;
- } else if (!strcmp(key, "click")) {
- /* click : module_name : "command" */
+ } else if (!strcmp(key, "click") || !strcmp(key, "scroll_up") || !strcmp(key, "scroll_down")) {
+ /* click/scroll_up/scroll_down : module_name : "command" */
char *p1 = strchr(rest, ':');
if (!p1) {
- fprintf(stderr, "sxbarc:%d: click missing name and command\n", lineno);
+ fprintf(stderr, "sxbarc:%d: %s missing name and command\n", lineno, key);
continue;
}
*p1 = '\0';
@@ -270,25 +270,33 @@ int parse_config(Config *cfg)
char *after = strip(p1 + 1);
if (*after != '"' && *after != '\'') {
- fprintf(stderr, "sxbarc:%d: click command must be quoted\n", lineno);
+ fprintf(stderr, "sxbarc:%d: %s command must be quoted\n", lineno, key);
continue;
}
char q = *after;
char *cmd_start = after + 1;
char *closing = strchr(cmd_start, q);
if (!closing) {
- fprintf(stderr, "sxbarc:%d: click command missing closing quote\n", lineno);
+ fprintf(stderr, "sxbarc:%d: %s command missing closing quote\n", lineno, key);
continue;
}
*closing = '\0';
Module *m = find_module(cfg, name);
if (!m) {
- fprintf(stderr, "sxbarc:%d: click: unknown module '%s'\n", lineno, name);
+ fprintf(stderr, "sxbarc:%d: %s: unknown module '%s'\n", lineno, key, name);
continue;
}
- free(m->click_command);
- m->click_command = expand_home(cmd_start);
+ if (!strcmp(key, "click")) {
+ free(m->click_command);
+ m->click_command = expand_home(cmd_start);
+ } else if (!strcmp(key, "scroll_up")) {
+ free(m->scroll_up_command);
+ m->scroll_up_command = expand_home(cmd_start);
+ } else {
+ free(m->scroll_down_command);
+ m->scroll_down_command = expand_home(cmd_start);
+ }
} else {
fprintf(stderr, "sxbarc:%d: unknown option '%s'\n", lineno, key);
}
diff --git a/src/sxbar.c b/src/sxbar.c
index 0d67ab1..8831f00 100644
--- a/src/sxbar.c
+++ b/src/sxbar.c
@@ -89,6 +89,8 @@ void cleanup_modules(void)
free(config.modules[i].name);
free(config.modules[i].command);
free(config.modules[i].click_command);
+ free(config.modules[i].scroll_up_command);
+ free(config.modules[i].scroll_down_command);
free(config.modules[i].cached_output);
}
free(config.modules);
@@ -384,7 +386,8 @@ static void spawn(const char *cmd)
void hdl_button(XEvent *xev)
{
- if (xev->xbutton.button != Button1)
+ unsigned int btn = xev->xbutton.button;
+ if (btn != Button1 && btn != Button4 && btn != Button5)
return;
int idx = find_window_monitor(xev->xbutton.window);
@@ -409,8 +412,12 @@ void hdl_button(XEvent *xev)
char *out = config.modules[i].cached_output;
int tw = XTextWidth(font, out, strlen(out));
if (x_click >= mx && x_click < mx + tw + mod_sp) {
- if (config.modules[i].click_command)
+ if (btn == Button1 && config.modules[i].click_command)
spawn(config.modules[i].click_command);
+ else if (btn == Button4 && config.modules[i].scroll_up_command)
+ spawn(config.modules[i].scroll_up_command);
+ else if (btn == Button5 && config.modules[i].scroll_down_command)
+ spawn(config.modules[i].scroll_down_command);
return;
}
mx += tw + mod_sp;