Lightbar

Lightbar is a small native Wayland panel for Sway. It draws directly into shared-memory layer-shell surfaces and updates only when module state changes or the compositor grants a frame. There is no GTK widget tree, CSS engine, browser, or free-running render loop.

The initial target is Ubuntu 26.04 and Sway 1.11. The implementation checklist and the deliberately deferred stress tests are tracked in TOOD.md.

What works

  • one top or bottom bar on every selected output, with output-local workspaces;
  • left, centered, and right module groups with hover/click/scroll hit testing;
  • Sway workspaces, binding mode, scratchpad list and direct IPC actions;
  • exact-boundary clock and calendar popup;
  • NetworkManager, PipeWire audio, battery, backlight, and hwmon temperature;
  • default output and microphone selection, volume and mute controls, backlight sliders, and external advanced settings launchers;
  • interval and streaming custom commands with limits, timeout, stale state, and explicit shell opt-in;
  • StatusNotifier watcher/host, validated ARGB pixmaps, attention state, activation, secondary activation, item context menus, and scrolling;
  • strict TOML validation and atomic config/theme hot reload;
  • check-config and doctor commands.

Tray items that only publish an icon-theme name currently use a dot fallback. Pixmap icons are rendered directly. DBusMenu is also still delegated to the tray item through ContextMenu; native nested DBusMenu rendering is tracked in the checklist.

Build and try it

Rust 1.93 or newer is required. On Ubuntu 26.04, install the normal Rust build toolchain and pkg-config; the Wayland and D-Bus protocol clients themselves are pure Rust.

cargo test --all-targets
cargo run -- check-config --config examples/config.toml
cargo run -- doctor --config examples/config.toml
cargo run -- --config examples/config.toml

The example uses UbuntuMono Nerd Font. Install that font for the intended icon glyphs; cosmic-text/fontconfig will otherwise select an installed fallback such as Noto Sans.

Install without replacing Waybar

./scripts/install.sh

This builds a locked release, installs ~/.local/bin/lightbar, and copies the example files only when the destination does not already exist. It also writes ~/.config/lightbar/bar.conf, a generated Sway fragment containing the current user's absolute binary and configuration paths. It does not edit your Sway or Waybar configuration.

Validate the installed configuration:

~/.local/bin/lightbar check-config
~/.local/bin/lightbar doctor

Then remove or comment out your existing top-level bar { ... } block, add the portable include below at top level, and reload Sway:

include ~/.config/lightbar/bar.conf

The included file is generated separately for each user, so a shared Sway configuration never contains a username or /home/... path. Absolute paths in that generated file also avoid depending on the graphical Sway session's PATH, which does not always include ~/.local/bin. If XDG_CONFIG_HOME is customized, use the absolute include line printed by the installer instead.

Sway appends -b lightbar; the CLI accepts that automatically. To roll back, remove the include, restore the old bar block, and reload. For a Waybar setup the bar block can contain:

bar {
    swaybar_command waybar
}

Lightbar never edits or removes Waybar's files.

Configuration

The configuration defaults to $XDG_CONFIG_HOME/lightbar/config.toml (or ~/.config/lightbar/config.toml). theme is resolved relative to that file. Both files reject unknown fields, and an invalid live edit is logged while the last valid configuration remains active.

Module actions live below common.actions and support four explicit types:

[modules.example]
kind = "command"
mode = "interval"       # or "stream"
argv = ["date", "+%s"] # no shell interpretation
interval = "30s"
timeout = "2s"
max_output_bytes = 65536

[modules.example.common.actions.left]
type = "exec"
argv = ["notify-send", "clicked"]

Use type = "shell" only when shell expansion is intentional. Command output may be plain text or a JSON object:

{"text":"42","tooltip":"answer","state":"warning","visible":true}

See examples/config.toml and examples/theme.toml for every built-in used by the current layout.

Audio

Audio requires PipeWire, WirePlumber (wpctl), and pw-dump on PATH. The bar shows the current default output and microphone independently. With the example actions, middle-click toggles mute and scrolling changes volume for the speaker or microphone segment under the pointer. Left-click opens the audio popup.

Click a device name in the popup to choose a default output or microphone. Each default has its own volume slider and mute button. Click the slider or scroll over it to adjust volume between 0% and 150%; changing volume preserves mute state. External changes and device connections update the bar and open popup automatically. A missing default is shown as unavailable while the other endpoint remains usable. If the audio service disconnects, the popup closes and the module reconnects automatically.

Use Tab/Shift+Tab or Up/Down to focus controls, Enter/Space to activate buttons, Left/Right to adjust a focused slider, Home/End for its limits, and Escape to close. Long device lists support scrolling and Previous/More buttons.

format and muted_format configure the output segment; microphone_format and microphone_muted_format configure the microphone segment. All four accept {volume}, {device} (description), and {name} (PipeWire node name). Existing output formats remain valid. The complete example configuration includes both microphone formats.

The popup's Open pavucontrol button provides per-application routing and device profile controls when pavucontrol is installed. Default-device selection uses WirePlumber's set-default command; routing of existing streams follows the session manager's policy.

CPU and memory comparison

Build release binaries, run one bar configuration at a time, and sample the process for at least ten minutes:

./scripts/sample-process.sh $(pgrep -n lightbar) 600 > lightbar-core.tsv
./scripts/sample-process.sh $(pgrep -n waybar) 600 > waybar.tsv

Repeat with the tray removed from each layout, then interact with workspaces, audio, popups, and tray items. Compare the samples and inspect whether RSS or CPU trends upward. An eight-hour soak and event-storm testing remain release gates in TOOD.md; a short local sample is not evidence that those gates pass.

Design guardrails

  • Module events are dirty-compared before drawing.
  • Frame callbacks prevent drawing faster than the compositor.
  • D-Bus tray signal queues, process output, IPC replies, and pixmaps are bounded.
  • Module workers back off or wait on events; low-frequency hardware values use sparse polling.
  • Dropping a configuration generation stops its workers and monitored children.
Description
No description provided
Readme MIT 217 KiB
Languages
Rust 99.1%
Shell 0.9%