oxwm

https://git.tonybtw.com/oxwm.git git://git.tonybtw.com/oxwm.git

Added layout module, and dynamic tiling layout instead of hard coded layout. Changed just file to just open xephyr, need to run xclock from a 2nd terminal.

Commit
245689833f06b3773bed8bb4aaa79deeabe4f8b7
Parent
42edaf6
Author
tonybtw <tonybtw@tonybtw.com>
Date
2025-09-26 05:07:14

Diff

diff --git a/justfile b/justfile
index 8f1819d..5fe20ff 100644
--- a/justfile
+++ b/justfile
@@ -1,8 +1,4 @@
 test:
     pkill Xephyr || true
     Xephyr -screen 1280x800 :1 & sleep 1
-    DISPLAY=:1 cargo run &
-    sleep 1
-    DISPLAY=:1 xterm &
-    DISPLAY=:1 xclock &
-    wait
+    DISPLAY=:1 cargo run
diff --git a/src/layout/mod.rs b/src/layout/mod.rs
new file mode 100644
index 0000000..df0e970
--- /dev/null
+++ b/src/layout/mod.rs
@@ -0,0 +1,19 @@
+pub mod tiling;
+
+use x11rb::protocol::xproto::Window;
+
+pub trait Layout {
+    fn arrange(
+        &self,
+        windows: &[Window],
+        screen_width: u32,
+        screen_height: u32,
+    ) -> Vec<WindowGeometry>;
+}
+
+pub struct WindowGeometry {
+    pub x_coordinate: i32,
+    pub y_coordinate: i32,
+    pub width: u32,
+    pub height: u32,
+}
diff --git a/src/layout/tiling.rs b/src/layout/tiling.rs
new file mode 100644
index 0000000..28deb79
--- /dev/null
+++ b/src/layout/tiling.rs
@@ -0,0 +1,47 @@
+use super::{Layout, WindowGeometry};
+use x11rb::protocol::xproto::Window;
+
+pub struct TilingLayout;
+
+impl Layout for TilingLayout {
+    fn arrange(
+        &self,
+        windows: &[Window],
+        screen_width: u32,
+        screen_height: u32,
+    ) -> Vec<WindowGeometry> {
+        let count = windows.len();
+        if count == 0 {
+            return Vec::new();
+        }
+
+        if count == 1 {
+            vec![WindowGeometry {
+                x_coordinate: 0,
+                y_coordinate: 0,
+                width: screen_width,
+                height: screen_height,
+            }]
+        } else {
+            let master_width = screen_width / 2;
+            let mut geometries = vec![WindowGeometry {
+                x_coordinate: 0,
+                y_coordinate: 0,
+                width: master_width,
+                height: screen_height,
+            }];
+
+            let stack_height = screen_height / (count - 1) as u32;
+            for i in 1..count {
+                let y_offset = ((i - 1) as u32) * stack_height;
+                geometries.push(WindowGeometry {
+                    x_coordinate: master_width as i32,
+                    y_coordinate: y_offset as i32,
+                    width: master_width,
+                    height: stack_height,
+                });
+            }
+            return geometries;
+        }
+    }
+}
diff --git a/src/main.rs b/src/main.rs
index 789ecc7..938a384 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,57 +1,8 @@
 use anyhow::Result;
