mirror of
https://github.com/house-of-vanity/tmux-helper.git
synced 2026-02-04 17:57:58 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d9b5643ae5 | ||
|
|
eb3577979c | ||
|
|
c082c328f5 |
14
Cargo.toml
14
Cargo.toml
@@ -1,14 +1,14 @@
|
||||
[package]
|
||||
name = "tmux-helper"
|
||||
version = "0.3.5"
|
||||
version = "0.4.0"
|
||||
description = "Utility for printing system info for tmux status line."
|
||||
authors = ["Ultra Desu <ultradesu@hexor.ru>"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
sys-info = "*"
|
||||
dbus = "*"
|
||||
chrono = "*"
|
||||
mpd = "*"
|
||||
clap = "*"
|
||||
size_format = "1.0"
|
||||
sysinfo = "0.36.0"
|
||||
dbus = "0.9"
|
||||
chrono = "0.4"
|
||||
mpd = "0.1"
|
||||
clap = "4"
|
||||
size_format = "1"
|
||||
|
||||
@@ -22,6 +22,10 @@ pub struct Config {
|
||||
pub mpd_server: String,
|
||||
pub lt_format: Option<String>,
|
||||
pub ut_format: Option<String>,
|
||||
pub bar_symbol: Option<String>,
|
||||
pub bar_empty_symbol: Option<String>,
|
||||
pub low_threshold: f32,
|
||||
pub mid_threshold: f32,
|
||||
pub color_low: String,
|
||||
pub color_mid: String,
|
||||
pub color_high: String,
|
||||
@@ -55,6 +59,20 @@ pub fn read() -> Config {
|
||||
.action(clap::ArgAction::SetTrue)
|
||||
.help("Print mem usage bar."),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("low")
|
||||
.long("low")
|
||||
.help("Low threshold (0.0 - 1.0)")
|
||||
.value_parser(clap::value_parser!(f32))
|
||||
.default_value("0.7"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("mid")
|
||||
.long("mid")
|
||||
.help("Mid threshold (0.0 - 1.0)")
|
||||
.value_parser(clap::value_parser!(f32))
|
||||
.default_value("0.9"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("mpris")
|
||||
.short('p')
|
||||
@@ -85,6 +103,22 @@ pub fn read() -> Config {
|
||||
.num_args(0..=1)
|
||||
.default_missing_value("%H:%M"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("bar_symbol")
|
||||
.short('s')
|
||||
.long("symbol")
|
||||
.help("Symbol to build bar")
|
||||
.num_args(0..=1)
|
||||
.default_value("▮"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("bar_empty_symbol")
|
||||
.short('e')
|
||||
.long("empty-symbol")
|
||||
.help("Symbol to represent the empty part of the bar")
|
||||
.num_args(0..=1)
|
||||
.default_value("▯"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("mpd_address")
|
||||
.short('a')
|
||||
@@ -136,20 +170,55 @@ pub fn read() -> Config {
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
let lt_format = cli_args.get_one::<String>("localtime").map(|s| s.to_string());
|
||||
let lt_format = cli_args
|
||||
.get_one::<String>("localtime")
|
||||
.map(|s| s.to_string());
|
||||
let ut_format = cli_args.get_one::<String>("utctime").map(|s| s.to_string());
|
||||
let bar_symbol = cli_args
|
||||
.get_one::<String>("bar_symbol")
|
||||
.map(|s| s.to_string());
|
||||
let bar_empty_symbol = cli_args
|
||||
.get_one::<String>("bar_empty_symbol")
|
||||
.map(|s| s.to_string());
|
||||
|
||||
let mut cfg = Config {
|
||||
action: Action::Cpu,
|
||||
mpd_server: cli_args.get_one::<String>("mpd_address").unwrap().to_string(),
|
||||
mpd_server: cli_args
|
||||
.get_one::<String>("mpd_address")
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
lt_format,
|
||||
ut_format,
|
||||
bar_symbol,
|
||||
bar_empty_symbol,
|
||||
low_threshold: *cli_args.get_one::<f32>("low").unwrap(),
|
||||
mid_threshold: *cli_args.get_one::<f32>("mid").unwrap(),
|
||||
color_low: colorize(cli_args.get_one::<String>("COLOR_LOW").unwrap().to_string()),
|
||||
color_mid: colorize(cli_args.get_one::<String>("COLOR_MID").unwrap().to_string()),
|
||||
color_high: colorize(cli_args.get_one::<String>("COLOR_HIGH").unwrap().to_string()),
|
||||
color_track_name: colorize(cli_args.get_one::<String>("COLOR_TRACK_NAME").unwrap().to_string()),
|
||||
color_track_artist: colorize(cli_args.get_one::<String>("COLOR_TRACK_ARTIST").unwrap().to_string()),
|
||||
color_track_time: colorize(cli_args.get_one::<String>("COLOR_TRACK_TIME").unwrap().to_string()),
|
||||
color_high: colorize(
|
||||
cli_args
|
||||
.get_one::<String>("COLOR_HIGH")
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
),
|
||||
color_track_name: colorize(
|
||||
cli_args
|
||||
.get_one::<String>("COLOR_TRACK_NAME")
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
),
|
||||
color_track_artist: colorize(
|
||||
cli_args
|
||||
.get_one::<String>("COLOR_TRACK_ARTIST")
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
),
|
||||
color_track_time: colorize(
|
||||
cli_args
|
||||
.get_one::<String>("COLOR_TRACK_TIME")
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
),
|
||||
color_end: colorize(cli_args.get_one::<String>("COLOR_END").unwrap().to_string()),
|
||||
};
|
||||
|
||||
@@ -174,4 +243,3 @@ pub fn read() -> Config {
|
||||
|
||||
cfg
|
||||
}
|
||||
|
||||
|
||||
@@ -22,4 +22,3 @@ fn main() {
|
||||
config::Action::Mpd => utils::mpd(&conf),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
154
src/utils.rs
154
src/utils.rs
@@ -1,13 +1,14 @@
|
||||
use dbus::arg::RefArg;
|
||||
use crate::config;
|
||||
use chrono::{Local, Utc};
|
||||
use dbus::arg::RefArg;
|
||||
use dbus::blocking::stdintf::org_freedesktop_dbus::Properties;
|
||||
use dbus::{arg, blocking::Connection};
|
||||
use mpd::Client;
|
||||
use size_format::SizeFormatterBinary;
|
||||
use std::process;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use sys_info;
|
||||
use dbus::blocking::stdintf::org_freedesktop_dbus::Properties;
|
||||
use sysinfo::System;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TrackInfo {
|
||||
@@ -20,7 +21,6 @@ pub struct TrackInfo {
|
||||
|
||||
pub fn to_bar(value: i32, max: i32, low: f32, mid: f32, config: &config::Config) {
|
||||
let mut bar = "".to_string();
|
||||
let bar_sym = "▮";
|
||||
let ratio = (value as f32) / (max as f32);
|
||||
bar.push_str(if ratio < low {
|
||||
&config.color_low
|
||||
@@ -29,8 +29,10 @@ pub fn to_bar(value: i32, max: i32, low: f32, mid: f32, config: &config::Config)
|
||||
} else {
|
||||
&config.color_high
|
||||
});
|
||||
let symbol = config.bar_symbol.as_deref().unwrap_or("█");
|
||||
let empty = config.bar_empty_symbol.as_deref().unwrap_or(" ");
|
||||
for i in 0..max {
|
||||
bar.push_str(if i < value { bar_sym } else { " " });
|
||||
bar.push_str(if i < value { symbol } else { empty });
|
||||
}
|
||||
bar.push_str(&config.color_end);
|
||||
bar.push('|');
|
||||
@@ -38,29 +40,79 @@ pub fn to_bar(value: i32, max: i32, low: f32, mid: f32, config: &config::Config)
|
||||
}
|
||||
|
||||
pub fn mem_load_bar(bar_len: i32, config: &config::Config) {
|
||||
let memory = sys_info::mem_info().expect("Failed to get mem_info");
|
||||
let used_ratio = (memory.total - memory.avail) as f32 / memory.total as f32;
|
||||
let mut sys = System::new_all();
|
||||
sys.refresh_memory();
|
||||
|
||||
let total_memory = sys.total_memory();
|
||||
let used_memory = sys.used_memory();
|
||||
|
||||
// On macOS sysinfo.used_memory() includes caches and compressed memory
|
||||
// Try a more conservative estimate
|
||||
// Usually real used memory is about 30-50% of what sysinfo shows
|
||||
#[cfg(target_os = "macos")]
|
||||
let actual_used = used_memory * 40 / 100; // Approximately 40% of sysinfo.used_memory
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
let actual_used = used_memory;
|
||||
|
||||
let actual_free = total_memory - actual_used;
|
||||
|
||||
let used_ratio = actual_used as f32 / total_memory as f32;
|
||||
let len = (used_ratio * bar_len as f32) as i32;
|
||||
to_bar(len, bar_len, 0.7, 0.9, config);
|
||||
|
||||
to_bar(
|
||||
len,
|
||||
bar_len,
|
||||
config.low_threshold,
|
||||
config.mid_threshold,
|
||||
config,
|
||||
);
|
||||
|
||||
// Show: used/free
|
||||
print!(
|
||||
"{}B #[default]",
|
||||
SizeFormatterBinary::new((memory.avail * 1024) as u64)
|
||||
"{}/{} #[default]",
|
||||
SizeFormatterBinary::new(actual_used),
|
||||
SizeFormatterBinary::new(actual_free)
|
||||
);
|
||||
}
|
||||
|
||||
pub fn cpu_load_bar(bar_len: i32, config: &config::Config) {
|
||||
let cpu_count = sys_info::cpu_num().expect("Failed to get cpu_num");
|
||||
let la_one = sys_info::loadavg().expect("Failed to get loadavg").one;
|
||||
let len = (la_one / cpu_count as f64 * bar_len as f64).round() as i32;
|
||||
to_bar(len, bar_len, 0.3, 0.7, config);
|
||||
print!("{:.2} LA1#[default]", la_one);
|
||||
let mut sys = System::new_all();
|
||||
|
||||
// Update CPU information
|
||||
sys.refresh_cpu_all();
|
||||
|
||||
// Wait a bit to get accurate CPU usage data
|
||||
thread::sleep(sysinfo::MINIMUM_CPU_UPDATE_INTERVAL);
|
||||
sys.refresh_cpu_all();
|
||||
|
||||
let cpu_count = sys.cpus().len();
|
||||
|
||||
// Get average CPU usage
|
||||
let cpu_usage: f32 = sys.cpus().iter().map(|cpu| cpu.cpu_usage()).sum::<f32>() / cpu_count as f32;
|
||||
let cpu_load_ratio = cpu_usage / 100.0; // sysinfo returns percentages
|
||||
|
||||
let len = (cpu_load_ratio * bar_len as f32).round() as i32;
|
||||
|
||||
to_bar(
|
||||
len,
|
||||
bar_len,
|
||||
config.low_threshold,
|
||||
config.mid_threshold,
|
||||
config,
|
||||
);
|
||||
|
||||
print!("{:.1}% CPU#[default]", cpu_usage);
|
||||
}
|
||||
|
||||
pub fn get_player() -> Result<Vec<String>, Box<dyn std::error::Error>> {
|
||||
let conn = Connection::new_session()?;
|
||||
let proxy = conn.with_proxy("org.freedesktop.DBus", "/", Duration::from_secs(5));
|
||||
let (names,): (Vec<String>,) = proxy.method_call("org.freedesktop.DBus", "ListNames", ())?;
|
||||
Ok(names.into_iter().filter(|n| n.contains("org.mpris.MediaPlayer2")).collect())
|
||||
Ok(names
|
||||
.into_iter()
|
||||
.filter(|n| n.contains("org.mpris.MediaPlayer2"))
|
||||
.collect())
|
||||
}
|
||||
|
||||
pub fn player_info(players: Vec<String>) -> Result<TrackInfo, Box<dyn std::error::Error>> {
|
||||
@@ -69,12 +121,21 @@ pub fn player_info(players: Vec<String>) -> Result<TrackInfo, Box<dyn std::error
|
||||
let proxy = conn.with_proxy(player, "/org/mpris/MediaPlayer2", Duration::from_secs(5));
|
||||
let metadata: arg::PropMap = proxy.get("org.mpris.MediaPlayer2.Player", "Metadata")?;
|
||||
|
||||
let title = metadata.get("xesam:title").and_then(|v| v.as_str()).unwrap_or("").to_string();
|
||||
let artist = metadata.get("xesam:artist")
|
||||
let title = metadata
|
||||
.get("xesam:title")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("")
|
||||
.to_string();
|
||||
let artist = metadata
|
||||
.get("xesam:artist")
|
||||
.and_then(|v| v.as_iter())
|
||||
.and_then(|mut artists| artists.next().and_then(|a| a.as_str()))
|
||||
.unwrap_or("").to_string();
|
||||
let duration_us = metadata.get("mpris:length").and_then(|v| v.as_i64()).unwrap_or(0);
|
||||
.unwrap_or("")
|
||||
.to_string();
|
||||
let duration_us = metadata
|
||||
.get("mpris:length")
|
||||
.and_then(|v| v.as_i64())
|
||||
.unwrap_or(0);
|
||||
let position_us: i64 = proxy.get("org.mpris.MediaPlayer2.Player", "Position")?;
|
||||
let status_text: String = proxy.get("org.mpris.MediaPlayer2.Player", "PlaybackStatus")?;
|
||||
|
||||
@@ -82,7 +143,8 @@ pub fn player_info(players: Vec<String>) -> Result<TrackInfo, Box<dyn std::error
|
||||
"Playing" => "▶",
|
||||
"Paused" => "⏸",
|
||||
_ => "⏹",
|
||||
}.to_string();
|
||||
}
|
||||
.to_string();
|
||||
|
||||
let track_info = TrackInfo {
|
||||
title,
|
||||
@@ -105,7 +167,11 @@ pub fn format_time(sec: i64) -> String {
|
||||
|
||||
pub fn get_time(utc: bool, format: Option<String>) {
|
||||
let fmt = format.unwrap_or_else(|| "%H:%M".to_string());
|
||||
let now = if utc { Utc::now().format(&fmt) } else { Local::now().format(&fmt) };
|
||||
let now = if utc {
|
||||
Utc::now().format(&fmt)
|
||||
} else {
|
||||
Local::now().format(&fmt)
|
||||
};
|
||||
println!("{}", now);
|
||||
}
|
||||
|
||||
@@ -118,7 +184,11 @@ fn shorten(line: &str, max_len: usize) -> String {
|
||||
}
|
||||
|
||||
fn format_player(track_info: TrackInfo, config: &config::Config) {
|
||||
let separator = if track_info.artist.is_empty() { "" } else { " — " };
|
||||
let separator = if track_info.artist.is_empty() {
|
||||
""
|
||||
} else {
|
||||
" — "
|
||||
};
|
||||
let max_len = if track_info.artist.is_empty() { 60 } else { 30 };
|
||||
|
||||
let artist_line = shorten(&track_info.artist, max_len);
|
||||
@@ -127,19 +197,31 @@ fn format_player(track_info: TrackInfo, config: &config::Config) {
|
||||
if track_info.position == "00:00" || track_info.duration.is_empty() {
|
||||
println!(
|
||||
"#[bold]{}{}{}{}{}{} {}{} {}#[default]",
|
||||
config.color_track_name, title_line, config.color_end,
|
||||
config.color_track_name,
|
||||
title_line,
|
||||
config.color_end,
|
||||
separator,
|
||||
config.color_track_artist, artist_line, config.color_end,
|
||||
config.color_track_time, track_info.status
|
||||
config.color_track_artist,
|
||||
artist_line,
|
||||
config.color_end,
|
||||
config.color_track_time,
|
||||
track_info.status
|
||||
);
|
||||
} else {
|
||||
println!(
|
||||
"#[bold]{}{}{}{}{}{} {}[{}/{}] {}{}{}#[default]",
|
||||
config.color_track_name, title_line, config.color_end,
|
||||
config.color_track_name,
|
||||
title_line,
|
||||
config.color_end,
|
||||
separator,
|
||||
config.color_track_artist, artist_line, config.color_end,
|
||||
config.color_track_time, track_info.position, track_info.duration,
|
||||
track_info.status, config.color_end
|
||||
config.color_track_artist,
|
||||
artist_line,
|
||||
config.color_end,
|
||||
config.color_track_time,
|
||||
track_info.position,
|
||||
track_info.duration,
|
||||
track_info.status,
|
||||
config.color_end
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -160,17 +242,17 @@ pub fn mpd(config: &config::Config) {
|
||||
let song = conn.currentsong().unwrap_or(None);
|
||||
let status = conn.status().unwrap();
|
||||
|
||||
let artist = song.as_ref()
|
||||
let artist = song
|
||||
.as_ref()
|
||||
.and_then(|s| s.tags.iter().find(|(k, _)| k == "Artist").map(|(_, v)| v))
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
|
||||
let title = song.as_ref()
|
||||
let title = song
|
||||
.as_ref()
|
||||
.and_then(|s| s.title.clone().or_else(|| s.name.clone()))
|
||||
.unwrap_or_default();
|
||||
|
||||
|
||||
let (position, duration) = status.time.unwrap_or_default();
|
||||
|
||||
let track_info = TrackInfo {
|
||||
@@ -182,9 +264,9 @@ pub fn mpd(config: &config::Config) {
|
||||
mpd::State::Play => "▶",
|
||||
mpd::State::Pause => "⏸",
|
||||
mpd::State::Stop => "⏹",
|
||||
}.to_string(),
|
||||
}
|
||||
.to_string(),
|
||||
};
|
||||
|
||||
format_player(track_info, config);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user