summaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs44
1 files changed, 35 insertions, 9 deletions
diff --git a/src/main.rs b/src/main.rs
index 54d893c..64e9203 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,4 +1,20 @@
//! A lightweight X11 window manager, inspired by dwm.
+//!
+//! # Structure
+//!
+//! The main thing a WM has to do is respond to events: this is done in the [`WM::event_loop`] function, which dispatches to the `handle_*` methods of that struct.
+//!
+//! [`conn_info`] wraps XCB's [`xcb::Connection`] type and caches common resources such as atoms, colours, cursors, and keyboard layout info.
+//!
+//! `focus.rs`, [`keys`], and [`clients`] all add some event handlers to [`WM`], but most of the important code is in [`clients`].
+//!
+//! [`config`] holds all of the variables that a user might want to change.
+//!
+//! # XCB
+//!
+//! Unlike dwm, blow uses XCB rather than Xlib. This means requests are asynchronous by default.
+//! In most places, we avoid checking for errors unless we need to see the response to a request.
+//! Errors will be caught and logged in the event loop instead. See [`xcb`] documentation for more details.
#![deny(clippy::all, clippy::pedantic, clippy::nursery)]
use clients::ClientState;
@@ -9,12 +25,14 @@ use xcb::{
Connection as RawConnection, Event, Extension,
};
-mod clients;
-mod config;
-mod conn_info;
+pub mod clients;
+pub mod config;
+pub mod conn_info;
+#[doc(hidden)]
mod error;
+#[doc(hidden)]
mod focus;
-mod keys;
+pub mod keys;
fn main() -> Result<()> {
cleanup_process_children();
@@ -29,12 +47,14 @@ fn main() -> Result<()> {
Ok(())
}
-struct WM<'a> {
+/// All of the state used by the window manager
+pub struct WM<'a> {
conn: Connection<'a>,
clients: ClientState,
}
impl<'a> WM<'a> {
+ /// Prepare to start the window manager, using the given connection and scren number.
pub fn new(conn: &'a RawConnection, screen_num: usize) -> Result<Self> {
let mut this = Self {
conn: Connection::new(conn, screen_num)?,
@@ -45,6 +65,8 @@ impl<'a> WM<'a> {
Ok(this)
}
+ /// Run the main event loop until we encounter a non-recoverable error (usually connection).
+ /// This will only ever return an error.
pub fn event_loop(&mut self) -> Result<()> {
loop {
match self.conn.wait_for_event() {
@@ -68,15 +90,19 @@ impl<'a> WM<'a> {
Event::X(x::Event::PropertyNotify(e)) => self.handle_property_notify(&e),
_ => {}
},
+ Err(Error::Xcb(xcb::Error::Protocol(e))) => {
+ eprintln!("protocol error in event loop: {e:#?}\ncontinuing anyway");
+ }
Err(e) => {
- eprintln!("error in event loop: {e:#?}\ncontinuing anyway");
+ eprintln!("unrecoverable error: {e:#?}\nexiting event loop");
+ return Err(e);
}
};
self.conn.flush()?;
}
}
- /// Handle a property notify event, by doing *todo*
+ /// Update client properties when they change in X11.
fn handle_property_notify(&mut self, e: &PropertyNotifyEvent) {
if x::ATOM_WM_HINTS == e.atom() {
let focused = self.clients.is_focused(e.window());
@@ -88,9 +114,9 @@ impl<'a> WM<'a> {
}
/// Cleanup this process' children and set some flags.
-/// This is necessary when used with `startx`
+/// This is necessary when used with `startx`.
fn cleanup_process_children() {
// TODO: dont transform children into zombies when they terminate
// TODO: cleanup zombies
- todo!()
+ // todo!()
}