@@ 1,14 1,10 @@
extern crate chrono;
extern crate libc;
extern crate x11;
-extern crate reqwest;
extern crate select;
-extern crate cached;
extern crate signal_hook;
-use signal_hook::{iterator::Signals, SIGINT};
-
-use cached::proc_macro::cached;
+use signal_hook::{iterator::Signals, SIGINT, SIGHUP};
use select::document::Document;
use select::predicate::{Class, Predicate};
@@ 22,19 18,26 @@ use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::{string, thread, time};
+/// System's name
const SYSTEM_NAME: &str = "humaid's system";
+/// DesktopStatus opens the X Display to allow changing the status (root
+/// window) name.
#[derive(Debug, Clone, Copy)]
pub struct DesktopStatus {
disp: *mut x11::xlib::Display,
}
impl DesktopStatus {
+ /// Initialises a new DesktopStatus, gets the pointer to the X Display.
pub fn new() -> Self {
DesktopStatus {
disp: unsafe { x11::xlib::XOpenDisplay(std::ptr::null()) },
}
}
+
+ /// Sets the name of the root window. This will change the status bar in
+ /// dwm.
pub fn set_status(self, stat: &str) {
unsafe {
let s = CString::new(stat).expect("CString::new failed at set_status");
@@ 46,38 49,43 @@ impl DesktopStatus {
x11::xlib::XSync(self.disp, 0);
}
}
- pub fn close(self) {
+
+ /// Closes the X Display. This needs to be called once finished using this
+ /// structure.
+ pub fn close(self) { // TODO destructor
unsafe {
x11::xlib::XCloseDisplay(self.disp);
}
}
}
-type StatusItem = fn() -> String;
+/// This represents a function which returns a status item.
+type StatusItem = fn() -> Result<String, String>;
-fn load_item() -> String {
+/// A StatusItem which displays the system load.
+fn load_item() -> Result<String, String> {
match get_load() {
- Ok(load) => return format!("L:{}|", load.as_str()),
- Err(why) => println!("Cannot get load: {}", why),
+ Ok(load) => return Ok(format!("L:{}|", load.as_str())),
+ Err(why) => Err(format!("Cannot get load: {}", why)),
}
- "".to_string()
}
-fn battery_item() -> String {
+/// A StatusItem which displays the battery percentage.
+fn battery_item() -> Result<String, String> {
match get_battery_with_status() {
- Ok(perc) => return format!("B:{}|", perc.as_str()),
- Err(why) => println!("Cannot get battery percentage: {}", why),
+ Ok(perc) => return Ok(format!("B:{}|", perc.as_str())),
+ Err(why) => Err(format!("Cannot get battery percentage: {}", why)),
}
- "".to_string()
}
-fn time_item() -> String {
+/// A StatusItem which displays the time and date.
+fn time_item() -> Result<String, String> {
let mut res = String::new();
let local: DateTime<Local> = Local::now();
res.push_str("UK:");
res.push_str(
local
- //.with_timezone(&chrono::FixedOffset::east(3600))
+ .with_timezone(&chrono::FixedOffset::east(3600))
.format("%I:%M")
.to_string()
.as_str(),
@@ 88,38 96,14 @@ fn time_item() -> String {
.with_timezone(&chrono::FixedOffset::east(4 * 3600))
.format("%I:%M %p %d-%m-%Y").to_string().as_str());
res.push('|');
- res
-}
-
-#[cached(time=21600)]
-fn get_covid_stats() -> String {
- let resp = reqwest::blocking::get("http://covid19.ncema.gov.ae/").unwrap();
- if !resp.status().is_success() {
- println!("covid fail: {}", resp.status());
- return "fail".to_string();
- }
- let doc = Document::from_read(resp).unwrap();
-
- let active = doc
- .select(Class("active").descendant(Class("counter"))).next().unwrap()
- .text();
-
- let recovered = doc
- .select(Class("recovered").descendant(Class("new-cases").descendant(Class("recovered")))).next().unwrap()
- .text();
-
- let rec = recovered.split(' ').collect::<Vec<&str>>()[2].to_string();
-
- return format!("A:{} R:{}|", active, rec);
+ Ok(res)
}
fn main() {
- let signals = Signals::new(&[SIGINT]).unwrap();
-
+ let signals = Signals::new(&[SIGINT, SIGHUP]).unwrap();
// Register the status items
let mut stat_items: Vec<StatusItem> = Vec::new();
- stat_items.push(get_covid_stats);
stat_items.push(load_item);
stat_items.push(battery_item);
stat_items.push(time_item);
@@ 127,11 111,11 @@ fn main() {
let status: DesktopStatus = DesktopStatus::new();
let cont = Arc::new(AtomicBool::new(true));
-
{
let c = Arc::clone(&cont);
thread::spawn(move || {
for _ in signals.forever() {
+ println!("Closing...");
c.store(false, Ordering::SeqCst);
}
});
@@ 166,7 150,10 @@ fn main() {
let mut stat = String::new();
for i in &stat_items {
- stat.push_str(i().as_str());
+ match i() {
+ Ok(s) => stat.push_str(s.as_str()),
+ Err(s) => println!("Error: {}", s),
+ }
}
stat.push_str(SYSTEM_NAME);
@@ 176,6 163,7 @@ fn main() {
status.close();
}
+/// Reads a file from the system.
fn read_file(file: &str) -> std::io::Result<string::String> {
let mut file = File::open(file)?;
let mut contents = String::new();
@@ 183,6 171,7 @@ fn read_file(file: &str) -> std::io::Result<string::String> {
Ok(contents.replace("\n", ""))
}
+/// Gets the system load, formatted in a way similar to uptime.
fn get_load() -> Result<string::String, &'static str> {
let mut avgs = Vec::with_capacity(3_usize);
match unsafe { getloadavg(avgs.as_mut_ptr(), 3 as c_int) } {
@@ 197,6 186,7 @@ fn get_load() -> Result<string::String, &'static str> {
}
}
+/// Gets the battery percentage.
fn get_battery_perc() -> i32 {
let present = read_file("/sys/class/power_supply/BAT0/present").unwrap();
assert_eq!(present, "1", "Battery not present");
@@ 209,6 199,7 @@ fn get_battery_perc() -> i32 {
return ((now as f64 / full as f64) * 100_f64) as i32
}
+/// Gets the charging/discharing status.
fn get_battery_with_status() -> std::io::Result<string::String> {
let status = read_file("/sys/class/power_supply/BAT0/status")?;
let stat = match status.as_ref() {