commit 1b2a2b5415e68d75daf304394202c004fca5549b
Author: MrJensK <jens.se@icloud.com>
AuthorDate: Fri Jul 31 13:37:42 2026 +0200
Commit: MrJensK <jens.se@icloud.com>
CommitDate: Fri Jul 31 13:37:42 2026 +0200
Unify built-in and custom modules; add startmenu/demo_menu scripts
Every module now resolves to scripts/<name>.sh the same way, whether
sxbar ships it or the user wrote it -- the `custom` directive and the
hardcoded built-in module list are gone, replaced by one `module :`
directive that creates modules on demand and lets any script self-declare
its popup via a `menu` subcommand. Also adds scripts/startmenu.sh (a
favorites launcher popup) and scripts/demo_menu.sh (a runnable reference
for every popup row type), and updates default_sxbarc, the wiki, README,
and the man page to match.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
---
README.md | 82 ++++++++++++++++++++
default_sxbarc | 214 +++++++++++++++++----------------------------------
docs/wiki.html | 93 +++++++++++++---------
scripts/demo_menu.sh | 48 ++++++++++++
scripts/startmenu.sh | 22 ++++++
src/parser.c | 108 +++++++++++---------------
src/sxbar.c | 76 ++----------------
sxbar.1 | 49 +++++++-----
8 files changed, 364 insertions(+), 328 deletions(-)
diff --git a/README.md b/README.md
index e384fa3..2b4152b 100644
--- a/README.md
+++ b/README.md
@@ -1026,6 +1026,88 @@ together with `cursor-dpi-fix.patch` in the order `install.sh` uses.
that honours `_NET_ACTIVE_WINDOW` requests -- for sxwm specifically,
that means the patch above.
+## Two more self-contained module scripts: a start menu and an every-row-type demo
+
+Two more scripts joining the built-in set, both using the
+`<script> menu`-declares-its-own-popup convention `scripts/usermenu.sh`
+already established, rather than needing any popup content in sxbarc.
+
+### Changes
+- New `scripts/startmenu.sh`: bar text is a static `"Start"` label; its
+ `menu` subcommand declares a hover popup of favorite apps/scripts as
+ button rows (Terminal, File manager, Web browser, Lock screen by
+ default). Adding, removing or reordering favorites is a one-line edit to
+ the script's own `popup_item` lines -- no sxbarc editing needed, same as
+ editing `usermenu.sh`'s Sleep/Log out/Shut down rows.
+- New `scripts/demo_menu.sh`: a runnable reference combining one of every
+ popup row type in a single popup -- `popup_info` (plain text),
+ `popup_image` (picks whatever icon it can find under
+ `/usr/share/pixmaps/` or `/usr/share/icons/hicolor/48x48/apps/`, empty
+ if none, same "no output" convention as `media.sh art`), `popup_item`,
+ `popup_set` (slider), and `popup_buttons` (three segments). The
+ button/slider rows fire a desktop notification via `scripts/demo_popup.sh`
+ (already shipped, previously wired up as three separate sxbarc
+ directives) so all five row types can be exercised safely before
+ pointing a real module's popup at anything.
+
+### Result
+- `module : startmenu : true : 3600` is a working start menu.
+- `module : demo_menu : true : 999` is a hands-on reference for every
+ popup row type sxbar supports, in one place.
+
+## No more built-in vs. custom modules -- every module resolves from scripts/
+
+Built-in modules (the fixed list `add_builtin_module()` registered in
+`init_modules()`, `src/sxbar.c`) and custom modules (the `custom : name :
+"command" : interval` directive, `src/parser.c`) worked differently:
+built-ins resolved to a script by name and could self-declare a popup via
+`<script> menu`; custom modules ran an arbitrary quoted command and (until
+this change) had no such mechanism. Not a distinction worth keeping --
+`scripts/startmenu.sh`/`demo_menu.sh` above are exactly as legitimate a
+module as `clock.sh`, just not pre-registered in the binary. This removes
+the split entirely: one `module :` directive, one resolution mechanism,
+for every module.
+
+### Changes
+- `resolve_script(name)` -- checks `~/.config/sxbar/scripts/<name>.sh`,
+ then `$PREFIX/share/sxbar/scripts/<name>.sh`, else a no-op `:` -- moved
+ from `sxbar.c` to `parser.c` (`static`, same behaviour) so the `module`
+ directive's parsing can call it directly.
+- `module : name : enabled : interval` (`src/parser.c`) now creates the
+ module on the spot if `name` isn't already registered, instead of
+ erroring `unknown builtin module`: resolves the script via
+ `resolve_script()`, then self-declares its popup via
+ `load_popup_from_script()` -- the exact sequence `add_builtin_module()`
+ used to run at startup for the fixed list, now run lazily for whichever
+ names sxbarc actually mentions.
+- The `custom` directive is gone. A one-off shell pipeline that used to be
+ a `custom` line now needs an actual script file, e.g.
+ `~/.config/sxbar/scripts/temp.sh` containing `sensors | grep 'Package' |
+ awk '{print $4}'`, enabled with `module : temp : true : 5` like anything
+ else. An old config's `custom :` lines are now an unrecognized directive
+ (a harmless per-line warning, config parsing continues).
+- `add_builtin_module()` and the eleven `add_builtin_module("clock", ...)`
+ etc. calls are gone from `init_modules()` (`src/sxbar.c`), which now
+ just allocates the (still growable) `config.modules` array and returns
+ -- every module, including the ones sxbar ships a script for, is created
+ entirely from sxbarc's `module :` lines via the mechanism above.
+ `default_sxbarc` is unchanged in what it enables by default, since it
+ already had a `module :` line for each of them.
+
+### Result
+- Writing your own module is now genuinely identical to using a shipped
+ one: put a script at `~/.config/sxbar/scripts/<name>.sh`, add
+ `module : <name> : true : <interval>` to sxbarc. No `custom` syntax to
+ learn, no separate mental model for "mine" vs. "sxbar's".
+- A config with no `module :` lines at all now starts with zero modules
+ (previously the eleven built-ins would still be pre-registered,
+ disabled/enabled per their compiled-in defaults, even with an empty or
+ missing sxbarc) -- config now fully determines what exists, not just
+ what's turned on.
+- A typo'd module name (`module : cclock : true : 1`) no longer errors --
+ it silently resolves to the no-op fallback, same as any other name
+ without a matching script.
+
## Disk footprint
- `sxbar` is lightweight: the compiled binary is about 36 KB without
diff --git a/default_sxbarc b/default_sxbarc
index e44c983..9c2ab4a 100644
--- a/default_sxbarc
+++ b/default_sxbarc
@@ -45,10 +45,22 @@ font : monospace:size=8
# workspace_icon : 2 : "" # terminal
# workspace_icon : 3 : "" # code
-# Built-in modules
+# Modules -- any name works here, not just the ones listed below. A
+# module's command is resolved by name: your own script at
+# ~/.config/sxbar/scripts/<name>.sh wins if it exists, else the reference
+# copy `make install` places at $(PREFIX)/share/sxbar/scripts/<name>.sh,
+# else the module is a silent no-op. There's no separate "custom module"
+# directive -- a module you write yourself is exactly as first-class as
+# one sxbar ships a script for; both are just a script in scripts/,
+# referenced the same way:
+#
# module : name : enabled : refresh_interval_seconds
-# Available: clock, date, battery, volume, cpu, brightness, bluetooth,
-# usermenu, network, media, taskbar
+#
+# The modules below are the ones sxbar ships a script for out of the box
+# (scripts/clock.sh, scripts/date.sh, ...; see "Reference scripts" in the
+# wiki for the full list). Each also self-declares its own popup (if it
+# has one) straight from its script -- see the `menu` subcommand note
+# under "Floating popups" further down.
# clock/date: hover over either to reveal an "Open calendar" shortcut (needs gsimplecal)
module : clock : true : 1
module : date : true : 60
@@ -75,19 +87,26 @@ module : media : false : 2
# actually focus what you click.
module : taskbar : false : 1
-# Custom script modules (like polybar exec)
-# Command must be quoted. Output is displayed in the bar.
-# custom : label : "command or script path" : refresh_interval_seconds
+# Your own modules -- write a script, drop it in ~/.config/sxbar/scripts/
+# as <name>.sh (executable), then enable it exactly like any module above:
#
-# Examples:
-# custom : temp : "sensors | grep 'Package' | awk '{print $4}'" : 5
-# custom : mem : "free -h | awk '/^Mem:/{print $3\"/\"$2}'" : 10
-# custom : netscript : "~/.config/sxbar/scripts/network.sh" : 5
-# custom : updates : "checkupdates | wc -l | tr -d ' '" : 300
+# module : temp : true : 5
+#
+# ...with ~/.config/sxbar/scripts/temp.sh being e.g.:
+# #!/bin/sh
+# sensors | grep 'Package' | awk '{print $4}'
+#
+# Two ready-made examples ship in scripts/ to copy from:
+#
+# module : startmenu : true : 3600 # your favorites as popup buttons --
+# # edit the popup_item lines in
+# # startmenu.sh itself to customize
+# module : demo_menu : true : 999 # one of every popup row type, see
+# # "Floating popups" further down
-# Prefix / icon — text prepended to a module's output (works for both
-# built-in and custom modules). Needs a Nerd Font set via `font` above to
-# render icon glyphs correctly.
+# Prefix / icon — text prepended to a module's output (works for any
+# module, by name). Needs a Nerd Font set via `font` above to render icon
+# glyphs correctly.
# prefix : module_name : "icon text"
# (icon : ... is accepted as an alias for prefix)
#
@@ -122,26 +141,18 @@ module : taskbar : false : 1
# and are installed to $(PREFIX)/share/sxbar/scripts/ -- copy them to
# ~/.config/sxbar/scripts/ (the path above) and edit from there.
#
-# The built-in modules' own bar text/popup commands work the same way,
-# under the hood: each resolves to a script in scripts/ (one per module --
-# cpu.sh, network.sh, battery.sh, volume.sh, brightness.sh, bluetooth.sh,
-# usermenu.sh, clock.sh, date.sh), checked in this order: your own copy at
-# ~/.config/sxbar/scripts/<name>.sh, then the installed system copy, else
-# a harmless no-op. Copy any of them over and edit -- no config line or
-# recompile needed, sxbar just picks up your copy on the next restart.
-#
-# Each of these scripts is also self-contained about its own popup menu:
-# running it as `<script> menu` (which sxbar does once at startup) prints
-# that module's popup/popup_item/popup_info/popup_set lines -- the exact
-# same directives as below, just without the module-name field, since a
-# script only ever describes itself. This is what usermenu.sh's Sleep/Log
-# out/Shut down rows are, for instance -- add, remove or edit rows there
-# to customize your user menu, no sxbarc editing needed. sxbarc's own
+# Any module's script is also self-contained about its own popup menu:
+# running it as `<script> menu` (which sxbar does once at startup, for
+# every module -- see "Modules"/"Your own modules" above) prints that
+# module's popup/popup_item/popup_info/popup_set lines -- the exact same
+# directives as below, just without the module-name field, since a script
+# only ever describes itself. This is what usermenu.sh's Sleep/Log out/
+# Shut down rows are, for instance -- add, remove or edit rows there to
+# customize your user menu, no sxbarc editing needed. sxbarc's own
# popup_item/popup_info/popup_set lines further down still work exactly as
# before, and still replace a module's rows on the first line for that
-# module -- whether those rows came from the compiled-in fallback or a
-# script no longer matters, sxbarc always wins if you use it. See
-# "Floating popups" below for the full directive reference.
+# module regardless of where those rows came from. See "Floating popups"
+# below for the full directive reference.
# Icon only -- hides a module's own text, showing just its prefix/icon.
# Needs a prefix/prefix_cmd set (see above) or there's nothing left to show.
@@ -243,14 +254,14 @@ module : taskbar : false : 1
# colour : network : #2ee6d6
# Click commands — run a command when a module is left-clicked
-# Works for both built-in modules and custom modules
+# Works for any module, by name
# click : module_name : "command"
#
# Examples:
# click : volume : "pavucontrol"
# click : clock : "xclock"
# click : battery : "xterm -e 'upower -i /org/freedesktop/UPower/devices/battery_BAT0; read'"
-# click : netscript : "xterm -e nmtui" # or use the built-in `network` module's popup instead
+# click : network : "xterm -e nmtui" # or use the built-in `network` module's own popup instead
#
# Scroll wheel — scroll up/down over a module to run a command
# scroll_up : module_name : "command"
@@ -263,10 +274,10 @@ module : taskbar : false : 1
# Floating popups -- a module can open a small floating window instead of
# (or as well as) running a plain click_command. A popup is just a list of
# rows, and text/button/slider/image rows can all be freely mixed in the
-# same popup (see demo_menu below for an example combining the first
-# three). Either trigger works with any row -- every built-in popup below
-# defaults to `hover` for a consistent feel, but `click` remains available
-# if you'd rather a menu only appear on a deliberate click:
+# same popup (see scripts/demo_menu.sh, further down, for an example
+# combining all five). Either trigger works with any row -- every shipped
+# module's popup defaults to `hover` for a consistent feel, but `click`
+# remains available if you'd rather a menu only appear on a deliberate click:
# text -- purely informational, not clickable at all (popup_info)
# button -- runs its own command on click; the popup stays open (popup_item)
# slider -- a draggable 0-100% track; dragging doesn't close the popup (popup_set)
@@ -317,8 +328,9 @@ module : taskbar : false : 1
# one row split into N equal-width button segments, each running its own
# command on click; the popup stays open -- same behaviour as popup_item
# but laid out side by side instead of stacked. Needs an even number of
-# quoted label/command pairs; glyphs work well as labels here (see the
-# media example below) so you get an icon row instead of stacked text.
+# quoted label/command pairs; glyphs work well as labels here (see
+# scripts/media.sh's own popup) so you get an icon row instead of
+# stacked text.
#
# popup_set : module_name : "command" -- adds (or updates) one slider row
# for this module. Run when the value changes; receives the new value as
@@ -327,109 +339,21 @@ module : taskbar : false : 1
# separate "get" command is needed. Can coexist with popup_item/popup_info/
# popup_image/popup_buttons rows on the same module.
#
-# All ten built-in modules below already come with sensible popup
-# defaults built in (see README) -- these directives are for overriding
-# them or adding this behaviour to your own custom modules. Want click
-# instead of hover for one of them? Just override its trigger, e.g.
-# `popup : usermenu : click : buttons`.
-#
-# The built-in modules' own commands (bar text, popup rows, slider
-# set-commands) all resolve to a script under scripts/ -- one script per
-# module, dispatched by subcommand where a module needs more than one
-# piece of output (e.g. cpu.sh handles bar text, its usage/mem/cores
-# popup rows). Copy any of them to ~/.config/sxbar/scripts/ and edit
-# freely, same as prefix_cmd scripts -- see README for the full list.
-#
-# Examples (these already match each module's own script default -- see
-# "Built-in modules" further up -- so uncommenting one as-is changes
-# nothing; they're here as a starting point to override from, e.g. to
-# reorder rows, add more, or point usermenu's "Log out" at your own
-# WM/session. Paths assume you've copied the scripts to
-# ~/.config/sxbar/scripts/, same as the prefix_cmd examples above):
-# popup : clock : hover : buttons
-# popup_item : clock : "Open calendar" : "gsimplecal"
-#
-# popup : date : hover : buttons
-# popup_item : date : "Open calendar" : "gsimplecal"
-#
-# popup : battery : hover : buttons
-# popup_info : battery : "~/.config/sxbar/scripts/battery.sh status"
-# popup_item : battery : "Toggle power saver" : "~/.config/sxbar/scripts/battery.sh toggle-powersave"
-#
-# popup : volume : hover : slider
-# popup_set : volume : "~/.config/sxbar/scripts/volume.sh set"
-#
-# popup : cpu : hover : buttons
-# popup_info : cpu : "~/.config/sxbar/scripts/cpu.sh usage 'CPU: '"
-# popup_info : cpu : "~/.config/sxbar/scripts/cpu.sh mem"
-# popup_info : cpu : "~/.config/sxbar/scripts/cpu.sh cores"
-#
-# popup : brightness : hover : slider
-# popup_set : brightness : "~/.config/sxbar/scripts/brightness.sh set"
-#
-# popup : bluetooth : hover : buttons
-# popup_item : bluetooth : "Turn on" : "bluetoothctl power on"
-# popup_item : bluetooth : "Turn off" : "bluetoothctl power off"
-# popup_item : bluetooth : "Search for devices" : "bluetoothctl --timeout 10 scan on"
-# popup_item : bluetooth : "Pair last found device" : "~/.config/sxbar/scripts/bluetooth.sh pair"
-#
-# popup : usermenu : hover : buttons
-# popup_item : usermenu : "Sleep" : "systemctl suspend"
-# popup_item : usermenu : "Log out" : "pkill sxwm" # adjust to your WM/session
-# popup_item : usermenu : "Shut down" : "systemctl poweroff"
-#
-# popup : network : hover : buttons
-# popup_info : network : "~/.config/sxbar/scripts/network.sh wifi"
-# popup_info : network : "~/.config/sxbar/scripts/network.sh ethernet"
-#
-# popup : media : hover : buttons
-# popup_image : media : "~/.config/sxbar/scripts/media.sh art" # album art (needs playerctl; curl for remote art)
-# popup_info : media : "~/.config/sxbar/scripts/media.sh track"
-# popup_buttons : media : "" : "~/.config/sxbar/scripts/media.sh prev" : "" : "~/.config/sxbar/scripts/media.sh playpause" : "" : "~/.config/sxbar/scripts/media.sh next"
-
-# Try-it-yourself demo popup -- a throwaway custom module combining all
-# three row kinds in one popup, so you can test hover/click/drag mechanics
-# safely before pointing popup_item/popup_info/popup_set at anything real:
-# a plain text row (not clickable at all), two buttons, and a slider. They
-# just fire a desktop notification via scripts/demo_popup.sh (installed to
-# $(PREFIX)/share/sxbar/scripts/ -- copy it to ~/.config/sxbar/scripts/,
-# same as battery_icon.sh/volume_icon.sh, then edit freely).
-#
-# custom : demo_menu : "echo 50%" : 999
-# popup : demo_menu : hover : buttons
-# popup_info : demo_menu : "echo 'Just text -- click me, nothing happens'"
-# popup_item : demo_menu : "Say hello" : "~/.config/sxbar/scripts/demo_popup.sh 'Hello!'"
-# popup_item : demo_menu : "Say bye" : "~/.config/sxbar/scripts/demo_popup.sh 'Bye!'"
-# popup_set : demo_menu : "~/.config/sxbar/scripts/demo_popup.sh"
-
-# Media controller example (requires playerctl)
-#
-# There's now a built-in `media` module (see "Built-in modules" above and
-# scripts/media.sh) with a hover popup showing album art, track info, and
-# Previous/Play-Pause/Next buttons -- `module : media : true : 2` is
-# usually the easier route. The four-segment custom-module version below
-# is still here for anyone who wants transport controls always visible
-# directly in the bar instead, with no popup involved.
-#
-# playerctl uses the MPRIS2 protocol and works with most players automatically:
-# Firefox (YouTube, Spotify Web), Spotify, VLC, mpv, rhythmbox, etc.
-# It will control whichever player was most recently active.
-#
-# Shows: << > >> Artist - Title
-#
-# custom : media_prev : "echo ' << '" : 999
-# custom : media_play : "playerctl status 2>/dev/null | sed 's/Playing/ > /;s/Paused/ || /;s/Stopped/ [] /'" : 1
-# custom : media_next : "echo ' >> '" : 999
-# custom : media_track : "playerctl metadata --format ' {{artist}} - {{title}} ' 2>/dev/null || echo ' - '" : 2
-#
-# click : media_prev : "playerctl previous"
-# click : media_play : "playerctl play-pause"
-# click : media_next : "playerctl next"
-#
-# If you have multiple players running at the same time and want to always
-# control the one that is currently playing (not just the most recent):
-#
-# custom : media_play : "playerctl -p $(playerctl -l 2>/dev/null | head -1) status 2>/dev/null | sed 's/Playing/ > /;s/Paused/ || /;s/Stopped/ [] /'" : 1
-#
-# List all available players: playerctl -l
+# Every module listed under "Modules" above already comes with sensible
+# popup defaults built into its own script (see the `menu` subcommand note
+# above) -- these directives are for overriding them, or adding this
+# behaviour to a module of your own. Want click instead of hover for one
+# of them? Just override its trigger, e.g. `popup : usermenu : click :
+# buttons`. For the full reference of what each shipped module's default
+# popup looks like (as the equivalent sxbarc directives, for copy-paste
+# starting points), see "Built-in popups" in the wiki -- not duplicated
+# here since it's identical to each script's own `menu` output.
+#
+# scripts/demo_menu.sh (enabled via `module : demo_menu : true : 999`
+# above) is a runnable example combining one of every row type: plain text
+# (not clickable), an image, a button, a slider, and a row of segmented
+# buttons. Its button/slider rows fire a desktop notification via
+# scripts/demo_popup.sh so you can test hover/click/drag mechanics safely
+# before pointing your own module's popup at anything real -- both ship in
+# scripts/ and install to $(PREFIX)/share/sxbar/scripts/.
diff --git a/docs/wiki.html b/docs/wiki.html
index b031c0c..279d7d1 100644
--- a/docs/wiki.html
+++ b/docs/wiki.html
@@ -409,7 +409,7 @@
<ul>
<li><a href="#modules">Built-in modules</a></li>
<li><a href="#taskbar">Taskbar</a></li>
- <li><a href="#custom">Custom modules & scripts</a></li>
+ <li><a href="#custom">Any name is a module</a></li>
<li><a href="#customize">Icons, colour, layout, clicks</a></li>
</ul>
</div>
@@ -452,7 +452,7 @@
<section id="overview">
<h2>Overview</h2>
<p class="lede">sxbar renders a slim EWMH workspace switcher plus a row of
- modules — clock, battery, volume, custom scripts, anything you configure —
+ modules — clock, battery, volume, scripts you write yourself, anything you configure —
and can pop a floating slider or menu window out of any module.</p>
<p>Everything is controlled from a single config file, read from (in order)
@@ -611,15 +611,19 @@ cp /usr/local/share/sxbarc ~/.config/sxbar/sxbarc</pre>
</div>
<p>Full detail on each popup is in <a href="#popup-builtins">Built-in popups</a> below.</p>
- <div class="note"><strong>Every built-in module's command — and its popup —
- is a script</strong>, not code baked into the binary. Each resolves (in order)
- to your own copy at <code class="inline">~/.config/sxbar/scripts/<name>.sh</code>,
- then the system copy <code class="inline">make install</code> places at
- <code class="inline">/usr/local/share/sxbar/scripts/<name>.sh</code>,
+ <div class="note"><strong>Not special-cased in the binary.</strong> Every
+ module — the ones above and any you write yourself — is resolved by name
+ to a script, purely from <code class="inline">module :</code> lines in
+ sxbarc; there's no separate "custom module" concept or directive. Each
+ name resolves (in order) to your own copy at
+ <code class="inline">~/.config/sxbar/scripts/<name>.sh</code>,
+ then the reference copy <code class="inline">make install</code> places
+ at <code class="inline">/usr/local/share/sxbar/scripts/<name>.sh</code>,
or a harmless no-op if neither exists. Copy any of them over and edit —
no recompile needed, and no sxbarc editing needed either to change a
module's popup content (e.g. adding a row to <code class="inline">usermenu</code>)
- — see <a href="#custom">Reference scripts</a> below for the full list.</div>
+ — see <a href="#custom">Reference scripts</a> below for the full list, and
+ "Any name is a module" in that section for writing your own.</div>
</section>
<section id="taskbar">
@@ -659,28 +663,39 @@ cp /usr/local/share/sxbarc ~/.config/sxbar/sxbarc</pre>
</section>
<section id="custom">
- <h2>Custom modules & scripts</h2>
- <p class="lede">Any shell command or script becomes a module — its stdout is the
- bar text, refreshed on its own interval.</p>
- <pre><span class="k">custom</span> : <span class="v">temp</span> : <span class="s">"sensors | grep 'Package' | awk '{print $4}'"</span> : 5
-<span class="k">custom</span> : <span class="v">mem</span> : <span class="s">"free -h | awk '/^Mem:/{print $3\"/\"$2}'"</span> : 10
-<span class="k">custom</span> : <span class="v">updates</span> : <span class="s">"checkupdates | wc -l | tr -d ' '"</span> : 300</pre>
- <p>Custom modules take every other directive on this page too — <code class="inline">prefix</code>,
+ <h2>Any name is a module</h2>
+ <p class="lede">There's no separate "custom module" directive. A module's
+ command is resolved purely by name — write a script, name it
+ <code class="inline"><name>.sh</code>, and it's a module.</p>
+ <pre><span class="k">module</span> : <span class="v">temp</span> : <span class="v">true</span> : 5</pre>
+ <p>...with <code class="inline">~/.config/sxbar/scripts/temp.sh</code> being e.g.:</p>
+ <pre><span class="c">#!/bin/sh</span>
+sensors | grep <span class="s">'Package'</span> | awk <span class="s">'{print $4}'</span></pre>
+ <p>Resolution order: your own copy at
+ <code class="inline">~/.config/sxbar/scripts/<name>.sh</code> wins if it
+ exists, then the reference copy <code class="inline">make install</code> places
+ at <code class="inline">$PREFIX/share/sxbar/scripts/<name>.sh</code>, else
+ the module is a silent no-op — so a typo'd module name just shows nothing
+ rather than erroring. A module you write yourself takes every other
+ directive on this page too — <code class="inline">prefix</code>,
<code class="inline">colour</code>, <code class="inline">click</code>,
- <code class="inline">popup</code>, and so on — just referenced by whatever name
- you gave the <code class="inline">custom</code> line.</p>
+ <code class="inline">popup</code>, and so on — exactly like any module sxbar
+ ships a script for; there's no distinction between the two once
+ <code class="inline">module :</code> has resolved a name to a script. If the
+ script understands a <code class="inline">menu</code> subcommand, it can also
+ declare its own popup, exactly like the reference scripts below — see the
+ note under the table.</p>
<h3>Reference scripts</h3>
<p>Every script sxbar ships with lives in <code class="inline">scripts/</code> and
installs to <code class="inline">$PREFIX/share/sxbar/scripts/</code> as a
reference copy — copy any of them to <code class="inline">~/.config/sxbar/scripts/</code>
- and point your config (or nothing, for the built-in module scripts —
- see the note above) at your own copy so you can edit freely.</p>
+ and edit your own copy freely, no sxbarc changes needed for the name to keep
+ resolving to it.</p>
- <p>One script per built-in module, dispatched by subcommand where a
- module needs more than one piece of output. Every one of them also
- answers a <code class="inline">menu</code> subcommand — see the note
- below the table.</p>
+ <p>One script per module, dispatched by subcommand where a module needs
+ more than one piece of output. Every one of them also answers a
+ <code class="inline">menu</code> subcommand — see the note below the table.</p>
<div class="twrap">
<table>
<thead><tr><th class="mono">script</th><th>subcommands</th></tr></thead>
@@ -700,8 +715,9 @@ cp /usr/local/share/sxbarc ~/.config/sxbar/sxbarc</pre>
</div>
<div class="note"><strong><code class="inline">menu</code></strong> is what
- makes a built-in module self-contained: run once at startup, it prints
- that module's <a href="#popup-builtins">popup definition</a> — the same
+ makes a module self-contained: run once at startup for every module
+ (whichever script its name resolves to), it prints that module's
+ <a href="#popup-builtins">popup definition</a> — the same
<code class="inline">popup</code>/<code class="inline">popup_item</code>/
<code class="inline">popup_info</code>/<code class="inline">popup_set</code>
directives sxbarc itself uses, minus the module-name field, since a
@@ -711,7 +727,7 @@ cp /usr/local/share/sxbarc ~/.config/sxbar/sxbarc</pre>
rather keep everything in one config file (see
<a href="#popup-rows">Row types</a>).</div>
- <p>Plus three standalone helpers:</p>
+ <p>Plus five standalone helpers:</p>
<div class="twrap">
<table>
<thead><tr><th class="mono">script</th><th>used as</th><th>does</th></tr></thead>
@@ -719,6 +735,8 @@ cp /usr/local/share/sxbarc ~/.config/sxbar/sxbarc</pre>
<tr><td class="mono">battery_icon.sh</td><td><code class="inline">prefix_cmd</code></td><td>picks a battery glyph by charge level, swaps to a bolt glyph while charging</td></tr>
<tr><td class="mono">volume_icon.sh</td><td><code class="inline">prefix_cmd</code></td><td>picks a volume glyph, mute-aware</td></tr>
<tr><td class="mono">demo_popup.sh</td><td><code class="inline">popup_item</code> / <code class="inline">popup_set</code></td><td>fires a desktop notification — a safe target for testing popup rows before wiring up real commands</td></tr>
+ <tr><td class="mono">demo_menu.sh</td><td><code class="inline">module : demo_menu : true : 999</code></td><td>self-declares one of every popup row type (text, image, button, slider, segmented buttons) via its own <code class="inline">menu</code> subcommand — a runnable reference, and a safe sandbox for testing hover/click/drag before building your own</td></tr>
+ <tr><td class="mono">startmenu.sh</td><td><code class="inline">module : startmenu : true : 3600</code></td><td>a hover popup of favorite apps as buttons — edit its <code class="inline">popup_item</code> lines to add/remove favorites, no sxbarc editing needed</td></tr>
</tbody>
</table>
</div>
@@ -744,14 +762,13 @@ status=$(cat /sys/class/power_supply/BAT*/status 2>/dev/null | head -n1)
<section id="customize">
<h2>Icons, colour, layout, clicks</h2>
- <p class="lede">These directives apply to any module — built-in or custom — by name.</p>
+ <p class="lede">These directives apply to any module, by name.</p>
<h3>Prefix / icon</h3>
<pre><span class="k">prefix</span> : module_name : <span class="s">"icon text"</span> <span class="c"># icon : ... is an accepted alias</span></pre>
<p>Static text prepended to a module's output. Needs a Nerd Font set via
- <code class="inline">font</code> to render glyphs. For <code class="inline">custom</code>
- modules, declare <code class="inline">prefix</code> after the module's
- <code class="inline">custom</code> line.</p>
+ <code class="inline">font</code> to render glyphs. Declare <code class="inline">prefix</code>
+ after the module's <code class="inline">module</code> line.</p>
<h4>Dynamic icon (<code class="inline">prefix_cmd</code>)</h4>
<pre><span class="k">prefix_cmd</span> : module_name : <span class="s">"command or script path"</span> <span class="c"># icon_cmd is an alias</span></pre>
@@ -902,14 +919,19 @@ status=$(cat /sys/class/power_supply/BAT*/status 2>/dev/null | head -n1)
<code class="inline">popup_set</code> again afterwards if you cleared a built-in
slider this way and still want it back.</p>
- <p>Example combining all five on one custom module:</p>
- <pre><span class="k">custom</span> : mymodule : <span class="s">"echo ok"</span> : 5
+ <p>Example combining all five on one module (<code class="inline">mymodule</code>
+ resolving to your own <code class="inline">~/.config/sxbar/scripts/mymodule.sh</code>):</p>
+ <pre><span class="k">module</span> : mymodule : <span class="v">true</span> : 5
<span class="k">popup</span> : mymodule : <span class="v">hover</span> : <span class="v">buttons</span>
<span class="k">popup_info</span> : mymodule : <span class="s">"echo 'status: '$(whoami)"</span>
<span class="k">popup_image</span> : mymodule : <span class="s">"echo /path/to/icon.png"</span>
<span class="k">popup_buttons</span> : mymodule : <span class="s">"⏮"</span> : <span class="s">"mymodule-ctl prev"</span> : <span class="s">"⏭"</span> : <span class="s">"mymodule-ctl next"</span>
<span class="k">popup_item</span> : mymodule : <span class="s">"Restart"</span> : <span class="s">"systemctl --user restart myservice"</span>
<span class="k">popup_set</span> : mymodule : <span class="s">"myservice-set-level"</span></pre>
+ <p>Or skip the sxbarc overrides entirely and declare the same popup from
+ <code class="inline">mymodule.sh</code> itself via its <code class="inline">menu</code>
+ subcommand — see <a href="#custom">Any name is a module</a> above and
+ <code class="inline">scripts/demo_menu.sh</code> for a runnable example of exactly this.</p>
</section>
<section id="popup-builtins">
@@ -917,7 +939,7 @@ status=$(cat /sys/class/power_supply/BAT*/status 2>/dev/null | head -n1)
<p class="lede">What each built-in module's popup contains by default. This
content isn't hardcoded in the binary — it's each module's own
<code class="inline"><script> menu</code> output (see
- <a href="#custom">Custom modules & scripts</a>), shown below as the
+ <a href="#custom">Any name is a module</a>), shown below as the
equivalent sxbarc directives for reference. Edit the script directly to
change these, or override wholesale from sxbarc with the directives
above — either works, since the first <code class="inline">popup_item</code>/
@@ -1057,8 +1079,8 @@ status=$(cat /sys/class/power_supply/BAT*/status 2>/dev/null | head -n1)
<span class="k">popup_item</span> : usermenu : <span class="s">"Log out"</span> : <span class="s">"loginctl terminate-session $XDG_SESSION_ID"</span>
<span class="k">popup_item</span> : usermenu : <span class="s">"Shut down"</span> : <span class="s">"systemctl poweroff"</span>
-<span class="c"># a custom module</span>
-<span class="k">custom</span> : mem : <span class="s">"free -h | awk '/^Mem:/{print $3\"/\"$2}'"</span> : 10
+<span class="c"># a module you wrote yourself (~/.config/sxbar/scripts/mem.sh)</span>
+<span class="k">module</span> : mem : <span class="v">true</span> : 10
<span class="k">prefix</span> : mem : <span class="s">" "</span></pre>
</section>
@@ -1070,7 +1092,6 @@ status=$(cat /sys/class/power_supply/BAT*/status 2>/dev/null | head -n1)
<thead><tr><th class="mono">directive</th><th>syntax</th></tr></thead>
<tbody>
<tr><td class="mono">module</td><td class="mono">module : name : true|false : interval</td></tr>
- <tr><td class="mono">custom</td><td class="mono">custom : name : "command" : interval</td></tr>
<tr><td class="mono">prefix</td><td class="mono">prefix : name : "text"</td></tr>
<tr><td class="mono">prefix_cmd</td><td class="mono">prefix_cmd : name : "command"</td></tr>
<tr><td class="mono">icon_only</td><td class="mono">icon_only : name : true|false</td></tr>
diff --git a/scripts/demo_menu.sh b/scripts/demo_menu.sh
new file mode 100755
index 0000000..a9ea087
--- /dev/null
+++ b/scripts/demo_menu.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+# demo_menu module -- a self-contained script demonstrating every popup
+# row type sxbar supports, all declared from this one script's `menu`
+# subcommand (see README, "Module-declared menus"):
+# popup_info -- plain text, not clickable
+# popup_image -- a picture (album art, an icon, etc.)
+# popup_item -- a full-width button
+# popup_set -- a draggable slider
+# popup_buttons -- a row of N equal-width button segments
+#
+# Wire it up as a custom module and hover over it to see them all:
+#
+# custom : demo_menu : "~/.config/sxbar/scripts/demo_menu.sh" : 999
+#
+# The button/slider rows fire a desktop notification via demo_popup.sh
+# (needs notify-send) so you can see them actually run without wiring up
+# anything real yet -- copy whichever row types you want into your own
+# module's script and point them at real commands instead.
+#
+# demo_menu.sh -> bar text
+# demo_menu.sh image -> path to a local image file for the image row --
+# picks whatever icon it can find on this system,
+# empty if none (same "no output" convention as
+# media.sh's `art` subcommand)
+# demo_menu.sh menu -> popup definition, read once at startup
+mode="${1:-text}"
+self="$0"
+dir=$(dirname "$self")
+popup="$dir/demo_popup.sh"
+
+case "$mode" in
+menu)
+ echo 'popup : hover : buttons'
+ echo 'popup_info : "Just text -- click me, nothing happens"'
+ echo "popup_image : \"$self image\""
+ echo "popup_item : \"Say hello\" : \"$popup 'Hello!'\""
+ echo "popup_set : \"$popup\""
+ echo "popup_buttons : \"One\" : \"$popup 'One!'\" : \"Two\" : \"$popup 'Two!'\" : \"Three\" : \"$popup 'Three!'\""
+ ;;
+image)
+ for f in /usr/share/pixmaps/*.png /usr/share/icons/hicolor/48x48/apps/*.png; do
+ [ -f "$f" ] && { printf '%s' "$f"; break; }
+ done
+ ;;
+*)
+ echo "Demo"
+ ;;
+esac
diff --git a/scripts/startmenu.sh b/scripts/startmenu.sh
new file mode 100755
index 0000000..f2148ed
--- /dev/null
+++ b/scripts/startmenu.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+# startmenu module -- dispatched by subcommand:
+# startmenu.sh -> bar text
+# startmenu.sh menu -> popup definition, read once at startup (see
+# README, "Module-declared menus")
+#
+# This *is* the start menu -- add, remove or reorder popup_item lines below
+# to customize it, no sxbarc editing needed.
+mode="${1:-text}"
+
+case "$mode" in
+menu)
+ echo 'popup : hover : buttons'
+ echo 'popup_item : "Terminal" : "xterm"'
+ echo 'popup_item : "File manager" : "pcmanfm"'
+ echo 'popup_item : "Web browser" : "firefox"'
+ echo 'popup_item : "Lock screen" : "slock"'
+ ;;
+*)
+ echo "Start"
+ ;;
+esac
diff --git a/src/parser.c b/src/parser.c
index 953b00a..ff8b411 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -121,6 +121,28 @@ static int grow_modules(Config *cfg)
return 0;
}
+/* resolve a module name to its script: the user's own edited copy at
+ * ~/.config/sxbar/scripts/<name>.sh wins over the installed reference copy
+ * at $PREFIX/share/sxbar/scripts/<name>.sh (currently hardcoded to
+ * /usr/local, matching the Makefile's default PREFIX), or a harmless
+ * shell no-op (`:`) if neither exists yet -- e.g. a freshly built,
+ * not-yet-installed checkout. Every module -- whether sxbar ships a
+ * script for it or the user wrote their own -- is resolved this same way. */
+static char *resolve_script(const char *name)
+{
+ char path[PATH_MAX];
+ const char *home = getenv("HOME");
+ if (home) {
+ snprintf(path, sizeof path, "%s/.config/sxbar/scripts/%s.sh", home, name);
+ if (access(path, X_OK) == 0)
+ return strdup(path);
+ }
+ snprintf(path, sizeof path, "/usr/local/share/sxbar/scripts/%s.sh", name);
+ if (access(path, X_OK) == 0)
+ return strdup(path);
+ return strdup(":");
+}
+
/* the first popup_item/popup_info line for a module wipes its built-in
* default rows (if any, including a popup_set-managed slider row); later
* lines from either directive just append. A popup_set line afterwards
@@ -676,7 +698,19 @@ int parse_config(Config *cfg)
cfg->workspace_icons[cfg->workspace_icon_count].icon = strdup(text_start);
cfg->workspace_icon_count++;
} else if (!strcmp(key, "module")) {
- /* module : name : enabled : interval */
+ /* module : name : enabled : interval
+ *
+ * There's no separate "custom module" directive -- any name
+ * works here. If a module by this name doesn't exist yet, one
+ * is created on the spot by resolving scripts/<name>.sh
+ * (resolve_script(), below): the user's own copy at
+ * ~/.config/sxbar/scripts/<name>.sh wins over the installed
+ * reference copy at $PREFIX/share/sxbar/scripts/<name>.sh, or
+ * a harmless no-op if neither exists yet. A module the user
+ * wrote themselves is exactly as first-class as one sxbar
+ * ships with -- both are just a script in scripts/, and both
+ * can self-declare a popup via a `menu` subcommand
+ * (load_popup_from_script(), below). */
char *p1 = strchr(rest, ':');
if (!p1) {
fprintf(stderr, "sxbarc:%d: module missing fields\n", lineno);
@@ -696,70 +730,22 @@ int parse_config(Config *cfg)
}
Module *m = find_module(cfg, name);
if (!m) {
- fprintf(stderr, "sxbarc:%d: unknown builtin module '%s'\n", lineno, name);
- continue;
+ if (grow_modules(cfg) < 0) {
+ fprintf(stderr, "sxbarc: out of memory\n");
+ fclose(f);
+ return -1;
+ }
+ m = &cfg->modules[cfg->module_count++];
+ memset(m, 0, sizeof *m);
+ m->name = strdup(name);
+ m->command = resolve_script(name);
+ m->refresh_interval = 5;
+ m->slider_item_idx = -1;
+ load_popup_from_script(m, m->command);
}
m->enabled = parse_bool(enabled_s);
if (interval_s && *interval_s)
m->refresh_interval = atoi(interval_s);
- } else if (!strcmp(key, "custom")) {
- /*
- * custom : name : "command" : interval
- * command must be quoted; may contain ':' or '#'
- * interval is optional (defaults to 5s)
- */
- char *p1 = strchr(rest, ':');
- if (!p1) {
- fprintf(stderr, "sxbarc:%d: custom missing name and command\n", lineno);
- continue;
- }
- *p1 = '\0';
- char *name = strip(rest);
- char *after = strip(p1 + 1);
-
- if (*after != '"' && *after != '\'') {
- fprintf(stderr, "sxbarc:%d: custom command must be quoted\n", lineno);
- continue;
- }
-
- char q = *after;
- char *cmd_start = after + 1;
- char *closing = strchr(cmd_start, q);
- if (!closing) {
- fprintf(stderr, "sxbarc:%d: custom command missing closing quote\n", lineno);
- continue;
- }
- *closing = '\0';
-
- int interval = 5;
- char *tail = strip(closing + 1);
- if (*tail == ':') {
- tail = strip(tail + 1);
- strip_comment(tail);
- if (*tail)
- interval = atoi(tail);
- }
-
- if (!*cmd_start) {
- fprintf(stderr, "sxbarc:%d: custom '%s' has empty command\n", lineno, name);
- continue;
- }
-
- if (grow_modules(cfg) < 0) {
- fprintf(stderr, "sxbarc: out of memory\n");
- fclose(f);
- return -1;
- }
-
- Module *m = &cfg->modules[cfg->module_count++];
- memset(m, 0, sizeof *m);
- m->name = strdup(name);
- m->command = expand_home(cmd_start);
- m->enabled = 1;
- m->refresh_interval = interval;
- m->last_update = 0;
- m->cached_output = NULL;
- m->slider_item_idx = -1;
} else if (!strcmp(key, "colour") || !strcmp(key, "color")) {
/* colour : module_name : #hex */
char *p1 = strchr(rest, ':');
diff --git a/src/sxbar.c b/src/sxbar.c
index 1bc62e0..639cfde 100644
--- a/src/sxbar.c
+++ b/src/sxbar.c
@@ -1587,78 +1587,18 @@ int find_bar(Window win)
return 0;
}
-/* resolve a built-in module's shell command to a script, checked in this
- * order: the user's own edited copy under ~/.config/sxbar/scripts/, then
- * the system-installed reference copy `make install` puts under
- * /usr/local/share/sxbar/scripts/ (same fallback-path convention as the
- * config file lookup in parser.c), or a harmless shell no-op if neither
- * exists yet -- e.g. running straight from a freshly built, not-yet-
- * installed checkout without having copied any scripts over */
-static char *resolve_script(const char *name)
-{
- char path[PATH_MAX];
- const char *home = getenv("HOME");
- if (home) {
- snprintf(path, sizeof path, "%s/.config/sxbar/scripts/%s.sh", home, name);
- if (access(path, X_OK) == 0)
- return strdup(path);
- }
- snprintf(path, sizeof path, "/usr/local/share/sxbar/scripts/%s.sh", name);
- if (access(path, X_OK) == 0)
- return strdup(path);
- return strdup(":");
-}
-
-/* add one built-in module: resolve its script, then let the script itself
- * declare its popup content (if any) by running `<script> menu` -- see
- * load_popup_from_script() in parser.c. This is what makes a built-in
- * module self-contained: sxbarc only ever turns it on/off and tunes its
- * refresh interval; everything about *how it behaves* (bar text, and now
- * its popup menu) lives in the module's own script. */
-static void add_builtin_module(const char *name, int enabled, int refresh_interval)
-{
- Module *m = &config.modules[config.module_count++];
- *m = (Module){.name = strdup(name),
- .command = resolve_script(name),
- .enabled = enabled,
- .refresh_interval = refresh_interval,
- .last_update = 0,
- .cached_output = NULL,
- .slider_item_idx = -1};
- load_popup_from_script(m, m->command);
-}
-
+/* every module -- whether sxbar ships a script for it or the user wrote
+ * their own -- is created on demand from sxbarc's `module :` directive
+ * (see parser.c: resolve_script(), and the `module` directive handling in
+ * parse_config()). There's no hardcoded built-in list any more: a config
+ * with no `module :` lines at all starts with zero modules. */
void init_modules(void)
{
- config.max_modules = 16; /* headroom beyond the built-ins added below --
- * grow_modules() (parser.c) takes over for any
- * further custom modules from sxbarc */
+ config.max_modules = 16; /* grow_modules() (parser.c) takes over from
+ * here as sxbarc's `module :` lines create
+ * more than this fits */
config.modules = malloc(config.max_modules * sizeof(Module));
config.module_count = 0;
-
- add_builtin_module("clock", True, 1);
- add_builtin_module("date", True, 60);
- add_builtin_module("battery", False, 30);
- add_builtin_module("volume", True, 5);
- add_builtin_module("cpu", False, 3);
- add_builtin_module("brightness", False, 5);
- add_builtin_module("bluetooth", False, 10);
- /* usermenu -- sxwm (https://github.com/uint23/sxwm) has no session
- * manager or external IPC to trigger its own `quit` keybind, so the
- * default "Log out" row (in scripts/usermenu.sh) ends the X session by
- * killing the WM -- edit your own copy under ~/.config/sxbar/scripts/
- * if this doesn't fit your WM/session, or add more rows of your own. */
- add_builtin_module("usermenu", True, 300);
- add_builtin_module("network", False, 10);
- /* media -- MPRIS controls via playerctl; popup shows album art (needs
- * curl for remote art URLs) plus Previous/Play-Pause/Next buttons */
- add_builtin_module("media", False, 2);
- /* taskbar -- one clickable entry per window on the current workspace,
- * rendered directly in the bar rather than a popup (see the dedicated
- * block in draw_bar_into()); needs wmctrl, and a window manager that
- * acts on _NET_ACTIVE_WINDOW client messages to actually focus what
- * you click (see scripts/taskbar.sh) */
- add_builtin_module("taskbar", False, 1);
}
unsigned long parse_col(const char *hex)
diff --git a/sxbar.1 b/sxbar.1
index 0d89393..08a2073 100644
--- a/sxbar.1
+++ b/sxbar.1
@@ -9,8 +9,8 @@ sxbar \- a small, fast EWMH status bar for Xorg
.SH DESCRIPTION
.B sxbar
is a single C99 binary that draws an EWMH workspace switcher plus a row of
-modules \(en clock, battery, volume, custom shell commands, anything you
-configure \(en on one bar per monitor (detected via Xinerama). Modules can
+modules \(en clock, battery, volume, scripts you write yourself, anything
+you configure \(en on one bar per monitor (detected via Xinerama). Modules can
open a small floating popup window on hover or click, containing text rows,
button rows, a draggable slider row, or an image row (e.g. album art).
.PP
@@ -248,14 +248,26 @@ the left- and right-aligned modules on its bar, split into equal-width
segments, rather than anchoring to one side. The currently-focused
window's segment is highlighted the same way the active workspace pill
is.
-.SH CUSTOM MODULES
-.B custom : name : "command" : refresh_interval_seconds
-runs an arbitrary shell command on its own interval; its stdout (trailing
-newline stripped) is the bar text. Every other directive on this page
-.RB ( prefix ", " colour ", " click ", " popup ", etc.) works on a custom"
-module by the name given here, exactly as on a built-in one.
+.SH WRITING YOUR OWN MODULE
+There is no separate "custom module" directive. Any name works with
+.BR "module : name : true|false : refresh_interval_seconds" :
+write a script, save it as
+.IR ~/.config/sxbar/scripts/ name .sh " (executable),"
+and enable it exactly like any module listed under
+.B BUILT-IN MODULES
+above \(en both resolve to a script the same way (see that section), so a
+module you write yourself is exactly as first-class as one sxbar ships a
+script for. A name with no matching script anywhere is a silent no-op, not
+an error. If the script understands a
+.B menu
+subcommand it can self-declare its own popup too, exactly like the shipped
+scripts \(en see
+.B POPUPS
+below. Every other directive on this page
+.RB ( prefix ", " colour ", " click ", " popup ", etc.) works on it by the"
+same name.
.SH ICONS, COLOUR, LAYOUT, CLICKS
-These apply to any module, built-in or custom, by name.
+These apply to any module, by name.
.TP
.BR "prefix : name : \(dqtext\(dq" " (or " icon )
Static text prepended to the module's output. Needs a Nerd Font set via
@@ -430,17 +442,18 @@ and add another
.B popup_item
line \(en no sxbarc editing needed. The sxbarc directives above still work
exactly as documented and can still override a script's menu wholesale, if
-you would rather keep everything in one config file. A
-.B custom
-module has no script to load a menu from, so give it a popup via the
-sxbarc directives instead.
+you would rather keep everything in one config file. This applies to any
+module's script, not just the ones sxbar ships \(en see
+.B WRITING YOUR OWN MODULE
+above.
.SH FILES
.TP
.I ~/.config/sxbarc, ~/.config/sxbar/sxbarc
User configuration file (see search order above).
.TP
.I ~/.config/sxbar/scripts/<name>.sh
-Your own edited copy of a built-in module's script, or of a
+Your own edited copy of a module's script (one sxbar ships, or one you
+wrote yourself), or of a
.B prefix_cmd
icon script; checked before the installed system copy.
.TP
@@ -454,11 +467,11 @@ This man page.
System-wide fallback default configuration.
.TP
.I $PREFIX/share/sxbar/scripts/
-Reference copies of every built-in module's script, plus the
+Reference copies of every module script sxbar ships, plus the
.IR battery_icon.sh / volume_icon.sh
prefix_cmd examples and the
-.I demo_popup.sh
-try-it-yourself popup script. Copy any of these to
+.IR demo_popup.sh / demo_menu.sh
+try-it-yourself popup scripts. Copy any of these to
.I ~/.config/sxbar/scripts/
and edit freely.
.TP
@@ -482,7 +495,7 @@ colour : clock : #50fa7b
scroll_up : volume : "wpctl set-volume --limit 1.0 @DEFAULT_AUDIO_SINK@ 5%+"
scroll_down : volume : "wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"
-custom : mem : "free -h | awk '/^Mem:/{print $3\\"/\\"$2}'" : 10
+module : mem : true : 10
.fi
.SH AUTHOR
Abhinav Prasai