From 7d5aae9cbecde004165db97593bc861f5a234b5b Mon Sep 17 00:00:00 2001 From: noonebtw Date: Sun, 25 Apr 2021 05:48:45 +0200 Subject: [PATCH] should work init --- src/clients.rs | 44 ++++++++ src/main.rs | 4 +- src/state.rs | 289 ++++++++++++++++++++++++++++++++++++++++++++++++- src/xlib.rs | 59 +++++++++- 4 files changed, 384 insertions(+), 12 deletions(-) diff --git a/src/clients.rs b/src/clients.rs index 9c60956..2b0dfd8 100644 --- a/src/clients.rs +++ b/src/clients.rs @@ -214,6 +214,20 @@ impl ClientEntry { } } + pub fn is_floating(&self) -> bool { + match self { + ClientEntry::Floating(_) => true, + _ => false, + } + } + + pub fn is_tiled(&self) -> bool { + match self { + ClientEntry::Tiled(_) => true, + _ => false, + } + } + pub fn is_occupied(&self) -> bool { !self.is_vacant() } @@ -328,6 +342,19 @@ impl ClientState { } } + pub fn get_mut(&mut self, key: &K) -> ClientEntry<&mut Client> + where + K: ClientKey, + { + match self.clients.get_mut(&key.key()) { + Some(client) => ClientEntry::Tiled(client), + None => match self.floating_clients.get_mut(&key.key()) { + Some(client) => ClientEntry::Floating(client), + None => ClientEntry::Vacant, + }, + } + } + pub fn get_focused(&self) -> ClientEntry<&Client> { if let Some(focused) = self.focused { self.get(&focused) @@ -344,6 +371,23 @@ impl ClientState { self.virtual_screens.rotate_left(1); } + /** + Sets a tiled client to floating and returns true, does nothing for a floating client and + returns false. + */ + pub fn set_floating(&mut self, key: &K) -> bool + where + K: ClientKey, + { + if self.get(key).is_floating() { + self.toggle_floating(key); + + true + } else { + false + } + } + pub fn toggle_floating(&mut self, key: &K) where K: ClientKey, diff --git a/src/main.rs b/src/main.rs index b4d59a4..94e3f06 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,6 @@ use std::io::Result; mod clients; mod state; mod util; -mod wm; mod xlib; #[allow(dead_code)] @@ -33,6 +32,5 @@ fn main() -> Result<()> { simple_logger::SimpleLogger::new().init().unwrap(); info!("Hello, World!"); - //wm::WMState::init().run(); - state::WindowManager::new().run(); + state::WindowManager::new().init().run(); } diff --git a/src/state.rs b/src/state.rs index 51ffaa1..265583d 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,14 +1,21 @@ -use log::info; +use std::rc::Rc; -use x11::xlib::{self, Window, XEvent}; +use log::{error, info}; + +use x11::xlib::{self, ShiftMask, Window, XButtonEvent, XEvent, XKeyEvent, XMotionEvent}; +use xlib::{ButtonPressMask, ButtonReleaseMask, Mod1Mask, PointerMotionMask}; use crate::{ clients::{Client, ClientKey, ClientState}, + xlib::KeyOrButton, xlib::XLib, }; pub struct WindowManager { clients: ClientState, + move_window: Option, + resize_window: Option, + keybinds: Vec, xlib: XLib, } @@ -17,30 +24,282 @@ pub enum Direction { Right, } +struct MoveWindow { + key: Window, + cached_cursor_position: (i32, i32), +} + +#[derive(Clone)] +struct KeyBinding { + key: KeyOrButton, + closure: Rc, +} + impl WindowManager { pub fn new() -> Self { let clients = ClientState::with_virtualscreens(3); let xlib = XLib::new().init(); - Self { clients, xlib } + Self { + clients, + move_window: None, + resize_window: None, + keybinds: Vec::new(), + xlib, + } + } + + pub fn init(mut self) -> Self { + self.xlib.add_global_keybind(KeyOrButton::button( + 1, + Mod1Mask, + ButtonPressMask | ButtonReleaseMask | PointerMotionMask, + )); + self.xlib.add_global_keybind(KeyOrButton::button( + 2, + Mod1Mask, + ButtonPressMask | ButtonReleaseMask | PointerMotionMask, + )); + self.xlib.add_global_keybind(KeyOrButton::button( + 3, + Mod1Mask, + ButtonPressMask | ButtonReleaseMask | PointerMotionMask, + )); + + self.add_keybind(KeyBinding::new( + self.xlib.make_key("M", Mod1Mask), + Self::handle_switch_stack, + )); + + self.add_keybind(KeyBinding::new( + self.xlib.make_key("Q", Mod1Mask), + Self::kill_client, + )); + + self.add_keybind(KeyBinding::new( + self.xlib.make_key("Q", Mod1Mask | ShiftMask), + |wm, _| wm.quit(), + )); + + self.add_keybind(KeyBinding::new( + self.xlib.make_key("T", Mod1Mask | ShiftMask), + |wm, _| wm.spawn("xterm", &[]), + )); + + self.add_keybind(KeyBinding::new( + self.xlib.make_key("Return", Mod1Mask | ShiftMask), + |wm, _| wm.spawn("xterm", &[]), + )); + + self.add_keybind(KeyBinding::new( + self.xlib.make_key("Left", Mod1Mask), + |wm, _| wm.rotate_virtual_screen(Direction::Left), + )); + + self.add_keybind(KeyBinding::new( + self.xlib.make_key("Right", Mod1Mask), + |wm, _| wm.rotate_virtual_screen(Direction::Right), + )); + + self + } + + fn add_keybind(&mut self, keybind: KeyBinding) { + self.xlib.add_global_keybind(keybind.key); + self.keybinds.push(keybind); } pub fn run(mut self) -> ! { loop { let event = self.xlib.next_event(); + self.handle_move_window(&event); + self.handle_resize_client(&event); + self.handle_toggle_floating(&event); + match event.get_type() { xlib::MapRequest => self.map_request(&event), xlib::UnmapNotify => self.unmap_notify(&event), xlib::ConfigureRequest => self.configure_request(&event), xlib::EnterNotify => self.enter_notify(&event), xlib::DestroyNotify => self.destroy_notify(&event), - xlib::ButtonPress => self.destroy_notify(&event), + xlib::ButtonPress => self.button_notify(&event), + xlib::KeyPress => self.handle_keybinds(event.as_ref()), _ => {} } } } + fn quit(&self) -> ! { + self.xlib.close_dpy(); + + std::process::exit(0); + } + + fn kill_client(&mut self, event: &XKeyEvent) { + if let Some(client) = self.clients.get(&event.subwindow).into_option() { + self.xlib.kill_client(client); + } + } + + // TODO: change this somehow cuz I'm not a big fan of this "hardcoded" keybind stuff + fn handle_keybinds(&mut self, event: &XKeyEvent) { + let clean_mask = self.xlib.get_clean_mask(); + for kb in self.keybinds.clone().into_iter() { + if let KeyOrButton::Key(keycode, modmask) = kb.key { + if keycode as u32 == event.keycode + && modmask & clean_mask == event.state & clean_mask + { + (kb.closure)(self, event); + } + } + } + } + + fn handle_toggle_floating(&mut self, event: &XEvent) { + if event.get_type() == xlib::ButtonPress { + let event = unsafe { &event.button }; + let clean_mask = self.xlib.get_clean_mask(); + + if event.button == 2 && event.state & clean_mask == Mod1Mask & clean_mask { + if self.clients.contains(&event.subwindow) { + self.clients.toggle_floating(&event.subwindow); + + self.arrange_clients(); + } + } + } + } + + fn handle_switch_stack(&mut self, event: &XKeyEvent) { + self.clients.switch_stack_for_client(&event.subwindow); + + self.arrange_clients(); + } + + fn handle_move_window(&mut self, event: &XEvent) { + let clean_mask = self.xlib.get_clean_mask(); + + match event.get_type() { + xlib::ButtonPress => { + let event: &XButtonEvent = event.as_ref(); + + if self.move_window.is_none() + && event.button == 1 + && event.state & clean_mask == Mod1Mask & clean_mask + && self.clients.contains(&event.subwindow) + { + // if client is tiled, set to floating + if self.clients.get(&event.subwindow).is_tiled() { + self.clients.toggle_floating(&event.subwindow); + + self.arrange_clients(); + } + + self.move_window = Some(MoveWindow { + key: event.subwindow, + cached_cursor_position: (event.x, event.y), + }); + } + } + + // reset on release + xlib::ButtonRelease => { + let event: &XButtonEvent = event.as_ref(); + + if event.button == 1 && self.move_window.is_some() { + self.move_window = None; + } + } + + xlib::MotionNotify => { + let event: &XMotionEvent = event.as_ref(); + + if let Some(move_window) = &self.move_window { + let (x, y) = ( + event.x - move_window.cached_cursor_position.0, + event.y - move_window.cached_cursor_position.1, + ); + + if let Some(client) = self.clients.get_mut(&move_window.key).into_option() { + let position = &mut client.position; + position.0 += x; + position.1 += y; + + self.xlib.move_client(client); + } + } + } + _ => {} + } + } + + fn handle_resize_client(&mut self, event: &XEvent) { + let clean_mask = self.xlib.get_clean_mask(); + + match event.get_type() { + xlib::ButtonPress => { + let event: &XButtonEvent = event.as_ref(); + + if self.resize_window.is_none() + && event.button == 3 + && event.state & clean_mask == Mod1Mask & clean_mask + && self.clients.contains(&event.subwindow) + { + // if client is tiled, set to floating + if self.clients.set_floating(&event.subwindow) { + self.arrange_clients(); + } + + let client = self.clients.get(&event.subwindow).unwrap(); + + let position = { + ( + client.position.0 + client.size.0, + client.position.1 + client.size.1, + ) + }; + + self.xlib.move_cursor(client.window, position); + + self.resize_window = Some(MoveWindow { + key: event.subwindow, + cached_cursor_position: position, + }); + } + } + + // reset on release + xlib::ButtonRelease => { + let event: &XButtonEvent = event.as_ref(); + + if event.button == 3 && self.resize_window.is_some() { + self.resize_window = None; + } + } + + xlib::MotionNotify => { + let event: &XMotionEvent = event.as_ref(); + + if let Some(resize_window) = &self.resize_window { + let (x, y) = ( + event.x - resize_window.cached_cursor_position.0, + event.y - resize_window.cached_cursor_position.1, + ); + + if let Some(client) = self.clients.get_mut(&resize_window.key).into_option() { + let size = &mut client.size; + size.0 += x; + size.1 += y; + + self.xlib.resize_client(client); + } + } + } + _ => {} + } + } + fn rotate_virtual_screen(&mut self, dir: Direction) { match dir { Direction::Left => self.clients.rotate_left(), @@ -87,6 +346,7 @@ impl WindowManager { } } + #[allow(dead_code)] fn unfocus_client(&mut self) { if let Some(client) = self.clients.unfocus().into_option() { self.xlib.unfocus_client(client); @@ -158,4 +418,25 @@ impl WindowManager { self.xlib.raise_client(client); } } + + pub fn spawn(&self, command: &str, args: &[&str]) { + match std::process::Command::new(command).args(args).spawn() { + Ok(_) => {} + Err(err) => { + error!("Failed to spawn {:?}: {:?}", command, err); + } + } + } +} + +impl KeyBinding { + fn new(key: KeyOrButton, closure: F) -> Self + where + F: Fn(&mut WindowManager, &XKeyEvent) + 'static, + { + Self { + key, + closure: Rc::new(closure), + } + } } diff --git a/src/xlib.rs b/src/xlib.rs index a734af6..bb04212 100644 --- a/src/xlib.rs +++ b/src/xlib.rs @@ -5,8 +5,9 @@ use x11::xlib::{ self, Atom, ButtonPressMask, CWEventMask, ControlMask, EnterWindowMask, FocusChangeMask, LockMask, Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask, PointerMotionMask, PropertyChangeMask, ShiftMask, StructureNotifyMask, SubstructureNotifyMask, - SubstructureRedirectMask, Window, XConfigureRequestEvent, XDefaultScreen, XEvent, XInternAtom, - XMapWindow, XOpenDisplay, XRaiseWindow, XRootWindow, XSync, + SubstructureRedirectMask, Window, XCloseDisplay, XConfigureRequestEvent, XDefaultScreen, + XEvent, XInternAtom, XKillClient, XMapWindow, XOpenDisplay, XRaiseWindow, XRootWindow, XSync, + XWarpPointer, }; use xlib::GrabModeAsync; @@ -17,6 +18,7 @@ pub struct XLib { root: Window, screen: i32, atoms: Atoms, + global_keybinds: Vec, } struct Atoms { @@ -26,17 +28,19 @@ struct Atoms { take_focus: Atom, } +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum KeyOrButton { Key(i32, u32), Button(u32, u32, u64), } impl KeyOrButton { + #[allow(dead_code)] pub fn key(keycode: i32, modmask: u32) -> Self { Self::Key(keycode, modmask) } - pub fn button(button: u32, modmask: u32, buttonmask: u64) -> Self { - Self::Button(button, modmask, buttonmask) + pub fn button(button: u32, modmask: u32, buttonmask: i64) -> Self { + Self::Button(button, modmask, buttonmask as u64) } } @@ -59,6 +63,7 @@ impl XLib { Self { atoms: Atoms::init(display.clone()), + global_keybinds: Vec::new(), root, screen, display, @@ -90,6 +95,17 @@ impl XLib { self } + pub fn add_global_keybind(&mut self, key: KeyOrButton) { + self.global_keybinds.push(key); + } + + #[allow(dead_code)] + pub fn remove_global_keybind(&mut self, key: &KeyOrButton) { + if self.global_keybinds.contains(key) { + self.global_keybinds.retain(|kb| kb != key); + } + } + fn dpy(&self) -> *mut xlib::Display { self.display.get() } @@ -335,6 +351,10 @@ impl XLib { EnterWindowMask | FocusChangeMask | PropertyChangeMask | StructureNotifyMask, ); } + + for kb in self.global_keybinds.iter() { + self.grab_key_or_button(window, kb); + } } pub fn dimensions(&self) -> (i32, i32) { @@ -346,6 +366,26 @@ impl XLib { } } + pub fn close_dpy(&self) { + unsafe { + XCloseDisplay(self.dpy()); + } + } + + pub fn kill_client(&self, client: &Client) { + if !self.send_event(client, self.atoms.delete_window) { + unsafe { + XKillClient(self.dpy(), client.window); + } + } + } + + pub fn move_cursor(&self, window: Window, position: (i32, i32)) { + unsafe { + XWarpPointer(self.dpy(), 0, window, 0, 0, 0, 0, position.0, position.1); + } + } + fn check_for_protocol(&self, client: &Client, proto: xlib::Atom) -> bool { let mut protos: *mut xlib::Atom = null_mut(); let mut num_protos: i32 = 0; @@ -391,6 +431,15 @@ impl XLib { } } + pub fn make_key(&self, key: S, modmask: u32) -> KeyOrButton + where + S: AsRef, + { + let key = self.keycode(key); + + KeyOrButton::Key(key, modmask) + } + fn keycode(&self, string: S) -> i32 where S: AsRef, @@ -424,7 +473,7 @@ impl XLib { 0 } - fn get_clean_mask(&self) -> u32 { + pub fn get_clean_mask(&self) -> u32 { !(self.get_numlock_mask() | LockMask) & (ShiftMask | ControlMask | Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) }