Status Providers
Battery, volume, brightness, and network are each an optional, opt-in
toolkit crate implementing patin::service::Provider (see
Architecture), not code inside the
patin library or a demo-only fixture. examples/demo_bar/services.rs
composes battery, volume, and network into one StatusSnapshot for its row
layout — that
composition, and the row’s dynamic membership/damage behavior, remains the
demo’s own job. A missing provider does not prevent the example from
starting.
Battery
crates/patin-service-upower’s BatteryProvider (see
Stage 6a) reads UPower’s synthetic
DisplayDevice over D-Bus — the aggregate device UPower maintains
specifically for status bars — via zbus’s blocking API, fetching the
Percentage and State properties. It does not depend on battery names
such as BAT0 or on a hardware model. A missing system bus or UPower
service returns None.
The demo renders the percentage as the fill level of a battery outline. Low battery uses a warning color and charging uses the Patin accent; no percentage or name is drawn.
Volume
Linux audio systems do not expose one universal standard D-Bus volume
interface, so crates/patin-service-volume’s VolumeProvider (see
Stage 6b) shells out instead:
wpctl get-volume @DEFAULT_AUDIO_SINK@;pactl get-sink-volume @DEFAULT_SINK@plusget-sink-mute.
This supports a native PipeWire default sink and the common PulseAudio
compatibility service. Failure of both commands returns None. The demo maps
the percentage to zero through three sound bars; mute replaces them with a
warning-colored strike.
Brightness
There is no portable D-Bus property to read the current backlight level
(systemd-logind only exposes a SetBrightness method, not a readable one),
so crates/patin-service-brightness’s BacklightProvider reads Linux’s
documented /sys/class/backlight ABI directly: it discovers entries, reads
brightness and max_brightness, and returns a percentage. It does not
assume a driver or panel name. A missing or invalid backlight entry returns
None. The adapter remains available to other compositions, but the current
demo bar deliberately does not instantiate it.
Network
crates/patin-service-network’s NetworkProvider (see
Stage 6e) reads every active
NetworkManager connection over D-Bus. A wifi connection additionally walks
ActiveConnection.Devices → Device.Wireless.ActiveAccessPoint →
AccessPoint.Strength for a signal percentage; ethernet sets the independent
wired field.
The same provider discovers modem objects through ModemManager’s standard
ObjectManager interface. A registered modem contributes its independent
SignalQuality percentage. This represents simultaneous wifi and SIM service
without treating VPN or loopback connections as physical transports.
The demo draws a wifi fan, a linked-node icon for wired, and cellular strength bars. Only active/registered transport icons receive slots.
Polling
The demo’s Shell::update polls its three providers once per platform
update. Unchanged values produce no redraw; changed values damage only
their component. A provider’s snapshot appearing or disappearing changes
row membership and damages the full bar.
examples/demo_bar/services.rs preserves the adapters’ structured snapshot
types instead of formatting strings. examples/demo_bar/scene.rs owns all
icon choices and builds them from existing Fill and RoundedFill commands,
so neither the toolkit nor service crates prescribe a visual style or require
an icon font.
All four adapters are poll-based today, reusing the same once-per-second tick. A future push-only service (notifications, media) will need a way to wake the platform event loop from a background thread between ticks — that plumbing does not exist yet.