use std::vec::Vec; use std::process::{ Command, Child, Stdio }; use std::io::{ BufReader, BufRead, Write }; use std::cell::RefCell; use std::path::Path; use std::io::Read; use ron; use crate::window_manager::{ DrawInstructions, WindowLike, WindowLikeType }; use crate::messages::{ WindowMessage, WindowMessageResponse }; use crate::framebuffer::Dimensions; use crate::themes::ThemeInfo; pub struct ProxyWindowLike { process: RefCell, } //try to handle panics of child processes so the entire wm doesn't crash //we can "guarantee" that the ron::to_string(...).unwrap() calls will never panic impl WindowLike for ProxyWindowLike { fn handle_message(&mut self, message: WindowMessage) -> WindowMessageResponse { if let Some(stdin) = self.process.borrow_mut().stdin.as_mut() { let _ = stdin.write_all(("handle_message ".to_string() + &ron::to_string(&message).unwrap() + "\n").as_bytes()); } let output = self.read_line(); ron::from_str(&output).unwrap_or(WindowMessageResponse::JustRedraw) } fn draw(&self, theme_info: &ThemeInfo) -> Vec { if let Some(stdin) = self.process.borrow_mut().stdin.as_mut() { let _ = stdin.write_all(("draw ".to_string() + &ron::to_string(&theme_info).unwrap() + "\n").as_bytes()); } let output = self.read_line(); ron::from_str(&output).unwrap_or(Vec::new()) } //properties fn title(&self) -> String { if let Some(stdin) = self.process.borrow_mut().stdin.as_mut() { let _ = stdin.write_all("title\n".as_bytes()); } self.read_line().chars().filter(|c| *c != '\n').collect() } fn resizable(&self) -> bool { if let Some(stdin) = self.process.borrow_mut().stdin.as_mut() { let _ = stdin.write_all("resizable\n".to_string().as_bytes()); } let output = self.read_line(); ron::from_str(&output).unwrap_or(false) } fn subtype(&self) -> WindowLikeType { if let Some(stdin) = self.process.borrow_mut().stdin.as_mut() { let _ = stdin.write_all("subtype\n".to_string().as_bytes()); } let output = self.read_line(); ron::from_str(&output).unwrap_or(WindowLikeType::Window) } fn ideal_dimensions(&self, dimensions: Dimensions) -> Dimensions { if let Some(stdin) = self.process.borrow_mut().stdin.as_mut() { let _ = stdin.write_all(("ideal_dimensions ".to_string() + &ron::to_string(&dimensions).unwrap() + "\n").as_bytes()); } let output = self.read_line(); ron::from_str(&output).unwrap_or([420, 420]) } } //kill process when this window like dropped impl Drop for ProxyWindowLike { fn drop(&mut self) { let _ = self.process.borrow_mut().kill(); } } impl ProxyWindowLike { pub fn new(command: &mut Command) -> Self { ProxyWindowLike { process: RefCell::new(command.stdout(Stdio::piped()).stdin(Stdio::piped()).stderr(Stdio::null()).spawn().unwrap()), } } pub fn new_rust(file: &str) -> Self { let loc = format!("./target/release/{}", file); if Path::new(&loc).exists() { ProxyWindowLike::new(Command::new(loc).stdout(Stdio::piped()).stdin(Stdio::piped()).stderr(Stdio::null())) } else { ProxyWindowLike::new(Command::new("cargo").arg("run").arg("--quiet").arg("--release").arg("--bin").arg(file).stdout(Stdio::piped()).stdin(Stdio::piped()).stderr(Stdio::null())) } } //return empty string if error, do not propogate Err becuase that's messy //or maybe return "panicked"? fn read_line(&self) -> String { let mut buffer = self.process.borrow_mut(); if let Some(buffer) = buffer.stdout.as_mut() { let mut output = String::new(); let mut reader = BufReader::new(buffer); if let Ok(_) = reader.read_line(&mut output) { output } else { String::new() } } else { String::new() } } }