Skip to content

0xferrous/wispd

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

50 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

wispd

wispd is an experimental Wayland notification daemon for org.freedesktop.Notifications. It includes:

  • wispd: layer-shell popup UI (one popup window per notification)
  • wisp-debug: CLI/debug daemon to inspect incoming notifications and test close/action flows
  • wispd-monitor: passive D-Bus monitor for notifications traffic (does not own org.freedesktop.Notifications)
  • wispd-forward: forwards host notifications into a VM over SSH (keeps host daemon like mako active)
  • wisp-random: sends randomized test notifications over org.freedesktop.Notifications
  • Reusable crates:
    • wisp-source (D-Bus server + notification lifecycle)
    • wisp-types (shared notification/event types)

Table of contents

Current status

Early-stage but functional:

  • Implements Notify, CloseNotification, GetCapabilities, GetServerInformation
  • Supports replacement (replaces_id), timeouts, actions, and D-Bus close/action signals
  • Configurable popup layout/colors/format via TOML
  • Optional timeout progress bar (top/bottom edge) for timed notifications

freedesktop.org Notifications API coverage

Checklist for org.freedesktop.Notifications support right now:

Core D-Bus methods

  • Notify
  • CloseNotification
  • GetCapabilities
  • GetServerInformation

D-Bus signals

  • NotificationClosed
  • ActionInvoked

Behavior/details

  • Replacement via replaces_id
  • Action invocation from UI/debug path
  • Timeout handling (> 0, 0, and < 0 + configurable default timeout)
  • Basic hints parsing: urgency, category, desktop-entry, transient
  • [~] Extra hints preserved as debug strings (not fully interpreted)
  • Rich hints/attachments (images, sound, progress, etc.)
  • Markup rendering
  • Icon rendering in UI (path/file URI icons)

Requirements

  • Linux Wayland session (for wispd UI)
  • Session D-Bus
  • No other daemon owning org.freedesktop.Notifications (e.g. stop mako/dunst first)

Quick start

1) Run debug daemon (easiest first test)

cargo run -p wisp-debug

Send randomized test notifications

cargo run -p wisp-random
cargo run -p wisp-random -- --count 10 --interval-ms 500
cargo run -p wisp-random -- --loop --interval-ms 750 --actions-always --icons-never
cargo run -p wisp-random -- --replace-id 1 --persistent-only

In another terminal:

notify-send "hello" "from notify-send"

2) Run UI daemon

cargo run -p wispd

If Wayland libraries are missing, use the flake dev shell:

nix develop
cargo run -p wispd

3) Run passive monitor (no name ownership)

cargo run -p wispd-monitor

Or via flake app:

nix run .#wispd-monitor

4) Forward host notifications into VM (while keeping host mako)

# defaults: wisp@127.0.0.1:2222 and remote notify-send
cargo run -p wispd-forward

# or
nix run .#wispd-forward

Useful env vars:

  • WISPD_FORWARD_SSH_HOST (default: 127.0.0.1)
  • WISPD_FORWARD_SSH_PORT (default: 2222)
  • WISPD_FORWARD_SSH_USER (default: wisp)
  • WISPD_FORWARD_SSH_PASSWORD (default: wisp)
  • WISPD_FORWARD_NOTIFY_SEND (default: notify-send)
  • WISPD_FORWARD_SSH_STARTUP_WAIT_SECS (default: 60)
  • WISPD_FORWARD_SSH_STARTUP_POLL_MS (default: 500)

Configuration

Config file path:

  • $XDG_CONFIG_HOME/wispd/config.toml
  • fallback: ~/.config/wispd/config.toml

Runtime reload:

  • Send SIGHUP to wispd to reload config without restarting:
    • pkill -HUP -x wispd
    • or systemctl --user kill -s HUP wispd

Example:

left_click_action / right_click_action allowed values:

  • "dismiss"
  • "invoke-default-action" (invokes action key default)
[source]
default_timeout_ms = 5000
capabilities = ["body", "actions"]

[ui]
format = "{app_name}: {summary}\n{body}"
max_visible = 5
width = 420
height = 64
gap = 8
padding = 10
font_size = 15
# `font` is an alias for `font_family`
font = "sans-serif"
show_icons = true
max_icon_size = 32
anchor = "top-right"
# focused (recommended), last-output (sticky), any/none/default, or exact output name (e.g. "DP-1")
output = "focused"
# optional override: command that prints the currently focused output name (first line)
# when unset, "focused" uses compositor-picked output for first popup and sticky last-output for stack
# focused_output_command = "niri msg -j outputs | jq -r '.[] | select(.is_focused) | .name'"
show_timeout_progress = true
timeout_progress_height = 3
timeout_progress_position = "bottom"
left_click_action = "dismiss"
right_click_action = "invoke-default-action"

