use atoms::InternedAtoms; use clients::ClientList; use thiserror::Error; use xcb::{ x::{self, ChangeWindowAttributes, Screen, Window}, Connection, Extension, }; mod atoms; mod clients; type Result = std::result::Result; #[derive(Debug, Error)] pub enum Error { #[error("xcb returned screen that doesn't exist")] NoSuchScreen, #[error("other wm is running")] OtherWMRunning, #[error("connection error: {0}")] ConnectionError(#[from] xcb::ConnError), #[error("generic xcb error: {0}")] XCBError(#[from] xcb::Error), } fn main() -> Result<()> { // todo: cli stuff let display_name = ":1"; cleanup_process_children(); let (conn, screen_num) = Connection::connect_with_extensions(Some(display_name), &[], &[Extension::Xinerama])?; let mut wm = WM::new(&conn, screen_num)?; wm.event_loop()?; Ok(()) } struct WM<'a> { conn: &'a Connection, screen_num: i32, root: Window, clients: ClientList, atoms: InternedAtoms, } impl WM<'_> { fn new<'a>(conn: &'a Connection, screen_num: i32) -> Result> { // Fetch root window let setup = conn.get_setup(); let screen = setup .roots() .nth(screen_num as usize) .ok_or(Error::NoSuchScreen)?; // Check no other WM is running let cookie = conn.send_request_checked(&ChangeWindowAttributes { window: screen.root(), value_list: &[ x::Cw::BackPixel(screen.white_pixel()), x::Cw::EventMask(x::EventMask::SUBSTRUCTURE_REDIRECT), ], }); conn.check_request(cookie) .map_err(|_| Error::OtherWMRunning)?; Ok(WM { atoms: InternedAtoms::new_with(conn)?, clients: Default::default(), conn, screen_num, root: screen.root(), }) } fn event_loop(&mut self) -> Result<()> { self.update_geometry()?; Ok(()) } } fn cleanup_process_children() { // todo: dont transform children into zombies when they terminate // todo: cleanup zombies } impl<'a> std::fmt::Debug for WM<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("WM") .field("screen_num", &self.screen_num) .field("root", &self.root) .field("clients", &self.clients) .field("atoms", &self.atoms) .finish() } }