Skip to main content
otter-bar and similar daemons use widget structs, not Surface Description trees. Each widget is a Zig struct with draw, setArea, getWidth, and optional input hooks. Layout code lines them up in a row and calls those methods directly. Stick to this path for config-driven status bar rows. New windows and forms should use Surface Description.

Widget shell

Every bar widget embeds ui.Widget:
Widget tracks area and pointer motion state. No vtable on the hot path for bar specs (see below).

Widget registries

FieldRegistry(Owner, specs)

Maps layout string names ("clock", "volume") to optional struct fields on the bar owner:
Registry methods: lookup by name, getWidth, setArea, hit test, popup enumeration, damage collection, drawByName.

NamedWidgetList(Owner, spec)

Same dispatch for dynamic lists like button_menu:

Draw specs (widgets/specs/)

Bar widgets render through draw specs under widgets/specs/ (for example widgets/specs/clock.zig). Specs are comptime structs with draw(frame, clip, ...) that call drawing primitives or emit Surface Description subtrees for complex popups. Registries invoke specs plus widget hooks for:
  • Input (click, scroll, motion)
  • Popups (getPopupSupport, volume/MPRIS/network menus)
  • Damage (collectDamage, markFullRedraw)

Available bar widgets

Enable flags in otter-bar.conf (workspaces_enabled, falcond_enabled, etc.). See otter-bar.

D-Bus and PipeWire dependencies

Several widgets need runtime services: Build with -Denable_dbus=false or -Denable_pipewire=false to drop widgets at compile time.

Popups and tooltips

Widgets implementing getPopupSupport can open PopupMenu or custom layer surfaces (volume slider, network Wi-Fi list). HoverState drives tooltip delay. Popups render in a separate draw pass with damage tracking.

Damage and redraw

Bar polls D-Bus, timers, and config inotify. Each widget reports damage via the registry so DamageTracker gets minimal rects. Full redraw on theme or config reload.

Adding a custom bar widget

  1. Create widgets/my_widget.zig struct with draw, setArea, getWidth.
  2. Add widgets/specs/my_widget.zig draw spec.
  3. Register field in otter-bar config schema and layout parser.
  4. Add FieldSpec entry to the bar registry.
For one-off launcher buttons, use NamedWidgetList and button_menu config entries instead of a new widget type.

Surface Description in bar popups

Some widgets (volume popup, MPRIS expanded) build a small UiState internally for the popup panel while the bar strip stays on the widget path. Hybrid pattern: bar strip = widget spec; popup = Surface Description.

Next