.TH SXBAR 1 "2026-07-29" "sxbar 1.1" "User Commands"
.SH NAME
sxbar \- a small, fast EWMH status bar for Xorg
.SH SYNOPSIS
.B sxbar
.br
.B sxbar
.RB { \-v | \-\-version }
.SH DESCRIPTION
.B sxbar
is a single C99 binary that draws an EWMH workspace switcher plus a row of
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
Everything is controlled by a single plain-text configuration file; there is
nothing to recompile to add or remove a module, change colours/icons, wire
up click actions, or define a popup menu.
.B sxbar
re-reads its configuration once, at startup \(en it is not live-reloaded.
.SH OPTIONS
.TP
.BR \-v ", " \-\-version
Print the version, author and licence line, then exit.
.SH CONFIGURATION FILE
The config file is searched for, in this order, and the first one found is
used:
.RS
.IP \(bu 2
.I $XDG_CONFIG_HOME/sxbarc
.IP \(bu 2
.I $XDG_CONFIG_HOME/sxbar/sxbarc
.IP \(bu 2
.I ~/.config/sxbarc
.IP \(bu 2
.I ~/.config/sxbar/sxbarc
.IP \(bu 2
.I /usr/local/share/sxbarc
(the system-wide fallback \fBmake install\fP places; see \fBFILES\fP)
.RE
.PP
Syntax is line-oriented,
.IR "key : value" ,
with extra
.I : name
fields for directives that target one module. A
.B #
starts a trailing comment, except as the very first character of a value
(so hex colours like
.I #50fa7b
are not treated as comments). Blank lines are ignored. A leading
.B ~/
in any command or path value is expanded to
.IR $HOME/ .
.SH GLOBAL OPTIONS
Set once, apply to every bar unless noted.
.TP
.B height : pixels
Bar height. Default 19.
.TP
.B bottom_bar : true|false
Dock at the screen bottom instead of the top. Default false.
.TP
.B vertical_padding : pixels
Gap between the bar and the screen edge it's docked to. Default 0.
.TP
.B horizontal_padding : pixels
Gap on both sides of the bar. Default 0.
.TP
.B text_padding : pixels
Inner padding before the first workspace/module. Default 0.
.TP
.B border : true|false
.TQ
.B border_width : pixels
Draw a border around the bar window. Default false / 0.
.TP
.BR background_colour " (or " background_color ") : " #rrggbb|X-colour-name
.TQ
.BR foreground_colour " (or " foreground_color ") : " #rrggbb|X-colour-name
.TQ
.BR border_colour " (or " border_color ") : " #rrggbb|X-colour-name
Bar colours. Defaults #000000, #7abccd, #005577. Unlike most other keys,
these three do not support a trailing
.I "# comment"
on the same line \(en put comments on their own line instead.
.TP
.B font : "Family:size=N[:style=Bold]"
Xft font name. Use a Nerd Font family here to render icon glyphs used by
.B prefix / prefix_cmd
below. Default monospace:size=10.
.TP
.B show_version : true|false
.TQ
.B version_text : "text"
Optional version string shown at the primary bar's right edge. Default
true / "sxbar ver. 1.1".
.TP
.B secondary_bar : true|false
Adds a second, modules-only bar on the edge opposite
.BR bottom_bar ,
with no workspace switcher and no version text \(en only modules tagged
.B "bar : name : secondary"
are drawn on it, everything else stays on the primary bar. Default false.
.SH WORKSPACES & MONITORS
The workspace switcher reads standard EWMH properties
.RI ( _NET_DESKTOP_NAMES ", " _NET_CURRENT_DESKTOP ", " _NET_CLIENT_LIST ", "
.IR _NET_WM_DESKTOP ),
so it works with any EWMH-compliant window manager with no sxbar-specific
setup. One bar opens per monitor automatically via Xinerama; each workspace
pill shows up to 4 small boxes for windows on that workspace, counted only
for windows actually on that bar's own monitor.
.TP
.BR "workspace_icon : name : \(dqicon text\(dq"
Replaces a workspace's displayed label \(en its
.I _NET_DESKTOP_NAMES
string, typically a plain number like
.IR \(dq1\(dq " -- with different text instead, e.g. a Nerd Font glyph."
Purely cosmetic: switching still targets the same underlying desktop,
only what's drawn changes. Needs a Nerd Font set via
.B font
to render glyphs. Repeatable, one line per workspace; any workspace
without a matching line just shows its plain name.
.SH BUILT-IN MODULES
Enabled with
.BR "module : name : true|false : refresh_interval_seconds" .
Available names:
.BR clock ", " date ", " battery ", " volume ", " cpu ", " brightness ", "
.BR bluetooth ", " usermenu ", " network ", " media .
.PP
Each resolves its bar-text command, and its popup content (see
.B POPUPS
below), to a script rather than code built into the binary \(en checked in
this order: your own copy at
.IR ~/.config/sxbar/scripts/ name .sh ,
then the system copy at
.IR /usr/local/share/sxbar/scripts/ name .sh ,
or a harmless no-op if neither exists. Copy any of them over and edit; no
config line or recompile is needed, sxbar picks up your copy on the next
restart.
.TP
.B clock
Bar text: current time (HH:MM:SS). Popup: "Open calendar" (needs
.BR gsimplecal ).
.TP
.B date
Bar text: current date (YYYY-MM-DD). Popup: "Open calendar" (needs
.BR gsimplecal ).
.TP
.B battery
Bar text: charge percentage, from
.IR /sys/class/power_supply .
Popup: detailed status line and a "Toggle power saver" button (needs
.B upower
and
.BR power-profiles-daemon ).
.TP
.B volume
Bar text: volume percentage (needs
.BR wpctl ).
Popup: a draggable slider.
.TP
.B cpu
Bar text: overall CPU usage percentage, sampled from
.IR /proc/stat .
Popup: usage, memory (used/total), and a per-core breakdown.
.TP
.B brightness
Bar text: screen brightness percentage (needs
.BR brightnessctl ).
Popup: a draggable slider.
.TP
.B bluetooth
Bar text: adapter power state, On/Off (needs
.BR bluetoothctl ).
Popup: Turn on, Turn off, Search for devices, and Pair last found device
(the most recently seen device \(en no interactive device list).
.TP
.B usermenu
Bar text: current username. Popup: Sleep
.RI ( "systemctl suspend" ),
Log out
.RI ( "pkill <your WM>" ", " sxwm " by default), and Shut down ("
.IR "systemctl poweroff" ).
Add, remove or reorder rows by editing your own copy of
.I usermenu.sh
directly \(en see
.B POPUPS
below.
.TP
.B network
Bar text: Online/Offline, based on whether a default route exists (needs
.BR ip ).
Popup: first WiFi interface + its IPv4, and first wired Ethernet interface
+ its IPv4 (or "none"/"disconnected").
.TP
.B media
Bar text: a play/pause glyph plus "Artist - Title" (needs
.BR playerctl ,
controlling whichever player it considers active). Popup: album art (a
.B popup_image
row; remote art URLs need
.B curl
to download and cache), track info, and Previous/Play-Pause/Next buttons.
.TP
.B taskbar
Not like the other modules: renders one clickable segment per window on
the current workspace directly in the bar, instead of one line of text.
See
.B TASKBAR
below.
.SH TASKBAR
.B taskbar
shows one clickable segment per window on the current workspace, so you
can switch focus between them \(en the motivating case being a window
manager's monocle mode, where only one window is visible at a time.
Typical setup is its own row on the secondary bar:
.RS
.nf
secondary_bar : true
module        : taskbar : true : 1
bar           : taskbar : secondary
.fi
.RE
.PP
Needs
.BR wmctrl ,
for both listing windows and requesting focus, and a window manager that
actually acts on a
.I _NET_ACTIVE_WINDOW
client message (what
.B "wmctrl -i -a"
sends to request focus). sxwm, as of this writing, does not out of the
box \(en see
.I patches/net-active-window-mrjensk.patch
in the sxwm repo, which adds that handling by reusing sxwm's own
.IR set_input_focus() ,
the same function
.IR focus_next / focus_prev
already use. Other window managers may already support this; check
yours.
.PP
Ignores its own
.B align
\(en as a fill module, it always occupies whatever space is left between
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 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, by name.
.TP
.BR "prefix : name : \(dqtext\(dq" " (or " icon )
Static text prepended to the module's output. Needs a Nerd Font set via
.B font
to render glyphs.
.TP
.BR "prefix_cmd : name : \(dqcommand\(dq" " (or " icon_cmd )
Like
.B prefix
but the icon comes from a script instead of fixed text, so it can change
with module state (e.g. battery charging, volume muted). Re-run on the
module's own refresh interval; the module's current bar-text output is
passed in as
.IR $1 .
Its stdout (no trailing newline) becomes the prefix verbatim, so include
your own trailing separator if you want one.
.TP
.BR "colour : name : #rrggbb" " (or " color )
Overrides
.B foreground_colour
for one module.
.TP
.B width : name : min_pixels
Reserves a minimum pixel width for the module's slot so neighbours don't
shift as its text width changes. Text itself is never truncated.
.TP
.B max_width : name : max_pixels
The opposite of
.BR width :
caps the module's slot at this many pixels. Text that fits draws as
normal; text wider than the cap scrolls left (a marquee/ticker) within
that fixed width instead of stretching the bar. sxbar only redraws faster
than its usual once-a-second cadence while something is actually
scrolling, so this costs nothing when nothing overflows.
.TP
.B bar : name : primary|secondary
Which bar the module is drawn on when
.B secondary_bar
is enabled. Default primary.
.TP
.B align : name : left|center|right
Which side of the bar the module is anchored to; each group is laid out
independently. Default right.
.TP
.B icon_only : name : true|false
Shows only the prefix/icon, hiding the module's own rendered text. The
underlying command, refresh interval and popup still run as normal. Needs
a
.B prefix
or
.B prefix_cmd
set, or there is nothing left to show.
.TP
.BR "click : name : \(dqcommand\(dq"
Runs a command, detached, on left-click. Ignored for a module whose popup
trigger is
.BR click ,
since the popup takes over left-click there.
.TP
.BR "scroll_up : name : \(dqcommand\(dq"
.TQ
.BR "scroll_down : name : \(dqcommand\(dq"
Run a command on scrolling up/down over the module; work independently of
any popup.
.SH POPUPS
A module can open a small floating window instead of, or as well as,
running a plain
.B click
command. Only one popup is open at a time.
.TP
.B popup : name : hover|click : buttons|slider
.B hover
opens the popup while the pointer is over the module, closing it when the
pointer leaves both the module and the popup;
.B click
opens it on left-click and closes it again on a second click, or on clicking
elsewhere. The third field only decides whether
the module has a popup at all \(en
.BR popup_item / popup_info / popup_set
below decide what is actually in it, in any combination, so either word
works there.
.TP
.BR "popup_item : name : \(dqLabel\(dq : \(dqcommand\(dq"
A button row: runs
.I command
(detached) on click; the popup stays open (see
.B POPUPS
above for how it closes). Repeatable.
.TP
.BR "popup_info : name : \(dqcommand\(dq"
A purely informational text row: its label is
.IR command 's
output, re-run fresh every time the popup opens. Not clickable at all.
Repeatable.
.TP
.BR "popup_image : name : \(dqcommand\(dq"
An image row (e.g. album art):
.IR command 's
stdout is a path to a local image file, re-run fresh every time the popup
opens; scaled down, preserving aspect ratio, to fit within a 160-pixel
square box if larger. Empty or failed output just means no image that
time \(en the row stays, it renders blank. Not clickable. Needs no extra
library: image decoding is vendored and compiled into
.BR sxbar .
An image row (like a slider or
.B popup_buttons
row) anchors the popup's width \(en any
.B popup_info
or
.B popup_item
text in the same popup is then capped to that width and scrolls instead
of stretching the popup past it.
.TP
.BI "popup_buttons : name : \(dqLabel1\(dq : \(dqcommand1\(dq : \(dqLabel2\(dq : \(dqcommand2\(dq " ...
One row split into N equal-width button segments side by side, e.g. media
transport controls, instead of N stacked full-width
.B popup_item
rows. Needs an even number of quoted label/command pairs; each segment
runs its own command on click and the popup stays open, same as
.BR popup_item .
Glyphs work well as segment labels here.
.TP
.BR "popup_set : name : \(dqcommand\(dq"
Adds (or updates) one draggable 0\(en100% slider row.
.I command
is run when the value changes, receiving the new value as
.I $1
(e.g.
.IR 45% ,
same convention as
.BR prefix_cmd ).
The slider's starting position comes from the module's own bar-text
command output, so no separate "get" command is needed.
.PP
Text, button, slider, image and segmented-button rows can be mixed freely
in the same popup. The first
.BR popup_item ,
.BR popup_info ,
.B popup_image
or
.B popup_buttons
line for a given module replaces its current default rows (including any
.B popup_set
slider row); later lines for that module append instead. Add
.B popup_set
again afterwards if you cleared a slider row this way and still want one.
.SS Module-declared menus
A built-in module's default popup content is not compiled into the binary:
it comes from that module's own script, run once at startup as
.IR "<script> menu" .
That subcommand prints the exact same
.BR popup / popup_item / popup_info / popup_image / popup_buttons / popup_set
directives described above, just without the
.I name
field, since a script only ever describes itself. For example,
.I usermenu.sh menu
prints:
.RS
.nf
popup : hover : buttons
popup_item : "Sleep" : "systemctl suspend"
popup_item : "Log out" : "pkill sxwm"
popup_item : "Shut down" : "systemctl poweroff"
.fi
.RE
.PP
To add your own entry to the user menu, edit the
.B menu)
case in your own copy of
.I ~/.config/sxbar/scripts/usermenu.sh
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. 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 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
.I $PREFIX/bin/sxbar
The installed binary.
.TP
.I $PREFIX/share/man/man1/sxbar.1
This man page.
.TP
.I $PREFIX/share/sxbarc
System-wide fallback default configuration.
.TP
.I $PREFIX/share/sxbar/scripts/
Reference copies of every module script sxbar ships, plus the
.IR battery_icon.sh / volume_icon.sh
prefix_cmd examples and the
.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
.I $XDG_CACHE_HOME/sxbar-media-art/ (or ~/.cache/sxbar-media-art/)
Downloaded album art cache, populated by
.IR "media.sh art" ,
one file per remote artwork URL.
.PP
.I $PREFIX
defaults to
.IR /usr/local .
.SH EXAMPLE
.nf
module : clock   : true : 1
module : battery : true : 30
module : volume  : true : 5

prefix : clock : " "
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%-"

module : mem : true : 10
.fi
.SH AUTHOR
Abhinav Prasai
.SH SEE ALSO
Full directive reference and worked examples ship in this repository's
.I README.md
and
.IR docs/wiki.html .
