A high-performance Rust daemon that provides system services for Nirvana Shell.
- Niri IPC - Real-time workspace and window events via niri's event stream
- Audio Control - PulseAudio/PipeWire volume and mute control
- Notifications - D-Bus notification daemon (org.freedesktop.Notifications)
- Clipboard - Wayland clipboard read/write via wl-clipboard
cargo build --releaseBinary will be at target/release/nirvana-daemon (~2.7MB stripped).
./install.shThis will:
- Build the release binary
- Install systemd user service
- Enable on graphical session start
systemctl --user start nirvana-daemonsystemctl --user enable nirvana-daemonsystemctl --user status nirvana-daemonjournalctl --user -u nirvana-daemon -fThe daemon exposes a Unix socket at /tmp/nirvana-daemon.sock.
{"method": "GetState"}
{"method": "SetVolume", "params": {"volume": 50}}
{"method": "ToggleMute"}
{"method": "FocusWorkspace", "params": {"id": 1}}
{"method": "Subscribe"}{"type": "Ok"}
{"type": "Error", "data": {"message": "..."}}
{"type": "State", "data": {...}}
{"type": "Event", "data": {...}}{"type": "Event", "data": {"type": "WorkspacesChanged", "data": [...]}}
{"type": "Event", "data": {"type": "VolumeChanged", "data": {"volume": 50, "muted": false}}}
{"type": "Event", "data": {"type": "NotificationAdded", "data": {...}}}nirvana-daemon
├── main.rs # Entry point, service orchestration
├── state.rs # Shared state (workspaces, windows, audio, notifications)
├── server.rs # Unix socket server for QML clients
├── niri.rs # Niri IPC event streaming
├── audio.rs # PulseAudio/PipeWire integration
├── notifications.rs # D-Bus notification service
└── clipboard.rs # wl-clipboard wrapper
niri-ipc- Niri IPC types and protocoltokio- Async runtimezbus- D-Bus for notificationslibpulse-binding- PulseAudio controlserde_json- JSON serializationtracing- Structured logging
Connect from Quickshell QML:
import QtQuick
import Quickshell.Io
PersistentProcess {
running: true
stdinEnabled: true
onStarted: {
write('{"method": "Subscribe"}\n');
}
stdout: SplitParser {
onRead: data => {
const event = JSON.parse(data);
if (event.type === "Event") {
handleEvent(event.data);
}
}
}
}MIT