oxwm

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

keybinds...

Commit
ed1cbf51c053ddaee0bee24549c2b308514d9529
Parent
882b52b
Author
tonybtw <tonybtw@tonybtw.com>
Date
2025-09-28 08:58:42

Diff

diff --git a/readme.org b/readme.org
index 2b378cd..1f53c3d 100644
--- a/readme.org
+++ b/readme.org
@@ -1,7 +1,7 @@
 #+AUTHOR: Tony
 #+STARTUP: overview
 
-* OxWM β€” Oxidize Window Manager
+* OXWM β€” DWM but Better (and oxidized)
 A dynamic window manager written in /RustπŸ¦€/, inspired by dwm but designed to evolve
 on its own. Configuration is done in Rust source code, keeping with the suckless
 philosophy of *"edit + recompile."*
@@ -24,7 +24,7 @@ nix develop
 cargo build
 #+end_src
 
-* πŸ§ͺ Running Tests with just
+* Testing Xephyr with Justfile
 The =justfile= includes a =test= recipe that starts Xephyr on =:1=, launches
 test clients (xterm, xclock), and runs oxwm in the foreground.
 
@@ -36,24 +36,55 @@ just test
 This should open a new Xephyr window. oxwm will attach to it and log X11
 events in your host terminal. Clients like xterm/xclock will appear inside Xephyr.
 
-* πŸ—Ί Roadmap (High Level)
-| Step | Task                                                                                  | Status |
-|------+---------------------------------------------------------------------------------------+--------|
-| 1    | Connect to X, claim =SubstructureRedirect=, print events                            | DONE   |
-| 2    | Handle =MapRequest= and =ConfigureRequest= so clients can map and appear in Xephyr | ⏳      |
-| 3    | Implement client tracking and a trivial tiling layout                              | ❌      |
-| 4    | Add keybindings (Mod+j/k, Mod+Enter, etc.)                                          | ❌      |
-| 5    | Per-tag state, multiple layouts, borders, focus handling                           | ❌      |
-| 6    | ICCCM/EWMH basics (WM_DELETE_WINDOW, fullscreen, etc)                              | ❌      |
-| 7    | Split into =oxwm-core= library and =oxwm-user= config crate                        | ❌      |
-| 8    | Polish developer workflow (Nix flake, just, docs)                                   | ❌      |
-
-* πŸ“„ Status
+* OXWM Todo List:
+** Reorganization Tasks
+- [ ] Move keyboard module to folder structure:
+  - [ ] Create =src/keyboard/mod.rs= with re-exports
+  - [ ] Move constants to =src/keyboard/keycodes.rs=
+  - [ ] Move key handlers to =src/keyboard/handlers.rs=
+  - [ ] Update imports in main.rs and window_manager.rs
+- [ ] Create =src/config.rs= in root directory for future configuration system
+
+** Core Window Management
+- [ ] Fix layout after program is closed (handle UnmapNotify events)
+  - [ ] Add UnmapNotify to event handling
+  - [ ] Remove closed windows from windows vector
+  - [ ] Re-apply layout after window removal
+- [ ] Add keybind to swap focus between windows
+  - [ ] Track focused window in WindowManager struct
+  - [ ] Implement focus cycling logic
+  - [ ] Add visual focus indication (borders/colors)
+
+** Key System Improvements
+- [ ] Connect config.rs to keyboard system for dynamic keybind generation
+- [ ] Add more dwm-like keybinds:
+  - [ ] Window focus switching (Alt+J/K)
+  - [ ] Master area resizing
+  - [ ] Layout switching
+  - [ ] Workspace/tag management
+- [ ] Better error handling for failed key grabs
+
+** Layout System
+- [ ] Add more layout types (monocle, floating)
+- [ ] Handle window resize requests properly
+- [ ] Add configurable gaps between windows
+- [ ] Implement layout switching keybinds
+
+** Polish & Features
+- [ ] Clean window destruction/cleanup
+- [ ] Handle edge cases (empty window list, invalid windows)
+- [ ] Add status bar integration
+- [ ] Better error messages and logging
+
+** Priority
+Reorganization and UnmapNotify handling should be immediate priorities.
+
+* Status
 - Rust + x11rb skeleton running
 - Nix flake devShell available
 - =just test= launches Xephyr, clients, and oxwm
 
