mod config; mod front; mod weather; mod xsetroot; use anyhow::{Result}; use chrono::prelude::*; use log::*; use std::{ fmt, io::{prelude::*, BufReader}, net::Shutdown, os::unix::net::{UnixListener, UnixStream}, sync::{Arc, Mutex}, thread, time, }; use xsetroot::XSetRoot; // Name your user agent after your app? pub static APP_USER_AGENT: &str = concat!( env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"), " +https://tulpa.dev/cadey/cabytcini", ); pub(crate) type MTState = Arc>; fn handle_client(stream: UnixStream, st: MTState) { let mut rdr = BufReader::new(&stream); let mut msg = String::new(); let _ = rdr.read_line(&mut msg); let _ = write!(&stream, "OK"); let mut data = st.lock().unwrap(); data.msg = msg.trim().to_string(); info!("new message: {}", data.msg); stream.shutdown(Shutdown::Both).expect("socket to close"); } const UPDATE_FREQUENCY: u64 = 1000; fn update_every_so_often(st: MTState) { loop { let so_often = time::Duration::from_millis(UPDATE_FREQUENCY); thread::sleep(so_often); { let data = st.lock().unwrap(); data.show(); } } } fn main() -> Result<()> { let st: State = State::init(); st.show(); let mtst = Arc::new(Mutex::new(st)); env_logger::init(); let cfg = config::load()?; // start update thread { let mtst = mtst.clone(); thread::spawn(move || update_every_so_often(mtst)); } // start front thread { let mtst = mtst.clone(); let cfg = cfg.clone(); thread::spawn(move || front::update(mtst, cfg)); } // start weather thread { let mtst = mtst.clone(); let cfg = cfg.clone(); thread::spawn(move || weather::update(mtst, cfg)); } let _ = std::fs::remove_file("/home/cadey/tmp/cabytcini.sock")?; let listener = UnixListener::bind("/home/cadey/tmp/cabytcini.sock")?; for stream in listener.incoming() { match stream { Ok(stream) => { let mtst = mtst.clone(); thread::spawn(move || handle_client(stream, mtst)); } Err(err) => { return Err(err.into()); } } } Ok(()) } pub(crate) struct State { msg: String, front: String, weather: Option, } impl fmt::Display for State { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.msg) } } impl State { fn init() -> State { State { msg: "".to_string(), front: "".to_string(), weather: None, } } pub(crate) fn show(&self) { let now = Local::now().format("%H:%M M%m %-d %a"); let xsr = XSetRoot::init().expect("xsetroot to initialize"); let mut msg = String::new(); if self.msg != "" { msg.push_str(format!("{} | ", self.msg).as_str()); } if self.front != "" { msg.push_str(format!("{} | ", self.front).as_str()); } match &self.weather { Some(datni) => msg.push_str( format!( "{} {} / {} {} | ", datni.currently.temperature, datni.currently.summary, datni.daily.data[0].temperature_high, datni.daily.data[0].temperature_low ) .as_str(), ), None => {} }; msg.push_str(format!("{}", now).as_str()); if let Err(why) = xsr.render(msg) { error!("error setting root window title: {:?}", why); } } }