forked from wathiede/i3xs
Read all CPUs in one widget.
This commit is contained in:
parent
a1cdec0a24
commit
10c7dcdeab
@ -2,8 +2,6 @@ use chrono::NaiveTime;
|
|||||||
|
|
||||||
use i3monkit::{ColorRGB, Header, I3Protocol, WidgetCollection};
|
use i3monkit::{ColorRGB, Header, I3Protocol, WidgetCollection};
|
||||||
|
|
||||||
use num_cpus;
|
|
||||||
|
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
use i3xs::widgets::cpu::CpuWidget;
|
use i3xs::widgets::cpu::CpuWidget;
|
||||||
@ -22,10 +20,7 @@ fn main() {
|
|||||||
|
|
||||||
let opts = Opt::from_args();
|
let opts = Opt::from_args();
|
||||||
|
|
||||||
// Display all the cpu usage for each core
|
bar.push(CpuWidget::new());
|
||||||
for i in 0..num_cpus::get() as u32 {
|
|
||||||
bar.push(CpuWidget::new(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Realtime upload/download rate for a interface
|
// Realtime upload/download rate for a interface
|
||||||
bar.push(NetworkSpeedWidget::new(&opts.nic, 6));
|
bar.push(NetworkSpeedWidget::new(&opts.nic, 6));
|
||||||
|
|||||||
@ -2,8 +2,6 @@ use chrono::NaiveTime;
|
|||||||
|
|
||||||
use i3monkit::{ColorRGB, Header, I3Protocol, WidgetCollection};
|
use i3monkit::{ColorRGB, Header, I3Protocol, WidgetCollection};
|
||||||
|
|
||||||
use num_cpus;
|
|
||||||
|
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
use i3xs::widgets::cpu::CpuWidget;
|
use i3xs::widgets::cpu::CpuWidget;
|
||||||
@ -22,10 +20,7 @@ fn main() {
|
|||||||
|
|
||||||
let opts = Opt::from_args();
|
let opts = Opt::from_args();
|
||||||
|
|
||||||
// Display all the cpu usage for each core
|
bar.push(CpuWidget::new());
|
||||||
for i in 0..num_cpus::get() as u32 {
|
|
||||||
bar.push(CpuWidget::new(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Realtime upload/download rate for a interface
|
// Realtime upload/download rate for a interface
|
||||||
bar.push(NetworkSpeedWidget::new(&opts.nic, 6));
|
bar.push(NetworkSpeedWidget::new(&opts.nic, 6));
|
||||||
|
|||||||
@ -6,24 +6,21 @@ use i3monkit::{Block, ColorRGB, Widget, WidgetUpdate};
|
|||||||
|
|
||||||
use crate::spark;
|
use crate::spark;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Stat {
|
||||||
|
usage: u64,
|
||||||
|
total: u64,
|
||||||
|
}
|
||||||
|
|
||||||
/// The CPU usage widget
|
/// The CPU usage widget
|
||||||
///
|
///
|
||||||
/// This widget draws a CPU usage pertentage bar on your i3 status bar.
|
/// This widget draws a CPU usage pertentage bar on your i3 status bar.
|
||||||
pub struct CpuWidget {
|
pub struct CpuWidget {
|
||||||
id: u32,
|
last_stats: Vec<Stat>,
|
||||||
user: u64,
|
history: Vec<VecDeque<f32>>,
|
||||||
nice: u64,
|
|
||||||
system: u64,
|
|
||||||
idel: u64,
|
|
||||||
width: u8,
|
|
||||||
user_color: String,
|
|
||||||
nice_color: String,
|
|
||||||
system_color: String,
|
|
||||||
history: VecDeque<f32>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn color_lerp(c1: ColorRGB, c2: ColorRGB, alpha: f32) -> ColorRGB {
|
fn color_lerp(c1: ColorRGB, c2: ColorRGB, alpha: f32) -> ColorRGB {
|
||||||
// TODO(wathiede): actual lerp.
|
|
||||||
ColorRGB(
|
ColorRGB(
|
||||||
(c1.0 as f32 * (1. - alpha) + c2.0 as f32 * alpha) as u8,
|
(c1.0 as f32 * (1. - alpha) + c2.0 as f32 * alpha) as u8,
|
||||||
(c1.1 as f32 * (1. - alpha) + c2.1 as f32 * alpha) as u8,
|
(c1.1 as f32 * (1. - alpha) + c2.1 as f32 * alpha) as u8,
|
||||||
@ -36,72 +33,72 @@ fn color_to_string(c: ColorRGB) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl CpuWidget {
|
impl CpuWidget {
|
||||||
fn read_status(id: u32) -> Result<(u64, u64, u64, u64)> {
|
fn read_status() -> Result<Vec<Stat>> {
|
||||||
let name = format!("cpu{}", id);
|
|
||||||
let file = File::open("/proc/stat")?;
|
let file = File::open("/proc/stat")?;
|
||||||
let reader = BufReader::new(file);
|
let reader = BufReader::new(file);
|
||||||
|
let mut stats: Vec<_> = Vec::new();
|
||||||
for line in reader.lines() {
|
for line in reader.lines() {
|
||||||
let line = line?;
|
let line = line?;
|
||||||
let tokens: Vec<_> = line.trim().split(|c| c == ' ' || c == '\t').collect();
|
let tokens: Vec<_> = line.trim().split(|c| c == ' ' || c == '\t').collect();
|
||||||
if tokens[0] == name {
|
// The sum total CPU entry has a space instead of a number, so we skip the line when
|
||||||
|
// there's an extra empty column.
|
||||||
|
if tokens[1] == "" {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if tokens[0].starts_with("cpu") {
|
||||||
let parsed = tokens[1..5]
|
let parsed = tokens[1..5]
|
||||||
.iter()
|
.iter()
|
||||||
.map(|x| u64::from_str_radix(x, 10).unwrap())
|
.map(|x| u64::from_str_radix(x, 10).unwrap())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
return Ok((parsed[0], parsed[1], parsed[2], parsed[3]));
|
let usage = parsed[0] + parsed[1] + parsed[2];
|
||||||
|
let total = usage + parsed[3];
|
||||||
|
stats.push(Stat { usage, total })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(stats)
|
||||||
|
}
|
||||||
|
|
||||||
Err(std::io::Error::new(
|
pub fn new() -> Self {
|
||||||
std::io::ErrorKind::Other,
|
let last_stats = Self::read_status().unwrap();
|
||||||
"No such CPU core",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new CPU usage monitor widget for specified core
|
|
||||||
///
|
|
||||||
/// **id** The core id
|
|
||||||
pub fn new(id: u32) -> Self {
|
|
||||||
let (user, nice, system, idel) = Self::read_status(id).unwrap();
|
|
||||||
let num_samples = 6;
|
let num_samples = 6;
|
||||||
|
let history = (0..last_stats.len())
|
||||||
|
.map(|_| (0..num_samples).map(|_| 0.).collect::<VecDeque<f32>>())
|
||||||
|
.collect::<Vec<VecDeque<f32>>>();
|
||||||
let ret = Self {
|
let ret = Self {
|
||||||
id,
|
last_stats,
|
||||||
user,
|
history,
|
||||||
nice,
|
|
||||||
system,
|
|
||||||
idel,
|
|
||||||
width: 20,
|
|
||||||
user_color: "#00ff00".to_string(),
|
|
||||||
nice_color: "#0000ff".to_string(),
|
|
||||||
system_color: "#ff0000".to_string(),
|
|
||||||
history: (0..num_samples).map(|_| 0.).collect(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_history(&mut self) -> Option<String> {
|
fn draw_history(&mut self) -> Option<String> {
|
||||||
let (user, nice, system, idel) = Self::read_status(self.id).ok()?;
|
let stats = Self::read_status().ok()?;
|
||||||
let used_diff = (user + nice + system) - (self.user + self.nice + self.system) + 1;
|
let percentages: Vec<_> = stats
|
||||||
let total_diff =
|
.iter()
|
||||||
(user + nice + system + idel) - (self.user + self.nice + self.system + self.idel) + 1;
|
.zip(self.last_stats.iter())
|
||||||
|
.map(|(cur, old)| {
|
||||||
self.nice = nice;
|
if cur.total == old.total {
|
||||||
self.user = user;
|
return 0.;
|
||||||
self.idel = idel;
|
}
|
||||||
self.system = system;
|
(cur.usage - old.usage) as f32 / (cur.total - old.total) as f32
|
||||||
|
})
|
||||||
let percent = used_diff as f32 / total_diff as f32;
|
.collect();
|
||||||
self.history.push_back(percent);
|
for (i, p) in percentages.iter().enumerate() {
|
||||||
self.history.pop_front();
|
self.history[i].push_back(*p);
|
||||||
if self.history.len() > 0 {
|
self.history[i].pop_front();
|
||||||
|
}
|
||||||
|
self.last_stats = stats;
|
||||||
let g = spark::Graph {
|
let g = spark::Graph {
|
||||||
min: Some(0.),
|
min: Some(0.),
|
||||||
max: Some(1.),
|
max: Some(1.),
|
||||||
};
|
};
|
||||||
let sg = g.render(&self.history.iter().cloned().collect::<Vec<f32>>());
|
let graphs: Vec<String> = self
|
||||||
let colored_sg = self
|
|
||||||
.history
|
.history
|
||||||
|
.iter()
|
||||||
|
.map(|history| {
|
||||||
|
let sg = g.render(&history.iter().map(|v| *v).collect::<Vec<f32>>());
|
||||||
|
history
|
||||||
.iter()
|
.iter()
|
||||||
.zip(sg.chars())
|
.zip(sg.chars())
|
||||||
.map(|(v, g)| {
|
.map(|(v, g)| {
|
||||||
@ -112,54 +109,15 @@ impl CpuWidget {
|
|||||||
g
|
g
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
Some(colored_sg)
|
Some(format!("CPU|{}", graphs.join("|")))
|
||||||
} else {
|
|
||||||
Some("N/A".to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_bar(&mut self) -> Option<String> {
|
|
||||||
let mut ret = Vec::new();
|
|
||||||
for _ in 0..self.width {
|
|
||||||
ret.push("<span foreground=\"grey\">|</span>".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
let (user, nice, system, idel) = Self::read_status(self.id).ok()?;
|
|
||||||
|
|
||||||
let total_diff =
|
|
||||||
(user + nice + system + idel) - (self.user + self.nice + self.system + self.idel);
|
|
||||||
|
|
||||||
if total_diff > 0 {
|
|
||||||
let diffs = [system - self.system, nice - self.nice, user - self.user];
|
|
||||||
let color = [&self.system_color, &self.nice_color, &self.user_color];
|
|
||||||
|
|
||||||
let mut idx = 0;
|
|
||||||
for (d, c) in diffs.iter().zip(color.iter()) {
|
|
||||||
for _ in 0..(d * (self.width as u64) / total_diff) {
|
|
||||||
ret[idx] = format!("<span foreground=\"{}\">|</span>", c);
|
|
||||||
idx += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut result = String::new();
|
|
||||||
for s in ret {
|
|
||||||
result.push_str(&s);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.nice = nice;
|
|
||||||
self.user = user;
|
|
||||||
self.idel = idel;
|
|
||||||
self.system = system;
|
|
||||||
|
|
||||||
return Some(result);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Widget for CpuWidget {
|
impl Widget for CpuWidget {
|
||||||
fn update(&mut self) -> Option<WidgetUpdate> {
|
fn update(&mut self) -> Option<WidgetUpdate> {
|
||||||
if self.history.len() > 0 {
|
|
||||||
if let Some(history) = self.draw_history() {
|
if let Some(history) = self.draw_history() {
|
||||||
let mut data = Block::new();
|
let mut data = Block::new();
|
||||||
|
|
||||||
@ -171,19 +129,6 @@ impl Widget for CpuWidget {
|
|||||||
data: Some(data),
|
data: Some(data),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if let Some(bar) = self.draw_bar() {
|
|
||||||
let mut data = Block::new();
|
|
||||||
|
|
||||||
data.use_pango();
|
|
||||||
data.append_full_text(&format!("{}[{}]", self.id + 1, bar));
|
|
||||||
|
|
||||||
return Some(WidgetUpdate {
|
|
||||||
refresh_interval: std::time::Duration::new(1, 0),
|
|
||||||
data: Some(data),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user