Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 718d3f99e9 | |||
| b2e8d5fc1b | |||
| 2572928195 |
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "fluxo-rs"
|
name = "fluxo-rs"
|
||||||
version = "0.3.3"
|
version = "0.4"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
@@ -1,18 +1,16 @@
|
|||||||
# fluxo-rs
|
# fluxo-rs
|
||||||
|
|
||||||
fluxo-rs is a high-performance system metrics daemon and client designed specifically for Waybar. It replaces standard shell scripts with a compiled Rust binary that collects data via a background polling loop and serves it over a Unix socket.
|
`fluxo-rs` is a high-performance system metrics daemon and client designed specifically for Waybar. It entirely replaces standard shell scripts with a compiled Rust binary that collects data via a background polling loop and serves it over a Unix socket.
|
||||||
|
|
||||||
|
With its **100% Native, Content-Based Event-Driven Architecture**, it consumes effectively 0% CPU while idle and signals Waybar to redraw *only* when the rendered UI text or icons physically change.
|
||||||
|
|
||||||
## Key Features
|
## Key Features
|
||||||
|
|
||||||
- **Asynchronous Architecture**: Built on **Tokio**, the daemon handles concurrent IPC requests and background tasks with zero latency and minimal CPU overhead.
|
- **100% Native Architecture**: Zero shell-outs or subprocesses. Uses `bluer` for Bluetooth, `libpulse-binding` for audio, `zbus` for MPRIS/DND, and `notify` for backlight.
|
||||||
- **Native Library Integrations**:
|
- **Content-Based Event Signaling**: `fluxo-rs` evaluates your custom configuration formats internally. It only sends a `SIGRTMIN+X` signal to Waybar if the resulting string or CSS class has actually changed, eliminating pointless re-renders from raw polling fluctuations.
|
||||||
- **Audio**: Direct `libpulse` integration for event-driven, instant volume and device updates.
|
- **Zero-Latency Interactions**: Direct library bindings mean that when you change your volume or connect a Bluetooth device via the CLI, the daemon updates instantly.
|
||||||
- **Bluetooth**: Native `bluer` integration for robust device monitoring.
|
- **Circuit Breaker (Failsafe)**: Automatically detects failing modules and enters a "Cool down" state, preventing resource waste and log spam. Fallback caching keeps your bar looking clean even during brief failures.
|
||||||
- **Pixel Buds Pro**: Custom native RPC implementation for real-time battery and ANC control.
|
- **Multi-threaded Polling**: Decoupled Tokio subsystem threads ensure that a hang in one system (e.g., a slow GPU probe) never freezes your Waybar.
|
||||||
- **Network**: Native `nix` and `/proc` inspection for high-speed interface monitoring.
|
|
||||||
- **Hyprland**: Direct IPC Unix socket communication for gamemode and animation status.
|
|
||||||
- **Circuit Breaker (Failsafe)**: Automatically detects failing modules and enters a "Cool down" state to prevent resource waste and log spam.
|
|
||||||
- **Multi-threaded Polling**: Decoupled subsystem threads ensure that a hang in one system (e.g., a slow GPU probe) never freezes your entire bar.
|
|
||||||
|
|
||||||
## Modules
|
## Modules
|
||||||
|
|
||||||
@@ -24,37 +22,47 @@ fluxo-rs is a high-performance system metrics daemon and client designed specifi
|
|||||||
| `sys` | System load and uptime | `{uptime}`, `{load1}`, `{load5}`, `{load15}`, `{procs}` |
|
| `sys` | System load and uptime | `{uptime}`, `{load1}`, `{load5}`, `{load15}`, `{procs}` |
|
||||||
| `disk` | Disk usage | `{mount}`, `{used}`, `{total}` |
|
| `disk` | Disk usage | `{mount}`, `{used}`, `{total}` |
|
||||||
| `pool` | Btrfs aggregate storage | `{used}`, `{total}` |
|
| `pool` | Btrfs aggregate storage | `{used}`, `{total}` |
|
||||||
|
| `gpu` | GPU usage & thermals | `{usage}`, `{vram_used}`, `{vram_total}`, `{temp}` |
|
||||||
| `vol` | Audio output (sink) | `{name}`, `{volume}`, `{icon}` |
|
| `vol` | Audio output (sink) | `{name}`, `{volume}`, `{icon}` |
|
||||||
| `mic` | Audio input (source) | `{name}`, `{volume}`, `{icon}` |
|
| `mic` | Audio input (source) | `{name}`, `{volume}`, `{icon}` |
|
||||||
| `bt` | Bluetooth status & plugins | `{alias}`, `{mac}`, `{left}`, `{right}`, `{anc}` |
|
| `bt` | Bluetooth status & plugins | `{alias}`, `{mac}`, `{left}`, `{right}`, `{anc}` |
|
||||||
| `power` | Battery and AC status | `{percentage}`, `{icon}` |
|
| `power` | Battery and AC status | `{percentage}`, `{icon}` |
|
||||||
| `game` | Hyprland status | active/inactive icons |
|
| `game` | Hyprland Gamemode status | active/inactive strings |
|
||||||
|
| `mpris` | Media Player status | `{artist}`, `{title}`, `{album}`, `{status_icon}` |
|
||||||
|
| `backlight` | Display Brightness | `{percentage}`, `{icon}` |
|
||||||
|
| `kbd` | Keyboard Layout | `{layout}` |
|
||||||
|
| `dnd` | Do Not Disturb (SwayNC) | active/inactive strings |
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
1. **Build**: `cargo build --release`
|
1. **Build**: `cargo build --release`
|
||||||
2. **Configure**: Create `~/.config/fluxo/config.toml` (see `example.config.toml`).
|
2. **Configure**: Create `~/.config/fluxo/config.toml` (see `example.config.toml`). Ensure you map your `[signals]`.
|
||||||
3. **Daemon**: Start `fluxo-rs daemon`. It's recommended to run this as a systemd user service.
|
3. **Daemon**: Start `fluxo-rs daemon`. It is highly recommended to run this as a systemd user service.
|
||||||
|
|
||||||
## Waybar Configuration
|
## Waybar Configuration
|
||||||
|
|
||||||
To achieve zero-latency updates, use **Waybar Signals**:
|
To achieve zero-latency updates and zero-polling CPU usage, set `interval: 0` on your modules and rely entirely on **Waybar Signals** mapped in your `config.toml`:
|
||||||
|
|
||||||
```jsonc
|
```jsonc
|
||||||
"custom/audio": {
|
"custom/volume": {
|
||||||
"exec": "fluxo vol",
|
"exec": "fluxo-rs vol",
|
||||||
"return-type": "json",
|
"return-type": "json",
|
||||||
"interval": 5,
|
"interval": 0,
|
||||||
"signal": 8,
|
"signal": 8, // Must match the value in config.toml [signals]
|
||||||
"on-click": "fluxo audio cycle sink && pkill -RTMIN+8 waybar"
|
"on-click": "fluxo-rs vol mute",
|
||||||
|
"on-scroll-up": "fluxo-rs vol up 1",
|
||||||
|
"on-scroll-down": "fluxo-rs vol down 1",
|
||||||
|
"on-click-right": "fluxo-rs vol cycle"
|
||||||
},
|
},
|
||||||
"custom/bluetooth": {
|
"custom/bluetooth-audio": {
|
||||||
"exec": "fluxo bt",
|
"format": "{}",
|
||||||
"return-type": "json",
|
"return-type": "json",
|
||||||
"interval": 5,
|
"exec": "fluxo-rs bt",
|
||||||
|
"on-click": "fluxo-rs bt menu",
|
||||||
|
"on-click-right": "fluxo-rs bt cycle_mode",
|
||||||
"signal": 9,
|
"signal": 9,
|
||||||
"on-click": "fluxo bt menu && pkill -RTMIN+9 waybar",
|
"interval": 0,
|
||||||
"on-click-right": "fluxo bt cycle_mode && pkill -RTMIN+9 waybar"
|
"tooltip": true
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
+62
-11
@@ -1,46 +1,97 @@
|
|||||||
# Fluxo configuration example
|
# Fluxo configuration example
|
||||||
|
# Place this at ~/.config/fluxo/config.toml
|
||||||
|
|
||||||
|
# Note: All tokens support standard alignment, padding, and precision specifiers dynamically.
|
||||||
|
# For example, you can change {rx:>5.2} to {rx:<8.1} or {used} to {used:^4.0} directly here.
|
||||||
|
|
||||||
[general]
|
[general]
|
||||||
|
# command used for interactive menus (e.g., bluetooth device selection)
|
||||||
|
# $FLUXO_PROMPT is securely injected to prevent shell escaping issues
|
||||||
menu_command = "fuzzel --dmenu --prompt \"$FLUXO_PROMPT\""
|
menu_command = "fuzzel --dmenu --prompt \"$FLUXO_PROMPT\""
|
||||||
|
# For Wofi use: menu_command = "wofi --show dmenu --prompt \"$FLUXO_PROMPT\""
|
||||||
|
|
||||||
|
# Map modules to specific Waybar signals for zero-latency, event-driven UI updates.
|
||||||
|
# These MUST match the `signal` configuration in your waybar config.jsonc
|
||||||
|
[signals]
|
||||||
|
network = 1
|
||||||
|
cpu = 2
|
||||||
|
memory = 3
|
||||||
|
gpu = 4
|
||||||
|
sys = 5
|
||||||
|
disk = 6
|
||||||
|
game = 7
|
||||||
|
audio = 8
|
||||||
|
bt = 9
|
||||||
|
power = 10
|
||||||
|
keyboard = 11
|
||||||
|
mpris = 12
|
||||||
|
backlight = 13
|
||||||
|
dnd = 14
|
||||||
|
|
||||||
[network]
|
[network]
|
||||||
format = "{interface} ({ip}): {rx:>5.2} MB/s {tx:>5.2} MB/s"
|
# tokens: {interface}, {ip}, {rx}, {tx}
|
||||||
|
format = "{interface} ({ip}): {rx:^4.1} MB/s {tx:^4.1} MB/s"
|
||||||
|
|
||||||
[cpu]
|
[cpu]
|
||||||
format = "CPU: {usage:>4.1}% {temp:>4.1}C"
|
# tokens: {usage}, {temp}, {model}
|
||||||
|
format = "CPU: {usage:^4.1}% {temp:^4.1}C"
|
||||||
|
|
||||||
[memory]
|
[memory]
|
||||||
format = "{used:>5.2}/{total:>5.2}GB"
|
# tokens: {used}, {total}
|
||||||
|
format = "MEM: {used:^4.1}/{total:^4.1}GB"
|
||||||
|
|
||||||
[gpu]
|
[gpu]
|
||||||
|
# tokens: {usage}, {vram_used}, {vram_total}, {temp}
|
||||||
format_amd = "AMD: {usage:>3.0}% {vram_used:>4.1}/{vram_total:>4.1}GB {temp:>4.1}C"
|
format_amd = "AMD: {usage:>3.0}% {vram_used:>4.1}/{vram_total:>4.1}GB {temp:>4.1}C"
|
||||||
format_intel = "iGPU: {usage:>3.0}%"
|
format_intel = "iGPU: {usage:>3.0}%"
|
||||||
format_nvidia = "NV: {usage:>3.0}% {vram_used:>4.1}/{vram_total:>4.1}GB {temp:>4.1}C"
|
format_nvidia = "NV: {usage:>3.0}% {vram_used:>4.1}/{vram_total:>4.1}GB {temp:>4.1}C"
|
||||||
|
|
||||||
[sys]
|
[sys]
|
||||||
format = "UP: {uptime} | LOAD: {load1:>4.2} {load5:>4.2} {load15:>4.2}"
|
# tokens: {uptime}, {load1}, {load5}, {load15}, {procs}
|
||||||
|
format = "UP: {uptime} LOAD: {load1:^3.1} "
|
||||||
|
|
||||||
[disk]
|
[disk]
|
||||||
format = "{mount} {used:>5.1}/{total:>5.1}G"
|
# tokens: {mount}, {used}, {total}
|
||||||
|
format = "{mount} {used:^3.0}/{total:^3.0}G"
|
||||||
|
|
||||||
[pool]
|
[pool]
|
||||||
|
# tokens: {used}, {total}
|
||||||
format = "{used:>4.0}G / {total:>4.0}G"
|
format = "{used:>4.0}G / {total:>4.0}G"
|
||||||
|
|
||||||
[power]
|
[power]
|
||||||
|
# tokens: {percentage}, {icon}
|
||||||
format = "{percentage:>3}% {icon}"
|
format = "{percentage:>3}% {icon}"
|
||||||
|
|
||||||
[bt]
|
|
||||||
format_connected = "{alias} "
|
|
||||||
format_plugin = "{alias} [{left}|{right}] {anc} "
|
|
||||||
format_disconnected = ""
|
|
||||||
format_disabled = " Off"
|
|
||||||
|
|
||||||
[audio]
|
[audio]
|
||||||
|
# tokens: {name}, {volume}, {icon}
|
||||||
format_sink_unmuted = "{name} {volume:>3}% {icon}"
|
format_sink_unmuted = "{name} {volume:>3}% {icon}"
|
||||||
format_sink_muted = "{name} {icon}"
|
format_sink_muted = "{name} {icon}"
|
||||||
format_source_unmuted = "{name} {volume:>3}% {icon}"
|
format_source_unmuted = "{name} {volume:>3}% {icon}"
|
||||||
format_source_muted = "{name} {icon}"
|
format_source_muted = "{name} {icon}"
|
||||||
|
|
||||||
|
[bt]
|
||||||
|
# tokens: {alias}, {mac}, {left}, {right}, {anc}
|
||||||
|
format_plugin = "{alias} [{left}|{right}] {anc} "
|
||||||
|
format_connected = " {alias}"
|
||||||
|
format_disconnected = " Disconnected"
|
||||||
|
format_disabled = " Off"
|
||||||
|
|
||||||
[game]
|
[game]
|
||||||
format_active = "<span size='large'></span>"
|
format_active = "<span size='large'></span>"
|
||||||
format_inactive = "<span size='large'></span>"
|
format_inactive = "<span size='large'></span>"
|
||||||
|
|
||||||
|
[mpris]
|
||||||
|
# tokens: {artist}, {title}, {album}, {status_icon}
|
||||||
|
format = "{status_icon} {artist} - {title}"
|
||||||
|
|
||||||
|
[backlight]
|
||||||
|
# tokens: {percentage}, {icon}
|
||||||
|
format = "{percentage:>3}% {icon}"
|
||||||
|
|
||||||
|
[keyboard]
|
||||||
|
# tokens: {layout}
|
||||||
|
format = "{layout}"
|
||||||
|
|
||||||
|
[dnd]
|
||||||
|
format_dnd = "<span size='large'></span>"
|
||||||
|
format_normal = "<span size='large'></span>"
|
||||||
|
|||||||
+1
-3
@@ -131,9 +131,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generic module dispatch
|
// Generic module dispatch
|
||||||
// To handle module-specific default targets like "vol up" -> "vol sink up" or "mic up" -> "vol source up"
|
// Translate module-specific shorthand targets
|
||||||
// Wait, `daemon.rs` `evaluate_module_for_signaler` defaults `vol` to `sink show`.
|
|
||||||
// If we map `mic` to `["vol", "source"]` in `main.rs` instead of keeping `mic` in daemon:
|
|
||||||
let (actual_module, actual_args) = if module == "vol" {
|
let (actual_module, actual_args) = if module == "vol" {
|
||||||
let mut new_args = vec!["sink".to_string()];
|
let mut new_args = vec!["sink".to_string()];
|
||||||
new_args.extend(cli.args.clone());
|
new_args.extend(cli.args.clone());
|
||||||
|
|||||||
@@ -109,9 +109,8 @@ impl MprisDaemon {
|
|||||||
|
|
||||||
info!("Connected to D-Bus for MPRIS monitoring");
|
info!("Connected to D-Bus for MPRIS monitoring");
|
||||||
|
|
||||||
// Simple poll loop for MPRIS since players can come and go, and tracking dynamically
|
// Periodically poll for the active player and update the MPRIS state.
|
||||||
// with pure signals across multiple dynamically spawned DBus names is complex.
|
// This avoids complex dynamic signal tracking across ephemeral player instances.
|
||||||
// A robust hybrid approach: poll every 2s for active players, and update state.
|
|
||||||
|
|
||||||
let dbus_proxy = DBusProxy::new(&connection).await?;
|
let dbus_proxy = DBusProxy::new(&connection).await?;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user