From f09145eccd524273ecca121d6225c2ba707c7e0e Mon Sep 17 00:00:00 2001 From: Bill Thiede Date: Sat, 21 Sep 2019 20:49:27 -0700 Subject: [PATCH] Move datetime to widgets subdir and import i3monkit network. --- src/{widgets.rs => widgets/datetime.rs} | 0 src/widgets/mod.rs | 2 + src/widgets/network.rs | 138 ++++++++++++++++++++++++ 3 files changed, 140 insertions(+) rename src/{widgets.rs => widgets/datetime.rs} (100%) create mode 100644 src/widgets/mod.rs create mode 100644 src/widgets/network.rs diff --git a/src/widgets.rs b/src/widgets/datetime.rs similarity index 100% rename from src/widgets.rs rename to src/widgets/datetime.rs diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs new file mode 100644 index 0000000..50526e7 --- /dev/null +++ b/src/widgets/mod.rs @@ -0,0 +1,2 @@ +pub mod datetime; +pub mod network; diff --git a/src/widgets/network.rs b/src/widgets/network.rs new file mode 100644 index 0000000..96b74e6 --- /dev/null +++ b/src/widgets/network.rs @@ -0,0 +1,138 @@ +use i3monkit::Block; +use i3monkit::{Widget, WidgetUpdate}; + +use std::path::PathBuf; + +use std::fs::File; +use std::io::{BufRead, BufReader, Error, ErrorKind, Result}; +use std::time::SystemTime; + +const NETWORK_PATH_PREFIX: &'static str = "/sys/class/net"; +const NETWORK_STAT_SUFFIX: &'static str = "statistics/dummy"; + +struct TransferStat { + rx: u64, + tx: u64, + ts: SystemTime, +} + +impl TransferStat { + fn read_stat(interface: &str) -> Result { + let mut path = PathBuf::new(); + path.push(format!( + "{}/{}/{}", + NETWORK_PATH_PREFIX, interface, NETWORK_STAT_SUFFIX + )); + + let mut read_stat_file = move |what: &str| { + path.set_file_name(what); + + let reader = BufReader::new(File::open(path.as_path())?); + + if let Some(line) = reader.lines().next() { + return line; + } + + return Err(Error::new(ErrorKind::Other, "Empty file")); + }; + + let rx = u64::from_str_radix(&(read_stat_file("rx_bytes")?), 10).unwrap(); + let tx = u64::from_str_radix(&(read_stat_file("tx_bytes")?), 10).unwrap(); + let ts = SystemTime::now(); + + return Ok(Self { rx, tx, ts }); + } + + fn duration(&self, earlier: &Self) -> f64 { + let duration = self.ts.duration_since(earlier.ts).unwrap(); + let secs = duration.as_secs() as f64 + duration.subsec_nanos() as f64 / 1_000_000_000.0; + return secs; + } + + fn rx_rate(&self, earlier: &Self) -> f64 { + let duration = self.duration(earlier); + if duration < 1e-5 { + return std::f64::NAN; + } + + return (self.rx - earlier.rx) as f64 / duration; + } + + fn tx_rate(&self, earlier: &Self) -> f64 { + let duration = self.duration(earlier); + if duration < 1e-5 { + return std::f64::NAN; + } + + return (self.tx - earlier.tx) as f64 / duration; + } +} + +/// A widget that shows the network speed realtimely +pub struct NetworkSpeedWidget { + interface: String, + last_stat: TransferStat, +} + +impl NetworkSpeedWidget { + /// Create the widget, for given interface. + /// + /// **interface** The interface to monitor + pub fn new(interface: &str) -> Self { + let last_stat = TransferStat::read_stat(interface).unwrap(); + let interface = interface.to_string(); + Self { + last_stat, + interface, + } + } + + fn format_rate(rate: f64) -> String { + if rate.is_nan() { + return "N/A".to_string(); + } + + const UNIT_NAME: [&'static str; 6] = [" B/s", "KB/s", "MB/s", "GB/s", "TB/s", "PB/s"]; + + let mut best_unit = UNIT_NAME[0]; + let mut best_multiplier = 1.0; + + for unit in UNIT_NAME[1..].iter() { + if best_multiplier > rate / 1024.0 { + break; + } + best_unit = unit; + best_multiplier *= 1024.0 + } + + let ret = format!("{:6.1}{}", rate / best_multiplier, best_unit); + + return ret; + } + + fn get_human_readable_stat(&mut self) -> Result<(String, String)> { + let cur_stat = TransferStat::read_stat(&self.interface)?; + + let rx_rate = cur_stat.rx_rate(&self.last_stat); + let tx_rate = cur_stat.tx_rate(&self.last_stat); + + self.last_stat = cur_stat; + + return Ok((Self::format_rate(rx_rate), Self::format_rate(tx_rate))); + } +} + +impl Widget for NetworkSpeedWidget { + fn update(&mut self) -> Option { + if let Ok((rx, tx)) = self.get_human_readable_stat() { + let mut data = Block::new(); + data.use_pango(); + data.append_full_text(&format!("Rx:{} Tx:{}", rx, tx)); + return Some(WidgetUpdate { + refresh_interval: std::time::Duration::new(1, 0), + data: Some(data), + }); + } + None + } +}