//! A lightweight X11 window manager, inspired by dwm. #![deny(clippy::all, clippy::pedantic, clippy::nursery)] use clients::ClientState; use conn_info::Connection; pub use error::*; use xcb::{ x::{self, PropertyNotifyEvent}, Connection as RawConnection, Event, Extension, }; mod clients; mod config; mod conn_info; mod error; mod focus; mod keys; fn main() -> Result<()> { cleanup_process_children(); let (conn, screen_num) = RawConnection::connect_with_extensions(None, &[], &[Extension::Xinerama])?; #[allow(clippy::cast_sign_loss)] let mut wm = WM::new(&conn, screen_num as usize)?; wm.event_loop()?; Ok(()) } struct WM<'a> { conn: Connection<'a>, clients: ClientState, } impl<'a> WM<'a> { pub fn new(conn: &'a RawConnection, screen_num: usize) -> Result { Ok(Self { conn: Connection::new(conn, screen_num)?, clients: ClientState::default(), }) } pub fn event_loop(&mut self) -> Result<()> { loop { match self.conn.wait_for_event()? { // See keys.rs Event::X(x::Event::KeyPress(e)) => self.handle_key_press(&e), Event::X(x::Event::MappingNotify(e)) => self.handle_mapping_notify(&e)?, // See clients/mod.rs Event::X(x::Event::ConfigureRequest(e)) => self.handle_configure_request(&e)?, Event::X(x::Event::ConfigureNotify(e)) => self.handle_configure_notify(&e)?, Event::X(x::Event::DestroyNotify(e)) => self.handle_destroy_notify(&e)?, Event::X(x::Event::MapRequest(e)) => self.handle_map_request(&e)?, Event::X(x::Event::UnmapNotify(e)) => self.handle_unmap_notify(&e)?, // // See focus.rs Event::X(x::Event::EnterNotify(e)) => self.handle_enter_notify(&e)?, Event::X(x::Event::FocusIn(e)) => self.handle_focus_in(&e)?, // // See below Event::X(x::Event::PropertyNotify(e)) => self.handle_property_notify(&e)?, _ => {} }; } } /// Handle a property notify event, by doing *todo* fn handle_property_notify(&mut self, e: &PropertyNotifyEvent) -> Result<()> { match e.atom() { x::ATOM_WM_HINTS => { let focused = self.clients.is_focused(e.window()); if let Some(c) = self.clients.find_client_mut(e.window()) { c.sync_properties(&self.conn, focused); self.conn.flush()?; } Ok(()) } _ => Ok(()), } } } /// Cleanup this process' children and set some flags. /// This is necessary when used with `startx` fn cleanup_process_children() { // TODO: dont transform children into zombies when they terminate // TODO: cleanup zombies todo!() }