2 Commits

Author SHA1 Message Date
nvrl c1152ce1b9 added .deb build target + systemd unit + updated readme
Release / Build and Release (push) Successful in 2m57s
2026-04-07 16:24:44 +02:00
nvrl 230604dae3 changed bin name to fluxo
Release / Build and Release (push) Successful in 2m53s
2026-04-07 12:01:54 +02:00
7 changed files with 161 additions and 36 deletions
+10 -10
View File
@@ -64,31 +64,31 @@ jobs:
VERSION: ${{ steps.get_version.outputs.VERSION }} VERSION: ${{ steps.get_version.outputs.VERSION }}
TAG: ${{ steps.get_version.outputs.TAG }} TAG: ${{ steps.get_version.outputs.TAG }}
run: | run: |
PKG="fluxo-rs_${VERSION}_amd64" PKG="fluxo_${VERSION}_amd64"
mkdir -p "${PKG}/DEBIAN" mkdir -p "${PKG}/DEBIAN"
mkdir -p "${PKG}/usr/bin" mkdir -p "${PKG}/usr/bin"
cp target/release/fluxo-rs "${PKG}/usr/bin/" cp target/release/fluxo "${PKG}/usr/bin/"
strip "${PKG}/usr/bin/fluxo-rs" strip "${PKG}/usr/bin/fluxo"
printf '%s\n' \ printf '%s\n' \
"Package: fluxo-rs" \ "Package: fluxo" \
"Version: ${VERSION}" \ "Version: ${VERSION}" \
"Section: utils" \ "Section: utils" \
"Priority: optional" \ "Priority: optional" \
"Architecture: amd64" \ "Architecture: amd64" \
"Maintainer: fluxo-rs contributors" \ "Maintainer: fluxo contributors" \
"Description: High-performance daemon/client for Waybar custom modules" \ "Description: High-performance daemon/client for Waybar custom modules" \
" fluxo-rs is a compiled Rust daemon that polls system metrics and" \ " fluxo is a compiled Rust daemon that polls system metrics and" \
" serves formatted JSON output to Waybar custom modules over a Unix" \ " serves formatted JSON output to Waybar custom modules over a Unix" \
" domain socket. Replaces shell scripts with a single binary." \ " domain socket. Replaces shell scripts with a single binary." \
> "${PKG}/DEBIAN/control" > "${PKG}/DEBIAN/control"
dpkg-deb --build "${PKG}" dpkg-deb --build "${PKG}"
mv "${PKG}.deb" "fluxo-rs-${TAG}-amd64.deb" mv "${PKG}.deb" "fluxo-${TAG}-amd64.deb"
echo "Built: fluxo-rs-${TAG}-amd64.deb" echo "Built: fluxo-${TAG}-amd64.deb"
- name: Create Release and Upload Assets - name: Create Release and Upload Assets
if: steps.check_release.outputs.EXISTS == 'false' if: steps.check_release.outputs.EXISTS == 'false'
@@ -101,8 +101,8 @@ jobs:
Commit: ${{ github.sha }} Commit: ${{ github.sha }}
Branch: ${{ github.ref_name }} Branch: ${{ github.ref_name }}
files: | files: |
fluxo-rs-${{ steps.get_version.outputs.TAG }}-amd64.deb fluxo-${{ steps.get_version.outputs.TAG }}-amd64.deb
target/release/fluxo-rs target/release/fluxo
draft: false draft: false
prerelease: false prerelease: false
env: env:
Generated
+1 -1
View File
@@ -572,7 +572,7 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
[[package]] [[package]]
name = "fluxo-rs" name = "fluxo-rs"
version = "0.5.2" version = "0.5.4"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bluer", "bluer",
+18 -1
View File
@@ -1,8 +1,12 @@
[package] [package]
name = "fluxo-rs" name = "fluxo-rs"
version = "0.5.2" version = "0.5.4"
edition = "2024" edition = "2024"
[[bin]]
name = "fluxo"
path = "src/main.rs"
[features] [features]
default = ["mod-audio", "mod-bt", "mod-network", "mod-hardware", "mod-dbus"] default = ["mod-audio", "mod-bt", "mod-network", "mod-hardware", "mod-dbus"]
mod-audio = ["dep:libpulse-binding"] mod-audio = ["dep:libpulse-binding"]
@@ -38,3 +42,16 @@ zbus = { version = "5", optional = true }
[dev-dependencies] [dev-dependencies]
tempfile = "3" tempfile = "3"
[package.metadata.deb]
maintainer = "Nils Pukropp"
copyright = "2024-2026 Nils Pukropp"
depends = "$auto"
section = "utils"
priority = "optional"
assets = [
["target/release/fluxo", "usr/bin/", "755"],
["dist/fluxo.service", "usr/lib/systemd/user/", "644"],
["README.md", "usr/share/doc/fluxo/", "644"],
["example.config.toml", "usr/share/doc/fluxo/", "644"],
]
+79 -16
View File
@@ -1,13 +1,13 @@
# fluxo-rs # fluxo
`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. `fluxo` 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. 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
- **100% Native Architecture**: Zero shell-outs or subprocesses. Uses `bluer` for Bluetooth, `libpulse-binding` for audio, `zbus` for MPRIS/DND, and `notify` for backlight. - **100% Native Architecture**: Zero shell-outs or subprocesses. Uses `bluer` for Bluetooth, `libpulse-binding` for audio, `zbus` for MPRIS/DND, and `notify` for backlight.
- **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. - **Content-Based Event Signaling**: `fluxo` 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.
- **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. - **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.
- **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. - **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.
- **Multi-threaded Polling**: Decoupled Tokio subsystem threads ensure that a hang in one system (e.g., a slow GPU probe) never freezes your Waybar. - **Multi-threaded Polling**: Decoupled Tokio subsystem threads ensure that a hang in one system (e.g., a slow GPU probe) never freezes your Waybar.
@@ -33,11 +33,60 @@ With its **100% Native, Content-Based Event-Driven Architecture**, it consumes e
| `kbd` | Keyboard Layout | `{layout}` | | `kbd` | Keyboard Layout | `{layout}` |
| `dnd` | Do Not Disturb (SwayNC) | active/inactive strings | | `dnd` | Do Not Disturb (SwayNC) | active/inactive strings |
## Installation
### From Source
```bash
cargo build --release
cp target/release/fluxo ~/.cargo/bin/
```
### Debian/Ubuntu (.deb)
```bash
cargo install cargo-deb
cargo deb
sudo dpkg -i target/debian/fluxo-rs_*.deb
```
The `.deb` package installs the binary to `/usr/bin/fluxo`, the systemd user service to `/usr/lib/systemd/user/fluxo.service`, and documentation to `/usr/share/doc/fluxo/`.
## Setup ## Setup
1. **Build**: `cargo build --release` 1. **Configure**: Create `~/.config/fluxo/config.toml` (see `example.config.toml`). Ensure you map your `[signals]`.
2. **Configure**: Create `~/.config/fluxo/config.toml` (see `example.config.toml`). Ensure you map your `[signals]`. 2. **Start the daemon** via systemd (recommended) or manually:
3. **Daemon**: Start `fluxo-rs daemon`. It is highly recommended to run this as a systemd user service.
### systemd (recommended)
If installed from the `.deb`, the service file is already in place. For manual installs:
```bash
mkdir -p ~/.config/systemd/user
cp dist/fluxo.service ~/.config/systemd/user/
```
If your binary is not at `~/.cargo/bin/fluxo`, edit the `ExecStart=` path in the service file.
Then enable and start:
```bash
systemctl --user daemon-reload
systemctl --user enable --now fluxo
```
Check status:
```bash
systemctl --user status fluxo
journalctl --user -u fluxo -f
```
### Manual
```bash
fluxo daemon
```
## Waybar Configuration ## Waybar Configuration
@@ -45,21 +94,21 @@ To achieve zero-latency updates and zero-polling CPU usage, set `interval: 0` on
```jsonc ```jsonc
"custom/volume": { "custom/volume": {
"exec": "fluxo-rs vol", "exec": "fluxo vol",
"return-type": "json", "return-type": "json",
"interval": 0, "interval": 0,
"signal": 8, // Must match the value in config.toml [signals] "signal": 8, // Must match the value in config.toml [signals]
"on-click": "fluxo-rs vol mute", "on-click": "fluxo vol mute",
"on-scroll-up": "fluxo-rs vol up 1", "on-scroll-up": "fluxo vol up 1",
"on-scroll-down": "fluxo-rs vol down 1", "on-scroll-down": "fluxo vol down 1",
"on-click-right": "fluxo-rs vol cycle" "on-click-right": "fluxo vol cycle"
}, },
"custom/bluetooth-audio": { "custom/bluetooth-audio": {
"format": "{}", "format": "{}",
"return-type": "json", "return-type": "json",
"exec": "fluxo-rs bt", "exec": "fluxo bt",
"on-click": "fluxo-rs bt menu", "on-click": "fluxo bt menu",
"on-click-right": "fluxo-rs bt cycle_mode", "on-click-right": "fluxo bt cycle_mode",
"signal": 9, "signal": 9,
"interval": 0, "interval": 0,
"tooltip": true "tooltip": true
@@ -68,7 +117,21 @@ To achieve zero-latency updates and zero-polling CPU usage, set `interval: 0` on
## Debugging ## Debugging
Start the daemon with `RUST_LOG=debug` to see detailed logs of library interactions and circuit breaker status: Use `--loglevel` to control log verbosity (trace, debug, info, warn, error):
```bash ```bash
RUST_LOG=debug fluxo-rs daemon fluxo daemon --loglevel debug
```
Or via the `RUST_LOG` environment variable:
```bash
RUST_LOG=debug fluxo daemon
```
For module help and available arguments:
```bash
fluxo help # overview of all modules
fluxo help vol # detailed help for a specific module
``` ```
+4 -3
View File
@@ -466,11 +466,12 @@ fn print_module_detail(m: &ModuleHelp) {
println!("\x1b[1mFORMAT TOKENS:\x1b[0m (for use in config.toml format strings)\n"); println!("\x1b[1mFORMAT TOKENS:\x1b[0m (for use in config.toml format strings)\n");
let max_token = m.tokens.iter().map(|(t, _)| t.len()).max().unwrap_or(0); let max_token = m.tokens.iter().map(|(t, _)| t.len()).max().unwrap_or(0);
for (token, desc) in m.tokens { for (token, desc) in m.tokens {
let padded = format!("{{{}}}", token);
println!( println!(
" \x1b[33m{{{:<width$}}}\x1b[0m {}", " \x1b[33m{:<width$}\x1b[0m {}",
token, padded,
desc, desc,
width = max_token width = max_token + 2 // +2 for the braces
); );
} }
println!(); println!();
+48 -4
View File
@@ -30,20 +30,49 @@ mod signaler;
mod state; mod state;
mod utils; mod utils;
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand, ValueEnum};
use std::path::PathBuf; use std::path::PathBuf;
use std::process; use std::process;
use tracing::{error, info}; use tracing::{error, info};
use tracing_subscriber::{EnvFilter, fmt, prelude::*}; use tracing_subscriber::{EnvFilter, fmt, prelude::*};
#[derive(Clone, ValueEnum)]
enum LogLevel {
Trace,
Debug,
Info,
Warn,
Error,
}
impl From<LogLevel> for tracing::Level {
fn from(level: LogLevel) -> Self {
match level {
LogLevel::Trace => tracing::Level::TRACE,
LogLevel::Debug => tracing::Level::DEBUG,
LogLevel::Info => tracing::Level::INFO,
LogLevel::Warn => tracing::Level::WARN,
LogLevel::Error => tracing::Level::ERROR,
}
}
}
#[derive(Parser)] #[derive(Parser)]
#[command(name = "fluxo")] #[command(name = "fluxo")]
#[command(about = "A high-performance daemon/client for Waybar custom modules", long_about = None)] #[command(about = "A high-performance daemon/client for Waybar custom modules", long_about = None)]
#[command(disable_help_subcommand = true)] #[command(disable_help_subcommand = true, disable_help_flag = true)]
struct Cli { struct Cli {
#[command(subcommand)] #[command(subcommand)]
command: Option<Commands>, command: Option<Commands>,
/// Print help information
#[arg(short, long, global = true)]
help: bool,
/// Set the log level (trace, debug, info, warn, error)
#[arg(long, global = true, value_enum)]
loglevel: Option<LogLevel>,
/// Module name to query or interact with /// Module name to query or interact with
module: Option<String>, module: Option<String>,
@@ -70,12 +99,27 @@ enum Commands {
} }
fn main() { fn main() {
let cli = Cli::parse();
// Explicit --loglevel takes priority, then RUST_LOG env var, then a
// sensible default: INFO for the daemon, WARN for client commands.
let default_level = if let Some(level) = &cli.loglevel {
tracing::Level::from(level.clone())
} else if matches!(&cli.command, Some(Commands::Daemon { .. })) {
tracing::Level::INFO
} else {
tracing::Level::WARN
};
tracing_subscriber::registry() tracing_subscriber::registry()
.with(fmt::layer().with_target(false).pretty()) .with(fmt::layer().with_target(false).pretty())
.with(EnvFilter::from_default_env().add_directive(tracing::Level::INFO.into())) .with(EnvFilter::from_default_env().add_directive(default_level.into()))
.init(); .init();
let cli = Cli::parse(); if cli.help {
help::print_help(None);
return;
}
if let Some(command) = &cli.command { if let Some(command) = &cli.command {
match command { match command {
+1 -1
View File
@@ -58,7 +58,7 @@ impl AudioDaemon {
ThreadedMainloop::new().expect("Failed to create pulse threaded mainloop"); ThreadedMainloop::new().expect("Failed to create pulse threaded mainloop");
let mut context = let mut context =
Context::new(&mainloop, "fluxo-rs").expect("Failed to create pulse context"); Context::new(&mainloop, "fluxo").expect("Failed to create pulse context");
context context
.connect(None, ContextFlag::NOFLAGS, None) .connect(None, ContextFlag::NOFLAGS, None)