add custom i3status

This commit is contained in:
LordMZTE 2021-08-12 12:50:44 +02:00
parent 3d69167fdb
commit 42fa35e223
7 changed files with 261 additions and 0 deletions

2
i3status/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
target
Cargo.lock

19
i3status/Cargo.toml Normal file
View file

@ -0,0 +1,19 @@
[package]
name = "i3status"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
heim = "0.0.11"
serde_json = "1.0.66"
chrono = "0.4.19"
[dependencies.serde]
version = "1.0.127"
features = ["derive"]
[dependencies.tokio]
version = "1.9.0"
features = ["macros", "rt-multi-thread", "time", "sync", "process"]

3
i3status/README.md Normal file
View file

@ -0,0 +1,3 @@
# i3status
custom i3status replacement.
to use with my config, build this and move it to `~/.config/i3/i3status`

11
i3status/src/colors.rs Normal file
View file

@ -0,0 +1,11 @@
#![allow(dead_code)]
pub const CYAN: &str = "#8be9fd";
pub const GREEN: &str = "#50fa7b";
pub const GREY: &str = "#6272a4";
pub const ORANGE: &str = "#ffb86c";
pub const PINK: &str = "#ff79c6";
pub const PURPLE: &str = "#bd93f9";
pub const RED: &str = "#ff5555";
pub const WHITE: &str = "#f8f8f2";
pub const YELLOW: &str = "#f1fa8c";

7
i3status/src/json.rs Normal file
View file

@ -0,0 +1,7 @@
use serde::Serialize;
#[derive(Debug, Serialize)]
pub struct Block {
pub full_text: String,
// this is always a const color string, so this is fine for now
pub color: &'static str,
}

65
i3status/src/main.rs Normal file
View file

@ -0,0 +1,65 @@
use std::{sync::Arc, time::Duration};
use tokio::sync::RwLock;
use crate::json::Block;
mod colors;
mod json;
mod workers;
#[tokio::main]
async fn main() {
let mut int = tokio::time::interval(Duration::from_millis(100));
let bar = Arc::new(RwLock::new(Bar::default()));
spawn_workers(Arc::clone(&bar)).await;
println!("{}\n[[]", r#"{ "version": 1 }"#);
loop {
int.tick().await;
let bar = bar.read().await;
let blocks = vec![
Block {
full_text: format!("龍 {:>7}", &bar.cpu_freq),
color: colors::CYAN,
},
Block {
full_text: format!(" {}", &bar.ram),
color: colors::PURPLE,
},
Block {
full_text: format!("{:>5}", &bar.vol),
color: colors::ORANGE,
},
Block {
full_text: format!("{:>5}", &bar.battery),
color: colors::GREEN,
},
Block {
full_text: bar.time.clone(),
color: colors::WHITE,
},
];
let json = serde_json::to_string(&blocks).expect("failed to create json");
println!(",{}", json);
}
}
async fn spawn_workers(bar: Arc<RwLock<Bar>>) {
tokio::spawn(workers::ram(Arc::clone(&bar)));
tokio::spawn(workers::time(Arc::clone(&bar)));
tokio::spawn(workers::pulseaudio_vol(Arc::clone(&bar)));
tokio::spawn(workers::cpu_freq(Arc::clone(&bar)));
tokio::spawn(workers::battery(Arc::clone(&bar)));
}
#[derive(Default)]
struct Bar {
ram: String,
time: String,
vol: String,
cpu_freq: String,
battery: String,
}

154
i3status/src/workers.rs Normal file
View file

@ -0,0 +1,154 @@
use crate::Bar;
use chrono::Local;
use heim::units::frequency::megahertz;
use heim::{memory::os::linux::MemoryExt, units::information::megabyte};
use std::sync::Arc;
use std::time::Duration;
use tokio::process::Command;
use tokio::sync::RwLock;
pub(crate) async fn ram(bar: Arc<RwLock<Bar>>) {
let mut int = tokio::time::interval(Duration::from_secs(1));
loop {
let mem = heim::memory::memory().await;
let ram;
if let Ok(mem) = mem {
ram = format!(
"{}MB/{}MB",
mem.used().get::<megabyte>(),
mem.free().get::<megabyte>()
);
} else {
ram = String::from("error reading ram :(");
}
bar.write().await.ram = ram;
int.tick().await;
}
}
pub(crate) async fn time(bar: Arc<RwLock<Bar>>) {
let mut int = tokio::time::interval(Duration::from_millis(200));
loop {
let time = Local::now().format("%a %d.%m.%Y %T").to_string();
bar.write().await.time = time;
int.tick().await;
}
}
pub(crate) async fn pulseaudio_vol(bar: Arc<RwLock<Bar>>) {
let mut int = tokio::time::interval(Duration::from_secs(2));
loop {
let res = Command::new("pactl")
.arg("get-sink-volume")
.arg("@DEFAULT_SINK@")
.output()
.await
.ok()
.and_then(|o| String::from_utf8(o.stdout).ok());
let mute = Command::new("pactl")
.arg("get-sink-mute")
.arg("@DEFAULT_SINK@")
.output()
.await
.map(|o| o.stdout.contains(&b'y'))
.unwrap_or(false);
let msg;
match res {
Some(out) => {
// get first line
let out = out.lines().next().unwrap_or(&out);
let volumes = out
.split(' ')
.filter(|s| s.contains('%'))
.map(|s| s.trim().replace('%', "").parse::<u8>())
.collect::<Result<Vec<_>, _>>()
.unwrap_or_else(|_| vec![]);
let mut avg = 0u8;
for v in &volumes {
avg += v / volumes.len() as u8;
}
let symbol = if mute { '遼' } else { '蓼' };
msg = format!("{} {}%", symbol, avg);
}
None => msg = String::from("PA Error :("),
}
bar.write().await.vol = msg;
int.tick().await;
}
}
pub(crate) async fn cpu_freq(bar: Arc<RwLock<Bar>>) {
let mut int = tokio::time::interval(Duration::from_secs(2));
loop {
let freq = heim::cpu::frequency().await;
let txt;
if let Ok(freq) = freq {
let freq = freq.current().get::<megahertz>();
txt = format!("{}MHz", freq);
} else {
txt = String::from("Error reading CPU frequency :(");
}
bar.write().await.cpu_freq = txt;
int.tick().await;
}
}
pub(crate) async fn battery(bar: Arc<RwLock<Bar>>) {
enum BatteryState {
Charging,
Discharging,
NotCharging,
}
let mut int = tokio::time::interval(Duration::from_secs(10));
loop {
let out = Command::new("acpi")
.output()
.await
.ok()
.and_then(|o| String::from_utf8(o.stdout).ok());
let txt;
if let Some(s) = out {
if let Some(bat) = s.lines().next() {
let percent = bat.split(' ').find(|s| s.contains('%')).unwrap_or("0%");
let state = if bat.contains("Charging") {
BatteryState::Charging
} else if bat.contains("Discharging") {
BatteryState::Discharging
} else {
BatteryState::NotCharging
};
let icon = match state {
BatteryState::Charging => '',
BatteryState::Discharging => '',
BatteryState::NotCharging => '',
};
txt = format!("{} {}", icon, percent);
} else {
txt = String::from("No bat")
}
} else {
txt = String::from("Battery Error :(");
}
bar.write().await.battery = txt;
int.tick().await;
}
}