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(());
+ }
+}