garbar
Async Status Bar
A Tokio-powered status bar with modular widgets and Cairo/Pango rendering. garbar displays system information, workspaces, and more with a clean, customizable interface. Integrates seamlessly with gar or runs standalone.
Features
- Async module architecture with Tokio runtime
- Cairo/Pango vector rendering for crisp text
- Battery, CPU, memory monitoring widgets
- Workspace indicator synced with gar/i3/sway
- Active window title display via EWMH
- System tray with XEMBED and SNI D-Bus support
- Per-monitor bars on multi-monitor setups
- Quick settings integration with gartray
- Custom script modules with click/scroll handlers
- TOML or Lua configuration (via gar integration)
- IPC control via garbarctl
- Gradient backgrounds and styling
- Click and scroll event handlers per module
- Hot-reload config via SIGHUP or garbarctl
Quick Start Guide
Get garbar running with gar or as a standalone status bar.
1. Installation
Install garbar using the unified installer or build from source:
2. Running garbar
garbar can run standalone or be auto-started by gar:
3. Integration with gar
Add a gar.bar table to your gar config to auto-start garbar:
-- Enable garbar (gar will spawn it automatically)
gar.bar = {
height = 32,
position = "top",
background = "#1a1a1a",
foreground = "#ffffff",
modules_left = {"workspaces", "window_title"},
modules_right = {"cpu", "memory", "battery", "datetime"},
} 4. Standalone Configuration
Without gar integration, configure garbar at ~/.config/garbar/config.toml:
5. Control with garbarctl
Control the running daemon with garbarctl:
6. Configuration Priority
garbar checks configuration in this order:
~/.config/gar/init.lua— Readsgar.bartable (gar integration)~/.config/garbar/config.toml— Standalone TOML config- Built-in defaults
Configuration Reference
garbar supports two configuration formats: Lua (via gar integration) or standalone TOML.
Minimal Configuration
height = 32
position = "top"
background = "#1a1a1a"
foreground = "#ffffff"
modules_left = ["workspaces", "window_title"]
modules_center = []
modules_right = ["cpu", "memory", "battery", "datetime"] Full Configuration Example
# ═══════════════════════════════════════════
# Bar Settings
# ═══════════════════════════════════════════
height = 32
position = "top" # "top" or "bottom"
opacity = 1.0 # 0.0 to 1.0
# Colors
background = "#1a1a1a"
foreground = "#ffffff"
# Fonts (Pango format with fallback chain)
fonts = [
"JetBrainsMono Nerd Font:size=10",
"DejaVu Sans Mono:size=10",
"monospace:size=10"
]
# ═══════════════════════════════════════════
# Spacing
# ═══════════════════════════════════════════
[margin]
top = 0.0
right = 0.0
bottom = 0.0
left = 0.0
[padding]
left = 8.0
right = 16.0
top = 0.0
bottom = 0.0
# ═══════════════════════════════════════════
# Border & Shadow
# ═══════════════════════════════════════════
[border]
width = 0.0
color = ""
radius = 0.0
[shadow]
enabled = false
color = "#00000080"
blur = 8.0
offset = { x = 0.0, y = 2.0 }
# ═══════════════════════════════════════════
# Animations
# ═══════════════════════════════════════════
[animations]
enabled = true
duration = 150 # milliseconds
easing = "ease-out-cubic"
# ═══════════════════════════════════════════
# Separator
# ═══════════════════════════════════════════
[separator]
text = "│"
foreground = "#555555"
[separator.padding]
left = 8.0
right = 8.0
# ═══════════════════════════════════════════
# Module Layout
# ═══════════════════════════════════════════
modules_left = ["workspaces", "window_title"]
modules_center = []
modules_right = ["cpu", "memory", "battery", "tray", "datetime"]
# ═══════════════════════════════════════════
# Module Configuration
# ═══════════════════════════════════════════
[modules.workspaces]
show_empty = false
show_urgent = true
pin_workspaces = false
[modules.workspaces.focused]
background = "transparent"
foreground = "#ffffff"
[modules.workspaces.focused.underline]
width = 2.0
color = "#33ccff"
[modules.workspaces.unfocused]
background = "transparent"
foreground = "#666666"
[modules.workspaces.urgent]
background = "#ff5555"
foreground = "#ffffff"
[modules.window_title]
max_length = 50
ellipsis = "…"
empty_text = "Desktop"
show_icon = true
icon_spacing = 8.0
[modules.cpu]
format = " {usage}%"
interval = 2
warning_threshold = 70
critical_threshold = 90
warning_foreground = "#ffaa00"
critical_foreground = "#ff5555"
[modules.memory]
format = " {percent}%"
format_alt = " {used}/{total}"
interval = 5
warning_threshold = 80
critical_threshold = 95
warning_foreground = "#ffaa00"
critical_foreground = "#ff5555"
[modules.battery]
device = "auto"
format_charging = " {percent}%"
format_discharging = "{icon} {percent}%"
format_full = " Full"
low_threshold = 15
low_foreground = "#ff5555"
[[modules.battery.icons]]
threshold = 10
icon = ""
[[modules.battery.icons]]
threshold = 25
icon = ""
[[modules.battery.icons]]
threshold = 50
icon = ""
[[modules.battery.icons]]
threshold = 75
icon = ""
[[modules.battery.icons]]
threshold = 100
icon = ""
[modules.datetime]
format = " %a %b %d %H:%M"
format_alt = " %Y-%m-%d %H:%M:%S"
interval = 1
[modules.tray]
icon_size = 18
spacing = 8.0
[modules.tray.padding]
left = 4.0
right = 4.0
# ═══════════════════════════════════════════
# Custom Script Modules
# ═══════════════════════════════════════════
[modules.script.weather]
exec = "curl -s 'wttr.in?format=1'"
interval = 300
format = " {output}"
[modules.script.music]
exec = "playerctl metadata --format '{{artist}} - {{title}}'"
interval = 1
click_left = "playerctl play-pause"
click_right = "playerctl next"
scroll_up = "playerctl volume 0.05+"
scroll_down = "playerctl volume 0.05-" Bar Options Reference
| Option | Type | Default | Description |
|---|---|---|---|
| height | u16 | 32 | Bar height in pixels |
| position | string | top | "top" or "bottom" |
| opacity | f64 | 1.0 | Bar opacity (0.0-1.0) |
| background | hex/gradient | #1a1a1a | Bar background (solid or gradient) |
| foreground | hex | #ffffff | Default text color |
| fonts | array | - | Pango font fallback chain |
| margin.* | f64 | 0.0 | External margin (top/right/bottom/left) |
| padding.left | f64 | 8.0 | Internal left padding |
| padding.right | f64 | 16.0 | Internal right padding |
| border.width | f64 | 0.0 | Border width in pixels |
| border.radius | f64 | 0.0 | Corner radius |
| modules_left | array | ["workspaces", ...] | Left-aligned modules |
| modules_center | array | [] | Center-aligned modules |
| modules_right | array | ["cpu", ...] | Right-aligned modules |
Overview
garbar is an async status bar built on Tokio with Cairo/Pango rendering. It features a modular architecture where each widget runs independently with its own update interval.
Architecture
- Runtime: Tokio async runtime for non-blocking module updates
- Rendering: Cairo for graphics, Pango for text layout
- X11 Backend: x11rb with EWMH and XEMBED support
- IPC: JSON over Unix domain sockets
- Config: TOML standalone or Lua via gar integration
File Locations
| ~/.config/garbar/config.toml | Standalone configuration |
| ~/.config/gar/init.lua | gar integration (gar.bar table) |
| $XDG_RUNTIME_DIR/garbar.sock | IPC socket |
Available Modules
workspaceswindow_titlecpumemorybatterydatetimetrayquick_settingsscriptModules
garbar uses a modular architecture. Each module runs independently with its own update interval and can handle click/scroll events.
Module Layout
modules_left = ["workspaces", "window_title"]
modules_center = []
modules_right = ["cpu", "memory", "battery", "quick_settings", "tray", "datetime"] Module Interactions
| Module | Left-Click | Scroll |
|---|---|---|
| workspaces | Switch workspace | - |
| memory | Toggle format | - |
| datetime | Toggle format | - |
| script | Custom command | Custom command |
Workspaces Module
Displays workspace indicators with visual state (focused, unfocused, urgent). Click to switch workspaces. Uses i3-compatible IPC for workspace info.
[modules.workspaces]
show_empty = false # Show empty workspaces
show_urgent = true # Highlight urgent workspaces
pin_workspaces = false # Always show pinned list
# Focused workspace style
[modules.workspaces.focused]
background = "transparent"
foreground = "#ffffff"
[modules.workspaces.focused.underline]
width = 2.0
color = "#33ccff"
# Unfocused workspace style
[modules.workspaces.unfocused]
background = "transparent"
foreground = "#666666"
# Urgent workspace style (flashing windows)
[modules.workspaces.urgent]
background = "#ff5555"
foreground = "#ffffff" IPC Socket Paths
The module checks these sockets in order:
$I3SOCKenvironment variable$SWAYSOCKenvironment variable (sway)$XDG_RUNTIME_DIR/gar-i3.sock/tmp/gar-i3.sock
Window Title Module
Displays the title of the currently focused window via X11 _NET_ACTIVE_WINDOW property.
[modules.window_title]
max_length = 50 # Max characters before truncation
ellipsis = "…" # Truncation indicator
empty_text = "Desktop" # Text when no window focused
show_icon = true # Show window icon (if available)
icon_spacing = 8.0 # Space between icon and text Update interval: 50ms for snappy focus tracking
CPU Module
Displays CPU usage percentage with color thresholds for warnings. Reads from /proc/stat with exponential smoothing.
[modules.cpu]
format = " {usage}%" # Display format
interval = 2 # Update interval (seconds)
warning_threshold = 70 # % for warning color (orange)
critical_threshold = 90 # % for critical color (red)
warning_foreground = "#ffaa00" # Warning color
critical_foreground = "#ff5555" # Critical color Format Placeholders
{usage}— CPU usage percentage (0-100)
Color Logic
- Critical (≥90%):
#ff5555 - Warning (≥70%):
#ffaa00 - Normal:
#5294e2
Memory Module
Displays memory usage. Click to toggle between percentage and absolute values. Reads from /proc/meminfo.
[modules.memory]
format = " {percent}%" # Primary format
format_alt = " {used}/{total}" # Alt format (click to toggle)
interval = 5 # Update interval (seconds)
warning_threshold = 80 # % for warning color
critical_threshold = 95 # % for critical color
warning_foreground = "#ffaa00"
critical_foreground = "#ff5555" Format Placeholders
{percent}— Memory usage percentage{used}— Used memory (auto-scaled: G/M/K){total}— Total memory{available}— Available memory
Battery Module
Displays battery status with charging indicator and threshold-based icons. Auto-detects from /sys/class/power_supply.
[modules.battery]
device = "auto" # Auto-detect or specify "BAT0"
format_charging = " {percent}%" # While charging
format_discharging = "{icon} {percent}%"
format_full = " Full"
low_threshold = 15 # Low battery warning %
low_foreground = "#ff5555" # Low battery color
# Threshold-based icons (matched in order)
[[modules.battery.icons]]
threshold = 10
icon = ""
[[modules.battery.icons]]
threshold = 25
icon = ""
[[modules.battery.icons]]
threshold = 50
icon = ""
[[modules.battery.icons]]
threshold = 75
icon = ""
[[modules.battery.icons]]
threshold = 100
icon = "" Format Placeholders
{percent}— Battery percentage{icon}— Threshold-based icon
Color Logic
- Charging:
#98c379 - Low (≤15%):
#ff5555 - Normal:
#e5c07b
Date/Time Module
Displays current date and time. Click to toggle between formats. Uses strftime syntax via chrono.
[modules.datetime]
format = " %a %b %d %H:%M" # Primary format
format_alt = " %Y-%m-%d %H:%M:%S" # Alt format (click)
interval = 1 # Update interval (seconds) Common Format Codes
| Code | Output | Example |
|---|---|---|
| %H:%M | 24-hour time | 14:30 |
| %I:%M %p | 12-hour time | 02:30 PM |
| %a | Day abbreviated | Mon |
| %A | Day full | Monday |
| %b %d | Month day | Jan 15 |
| %Y-%m-%d | ISO date | 2024-01-15 |
| %S | Seconds | 45 |
Script Module
Run custom shell commands and display output. Supports click/scroll handlers for interactive widgets. Commands run in background threads.
# Weather widget
[modules.script.weather]
exec = "curl -s 'wttr.in?format=1'"
interval = 300 # 5 minutes
format = " {output}"
# Music player controls
[modules.script.music]
exec = "playerctl metadata --format '{{artist}} - {{title}}'"
interval = 1
click_left = "playerctl play-pause"
click_right = "playerctl next"
scroll_up = "playerctl volume 0.05+"
scroll_down = "playerctl volume 0.05-"
# Volume control
[modules.script.volume]
exec = "pamixer --get-volume-human"
interval = 0 # Event-driven only (no polling)
click_left = "pavucontrol"
scroll_up = "pamixer -i 5"
scroll_down = "pamixer -d 5"
# VPN status
[modules.script.vpn]
exec = "nmcli -t -f TYPE,STATE connection show --active | grep -q vpn && echo '🔒' || echo '🔓'"
interval = 10 Script Options
| Option | Type | Description |
|---|---|---|
| exec | string | Shell command to execute (required) |
| interval | u32 | Update interval in seconds (0 = event-only) |
| format | string | Wrapper format (use {output}) |
| click_left | string | Command on left-click |
| click_middle | string | Command on middle-click |
| click_right | string | Command on right-click |
| scroll_up | string | Command on scroll up |
| scroll_down | string | Command on scroll down |
| tail | bool | Run command as long-lived process, read stdout continuously |
Note: Scripts run via sh -c "command". Output is stdout; empty on non-zero exit.
Common Workflows
Practical examples of using garbar with gar and other tools.
Auto-Start with gar
The simplest way to use garbar is via gar integration:
-- garbar auto-starts when gar.bar is defined
gar.bar = {
height = 32,
position = "top",
background = "#1a1a1a",
modules_left = {"workspaces", "window_title"},
modules_right = {"cpu", "memory", "battery", "tray", "datetime"},
} Media Player Widget
Create an interactive media player widget with playerctl:
[modules.script.media]
exec = '''
if playerctl status 2>/dev/null | grep -q Playing; then
echo " $(playerctl metadata --format '{{artist}} - {{title}}' | cut -c1-40)"
elif playerctl status 2>/dev/null | grep -q Paused; then
echo " $(playerctl metadata --format '{{artist}} - {{title}}' | cut -c1-40)"
else
echo ""
fi
'''
interval = 1
click_left = "playerctl play-pause"
click_right = "playerctl next"
scroll_up = "playerctl volume 0.05+"
scroll_down = "playerctl volume 0.05-" System Monitor Scripts
# Disk usage warning
[modules.script.disk]
exec = "df -h / | awk 'NR==2 {print $5}'"
interval = 60
format = " {output}"
# Network status
[modules.script.network]
exec = "nmcli -t -f DEVICE,STATE device | grep -m1 connected | cut -d: -f1"
interval = 10
format = " {output}"
# VPN indicator
[modules.script.vpn]
exec = "nmcli -t -f TYPE,STATE connection show --active | grep -q vpn && echo '' || echo ''"
interval = 10
# Unread mail count
[modules.script.mail]
exec = "notmuch count tag:unread"
interval = 60
format = " {output}" Toggle Bar Visibility
Bind a key in gar to toggle the bar:
gar.bind("mod+b", function()
gar.exec("garbarctl toggle")
end) Hot Reload
Reload configuration without restarting:
Floating Bar Style
Create a floating bar with margins and rounded corners:
height = 28
opacity = 0.9
[margin]
top = 8.0
right = 8.0
left = 8.0
[border]
radius = 8.0
[shadow]
enabled = true
blur = 12.0
color = "#00000060"
offset = { x = 0.0, y = 4.0 } IPC Protocol
garbar exposes a Unix domain socket for control and status queries.
Socket Location
garbarctl Commands
| Command | Description |
|---|---|
| show | Make bar visible |
| hide | Hide bar |
| toggle | Toggle visibility |
| reload | Reload configuration from disk |
| status | Query bar status (returns JSON) |
| update <module> | Force update a specific module |
| quit | Gracefully shut down daemon |
JSON Protocol
Send JSON objects over the socket for scripting:
{"command": "show"}
{"command": "toggle"}
{"command": "update_module", "module": "cpu"}
{"command": "status"} // Success
{"success": true, "error": null}
// Status response
{
"success": true,
"data": {
"visible": true,
"width": 1920,
"height": 32,
"modules_left": ["workspaces", "window_title"],
"modules_center": [],
"modules_right": ["cpu", "memory", "datetime"]
}
}
// Error
{"success": false, "error": "Connection refused"} Scripting Example
#!/bin/bash
# Toggle garbar visibility
SOCK="${XDG_RUNTIME_DIR:-/tmp}/garbar.sock"
echo '{"command": "toggle"}' | nc -U "$SOCK" Troubleshooting
garbar won't start
Check for errors with debug logging:
Workspaces not showing
The module needs an i3-compatible IPC socket:
Ensure gar is running and exposes /tmp/gar-i3.sock or set I3SOCK.
Icons not displaying
Ensure a Nerd Font is installed and configured:
Install with: sudo pacman -S ttf-jetbrains-mono-nerd (Arch) or download from nerdfonts.com.
Config not loading
Verify config file syntax:
Check for missing quotes, unbalanced brackets, or invalid values.
Battery module not showing
Check power supply detection:
Set device = "BAT0" explicitly if auto-detection fails. Module hides if no battery found.
System tray icons missing
Only one app can own the system tray:
Kill other status bars (polybar, i3bar, waybar) that may own the tray.
garbarctl: connection refused
Verify garbar is running:
Start garbar first: garbar &
Script module not updating
Test the script manually:
Scripts must exit 0 and output to stdout. Empty output on non-zero exit.
High CPU usage
Check module intervals:
- Increase
intervalfor CPU-intensive scripts - Set
interval = 0for event-only updates - Avoid sub-second intervals for network requests