155 lines
3.7 KiB
Rust
155 lines
3.7 KiB
Rust
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<Mutex<State>>;
|
|
|
|
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<weather::Root>,
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|