-* πŸ“œ License
+* License
 [[https://www.gnu.org/licenses/gpl-3.0.en.html][GPL]]
 
 
diff --git a/src/keyboard.rs b/src/keyboard.rs
new file mode 100644
index 0000000..f1508e2
--- /dev/null
+++ b/src/keyboard.rs
@@ -0,0 +1,28 @@
+// wip
+pub const RETURN: u8 = 36;
+pub const Q: u8 = 24;
+pub const ESCAPE: u8 = 9;
+pub const SPACE: u8 = 65;
+pub const TAB: u8 = 23;
+pub const BACKSPACE: u8 = 22;
+pub const DELETE: u8 = 119;
+
+// Function keys
+pub const F1: u8 = 67;
+pub const F2: u8 = 68;
+pub const F3: u8 = 69;
+pub const F4: u8 = 70;
+
+// Letters (assuming QWERTY)
+pub const A: u8 = 38;
+pub const S: u8 = 39;
+pub const D: u8 = 40;
+pub const F: u8 = 41;
+pub const J: u8 = 44;
+pub const K: u8 = 45;
+pub const L: u8 = 46;
+
+// Numbers
+pub const KEY_1: u8 = 10;
+pub const KEY_2: u8 = 11;
+pub const KEY_3: u8 = 12;
diff --git a/src/keys.rs b/src/keys.rs
new file mode 100644
index 0000000..af2a0bf
--- /dev/null
+++ b/src/keys.rs
@@ -0,0 +1,55 @@
+use crate::keyboard;
+use anyhow::Result;
+use x11rb::connection::Connection;
+use x11rb::protocol::xproto::*;
+
+pub fn setup_keybinds(connection: &impl Connection, root: Window) -> Result<()> {
+    connection.grab_key(
+        false,
+        root,
+        ModMask::M4.into(),
+        keyboard::RETURN,
+        GrabMode::ASYNC,
+        GrabMode::ASYNC,
+    )?;
+    connection.grab_key(
+        false,
+        root,
+        (ModMask::M1 | ModMask::SHIFT).into(),
+        keyboard::Q,
+        GrabMode::ASYNC,
+        GrabMode::ASYNC,
+    )?;
+
+    connection.grab_key(
+        false,
+        root,
+        ModMask::M4.into(),
+        keyboard::Q,
+        GrabMode::ASYNC,
+        GrabMode::ASYNC,
+    )?;
+
+    Ok(())
+}
+
+pub fn handle_key_press(connection: &impl Connection, event: KeyPressEvent) -> Result<()> {
+    println!("KeyPress: detail={}, state={:?}", event.detail, event.state);
+
+    match (event.detail, event.state) {
+        (keyboard::RETURN, state) if state.contains(KeyButMask::MOD1) => {
+            println!("Spawning terminal");
+            std::process::Command::new("xterm").spawn()?;
+        }
+        (keyboard::Q, state) if state.contains(KeyButMask::MOD1 | KeyButMask::SHIFT) => {
+            println!("Closing focused window");
+            let focus_reply = connection.get_input_focus()?.reply()?;
+            if focus_reply.focus != x11rb::NONE && focus_reply.focus != event.root {
+                connection.kill_client(focus_reply.focus)?;
+                connection.flush()?;
+            }
+        }
+        _ => {}
+    }
+    Ok(())
+}
diff --git a/src/main.rs b/src/main.rs
index 938a384..96967ce 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,4 +1,6 @@
 use anyhow::Result;
+mod keyboard;
+mod keys;
 mod layout;
 mod window_manager;
 
diff --git a/src/window_manager.rs b/src/window_manager.rs
index dded7e3..f63758c 100644
--- a/src/window_manager.rs
+++ b/src/window_manager.rs
@@ -1,3 +1,4 @@
+use crate::keys;
 use crate::layout::Layout;
 use crate::layout::tiling::TilingLayout;
 
@@ -11,6 +12,7 @@ use x11rb::rust_connection::RustConnection;
 pub struct WindowManager {
     connection: RustConnection,
     screen_number: usize,
+    root: Window,
     screen: Screen,
     windows: Vec<Window>,
     layout: Box<dyn Layout>,
@@ -28,7 +30,8 @@ impl WindowManager {
                 &ChangeWindowAttributesAux::new().event_mask(
                     EventMask::SUBSTRUCTURE_REDIRECT
                         | EventMask::SUBSTRUCTURE_NOTIFY
-                        | EventMask::PROPERTY_CHANGE,
+                        | EventMask::PROPERTY_CHANGE
+                        | EventMask::KEY_PRESS,
                 ),
             )?
             .check()?;
@@ -36,6 +39,7 @@ impl WindowManager {
         return Ok(Self {
             connection,
             screen_number,
+            root,
             screen,
             windows: Vec::new(),
             layout: Box::new(TilingLayout),
@@ -45,6 +49,8 @@ impl WindowManager {
     pub fn run(&mut self) -> Result<()> {
         println!("oxwm started on display {}", self.screen_number);
 
+        keys::setup_keybinds(&self.connection, self.root)?;
+
         loop {
             let event = self.connection.wait_for_event()?;
             println!("event: {:?}", event);
@@ -65,6 +71,10 @@ impl WindowManager {
                 )?;
                 self.connection.flush()?;
             }
+            Event::KeyPress(event) => {
+                println!("KeyPress event received!");
+                keys::handle_key_press(&self.connection, event)?;
+            }
             _ => {}
         }
         return Ok(());