-use x11rb::connection::Connection;
-use x11rb::protocol::Event;
-use x11rb::protocol::xproto::*;
-use x11rb::rust_connection::RustConnection;
+mod layout;
+mod window_manager;
 
 fn main() -> Result<()> {
-    let (connection, screen_number) = x11rb::connect(None)?;
-    let root = connection.setup().roots[screen_number].root;
-    let screen = &connection.setup().roots[screen_number];
-
-    connection
-        .change_window_attributes(
-            root,
-            &ChangeWindowAttributesAux::new().event_mask(
-                EventMask::SUBSTRUCTURE_REDIRECT
-                    | EventMask::SUBSTRUCTURE_NOTIFY
-                    | EventMask::PROPERTY_CHANGE,
-            ),
-        )?
-        .check()?;
-
-    println!("oxwm started on display {}", screen_number);
-
-    let mut window_count = 0;
-    loop {
-        let event = connection.wait_for_event()?;
-        println!("event: {:?}", event);
-        match event {
-            Event::MapRequest(event) => {
-                connection.map_window(event.window)?;
-                let x_coordinate = if window_count == 0 {
-                    0
-                } else {
-                    (screen.width_in_pixels / 2) as i32
-                };
-                connection.configure_window(
-                    event.window,
-                    &ConfigureWindowAux::new()
-                        .x(x_coordinate)
-                        .y(0)
-                        .border_width(1)
-                        .width((screen.width_in_pixels / 2) as u32)
-                        .height(screen.height_in_pixels as u32),
-                )?;
-                window_count += 1;
-                connection.set_input_focus(
-                    InputFocus::POINTER_ROOT,
-                    event.window,
-                    x11rb::CURRENT_TIME,
-                )?;
-                connection.flush()?;
-            }
-            _ => {}
-        }
-    }
+    let mut window_manager = window_manager::WindowManager::new()?;
+    return window_manager.run();
 }
diff --git a/src/window_manager.rs b/src/window_manager.rs
new file mode 100644
index 0000000..dded7e3
--- /dev/null
+++ b/src/window_manager.rs
@@ -0,0 +1,94 @@
+use crate::layout::Layout;
+use crate::layout::tiling::TilingLayout;
+
+use anyhow::Result;
+
+use x11rb::connection::Connection;
+use x11rb::protocol::Event;
+use x11rb::protocol::xproto::*;
+use x11rb::rust_connection::RustConnection;
+
+pub struct WindowManager {
+    connection: RustConnection,
+    screen_number: usize,
+    screen: Screen,
+    windows: Vec<Window>,
+    layout: Box<dyn Layout>,
+}
+
+impl WindowManager {
+    pub fn new() -> Result<Self> {
+        let (connection, screen_number) = x11rb::connect(None)?;
+        let root = connection.setup().roots[screen_number].root;
+        let screen = connection.setup().roots[screen_number].clone();
+
+        connection
+            .change_window_attributes(
+                root,
+                &ChangeWindowAttributesAux::new().event_mask(
+                    EventMask::SUBSTRUCTURE_REDIRECT
+                        | EventMask::SUBSTRUCTURE_NOTIFY
+                        | EventMask::PROPERTY_CHANGE,
+                ),
+            )?
+            .check()?;
+
+        return Ok(Self {
+            connection,
+            screen_number,
+            screen,
+            windows: Vec::new(),
+            layout: Box::new(TilingLayout),
+        });
+    }
+
+    pub fn run(&mut self) -> Result<()> {
+        println!("oxwm started on display {}", self.screen_number);
+
+        loop {
+            let event = self.connection.wait_for_event()?;
+            println!("event: {:?}", event);
+            self.handle_event(event)?;
+        }
+    }
+
+    fn handle_event(&mut self, event: Event) -> Result<()> {
+        match event {
+            Event::MapRequest(event) => {
+                self.connection.map_window(event.window)?;
+                self.windows.push(event.window);
+                self.apply_layout()?;
+                self.connection.set_input_focus(
+                    InputFocus::POINTER_ROOT,
+                    event.window,
+                    x11rb::CURRENT_TIME,
+                )?;
+                self.connection.flush()?;
+            }
+            _ => {}
+        }
+        return Ok(());
+    }
+
+    fn apply_layout(&self) -> Result<()> {
+        let screen_width = self.screen.width_in_pixels as u32;
+        let screen_height = self.screen.height_in_pixels as u32;
+
+        let geometries = self
+            .layout
+            .arrange(&self.windows, screen_width, screen_height);
+
+        for (window, geometry) in self.windows.iter().zip(geometries.iter()) {
+            self.connection.configure_window(
+                *window,
+                &ConfigureWindowAux::new()
+                    .x(geometry.x_coordinate)
+                    .y(geometry.y_coordinate)
+                    .width(geometry.width)
+                    .height(geometry.height)
+                    .border_width(1),
+            )?;
+        }
+        return Ok(());
+    }
+}