[ui.margin]
top = 16
right = 16
bottom = 16
left = 16

[ui.colors]
low = "#6aa9ff"
normal = "#7dcf7d"
critical = "#ff6b6b"
background = "#1e1e2ecc"
text = "#f8f8f2" # fallback text color
timeout_progress = "#f8f8f2"

[ui.text.app_name]
color = "#a89984"
font_size = 15

[ui.text.summary]
color = "#f8f8f2"
font_size = 15

[ui.text.body]
color = "#f8f8f2"
font_size = 15

[ui.buttons]
text_color = "#ebdbb2"
background = "#3c3836"
border_color = "#665c54"
hover_background = "#504945"
hover_text_color = "#fbf1c7"
# optional: defaults to ui.font
font = "Recursive Mono Casual Static"
# optional: defaults to ui.font_size
font_size = 15
# optional: defaults to (font_size or ui.font_size - 2)
close_font_size = 13

Home Manager module

This flake exports homeManagerModules.wispd.

Example:

{
  inputs.wispd.url = "github:dmnt/wispd";

  outputs = { self, nixpkgs, home-manager, wispd, ... }: {
    homeConfigurations.me = home-manager.lib.homeManagerConfiguration {
      pkgs = import nixpkgs { system = "x86_64-linux"; };
      modules = [
        wispd.homeManagerModules.wispd
        ({ ... }: {
          services.wispd.enable = true;
          services.wispd.rustLog = "info,wispd=debug,wisp_source=debug";
          # services.wispd.package = wispd.packages.${pkgs.system}.wispd; # optional override
        })
      ];
    };
  };
}

The module creates a systemd --user service (wispd.service) and sets a runtime LD_LIBRARY_PATH/PATH for Wayland and libxkbcommon dependencies.

It also provides a D-Bus activation service (org.freedesktop.Notifications) via dbus.packages and wires wispd.service as a D-Bus service (BusName=org.freedesktop.Notifications).

Useful module options:

  • services.wispd.autostart = true|false (default: true)
  • services.wispd.dbusActivation.enable = true|false (default: true)

Niri + wispd MicroVM (QEMU)

A ready-to-run MicroVM configuration is included via github:microvm-nix/microvm.nix.

What it configures:

  • QEMU MicroVM with graphics enabled
  • Niri compositor started at boot via greetd
  • wispd started as a systemd --user service from /work/wispd/target/debug/wispd
  • host workspace is shared into the guest at /work/wispd via a relative 9p share (source = ".")
  • alacritty installed (and exported as TERMINAL)
  • SSH enabled in guest, forwarded as host 127.0.0.1:2222 -> guest:22 (for wispd-forward)

Run it:

nix run .#wispd-microvm

Inside the VM:

  • user: wisp
  • graphical login: passwordless (auto-login via greetd)
  • SSH password (for wispd-forward): wisp

Test notifications in alacritty:

notify-send "hello" "from wispd microvm"

Hot-reload-ish dev loop (no VM reboot):

# host
cargo build -p wispd

# guest
systemctl --user restart wispd

Development

Rust checks:

cargo fmt
cargo clippy --workspace --all-targets
cargo test --workspace

Nix package build (uses ipetkov/crane for faster incremental dependency reuse):

nix build .#wispd

MicroVM development / validation:

# evaluate exposed outputs
nix flake show

# dry-run build of the runnable qemu microvm package
nix build .#wispd-microvm --dry-run

# run the VM
nix run .#wispd-microvm

MicroVM config source:

  • flake.nix (inputs + nixosConfigurations.wispd-microvm + app/package outputs)
  • nix/microvm/wispd-microvm.nix (Niri/greetd/wispd/alacritty VM config)

Dev convenience setup:

  • wispd-forward defaults are aligned with the bundled microvm (127.0.0.1:2222, user wisp, password wisp).
  • Forwarder startup polls until VM SSH is reachable, so it can be started before or during VM boot.

See docs/ARCHITECTURE.md for implementation details and current behavior.

About

A Wayland notification daemon implementing org.freedesktop.Notifications, with a layer-shell popup UI and debug tooling.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors