use atoms::InternedAtoms; use clients::ClientList; use cursors::Cursors; use error::*; use xcb::{ x::{self, ChangeWindowAttributes, PropertyNotifyEvent, Window}, Connection, Event, Extension, }; mod atoms; mod clients; mod cursors; mod error; mod focus; mod keys; 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, cursors: Cursors, 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 conn.check_request(conn.send_request_checked(&ChangeWindowAttributes { window: screen.root(), value_list: &[ x::Cw::BackPixel(screen.white_pixel()), x::Cw::EventMask(x::EventMask::SUBSTRUCTURE_REDIRECT), ], })) .map_err(|_| Error::OtherWMRunning)?; Ok(WM { atoms: InternedAtoms::new_with(conn)?, cursors: Cursors::new_with(conn)?, clients: Default::default(), conn, screen_num, root: screen.root(), }) } fn setup_root(&self) -> Result<()> { // TODO: set wm properties self.conn .check_request(self.conn.send_request_checked(&ChangeWindowAttributes { window: self.root, value_list: &[ x::Cw::EventMask( x::EventMask::SUBSTRUCTURE_REDIRECT | x::EventMask::SUBSTRUCTURE_NOTIFY | x::EventMask::BUTTON_PRESS | x::EventMask::POINTER_MOTION | x::EventMask::ENTER_WINDOW | x::EventMask::LEAVE_WINDOW | x::EventMask::STRUCTURE_NOTIFY | x::EventMask::PROPERTY_CHANGE, ), x::Cw::Cursor(self.cursors.normal), ], }))?; self.grabkeys()?; // TODO: reset focus Ok(()) } fn event_loop(&mut self) -> Result<()> { self.update_geometry()?; self.setup_root()?; loop { match self.conn.wait_for_event()? { // todo: keybinding related stuff Event::X(x::Event::KeyPress(e)) => self.handle_key_press(e)?, Event::X(x::Event::MappingNotify(e)) => self.handle_mapping_notify(e)?, // todo: windows coming and going 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)?, // todo: focus stuff Event::X(x::Event::EnterNotify(e)) => self.handle_enter_notify(e)?, Event::X(x::Event::FocusIn(e)) => self.handle_focus_in(e)?, Event::X(x::Event::MotionNotify(e)) => self.handle_motion_notify(e)?, // todo: other Event::X(x::Event::PropertyNotify(e)) => self.handle_property_notify(e)?, _ => {} }; } } fn handle_property_notify(&self, e: PropertyNotifyEvent) -> Result<()> { todo!() } } 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() } }