From 4235fff343b253d64200b5391835991c87e2d0e7 Mon Sep 17 00:00:00 2001 From: noonebtw Date: Sun, 25 Apr 2021 07:45:18 +0200 Subject: [PATCH] v0.2 --- .gitignore | 2 + Cargo.toml | 2 - src/clients.rs | 78 +++++++++++++++++++++----------------- src/state.rs | 81 ++++++++++++++++++++++----------------- src/xlib.rs | 100 ++++++++++++++++++++++++++++++------------------- 5 files changed, 153 insertions(+), 110 deletions(-) diff --git a/.gitignore b/.gitignore index c27467e..2c6f84f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ /target *~ /Cargo.lock +/.cargo/ +/wmlog diff --git a/Cargo.toml b/Cargo.toml index 9d9fbd3..9eece30 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,8 +7,6 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -nix = "0.19.1" x11 = {version = "2.18.2", features = ["xlib"] } log = "0.4.13" -weak-table = "0.3.0" simple_logger = "1.11.0" \ No newline at end of file diff --git a/src/clients.rs b/src/clients.rs index 2b0dfd8..5aa63c4 100644 --- a/src/clients.rs +++ b/src/clients.rs @@ -3,6 +3,8 @@ use std::collections::HashMap; use std::num::NonZeroI32; +use log::{debug, error}; + use crate::util::BuildIdentityHasher; mod client { @@ -282,7 +284,7 @@ impl ClientState { self.clients.insert(key, client); if let Some(vs) = self.virtual_screens.front_mut() { - vs.aux.push(key); + vs.insert(&key); } self.focus_client(&key); @@ -373,13 +375,13 @@ impl ClientState { /** Sets a tiled client to floating and returns true, does nothing for a floating client and - returns false. + returns false. If this function returns `true` you have to call `arrange_clients` after. */ pub fn set_floating(&mut self, key: &K) -> bool where K: ClientKey, { - if self.get(key).is_floating() { + if self.get(key).is_tiled() { self.toggle_floating(key); true @@ -388,6 +390,10 @@ impl ClientState { } } + /** + This function invalidates the tiling, call `arrange_clients` to fix it again (it doesn't do it + automatically since xlib has to move and resize all windows anyways). + */ pub fn toggle_floating(&mut self, key: &K) where K: ClientKey, @@ -404,10 +410,12 @@ impl ClientState { (None, Some(client)) => { self.clients.insert(key, client); if let Some(vs) = self.virtual_screens.front_mut() { - vs.aux.push(key); + vs.insert(&key); } } - _ => {} + _ => { + error!("wtf? Client was present in tiled and floating list.") + } }; } @@ -497,38 +505,12 @@ impl ClientState { } } - /** - This shouldn't be ever needed to be called since any client added is automatically added - to the first `VirtualScreen`. - */ - #[deprecated] - fn stack_unstacked(&mut self) { - let unstacked = self - .clients - .iter() - .filter(|&(key, _)| self.get_virtualscreen_for_client(key).is_none()) - .map(|(key, _)| key) - .collect::>(); - - if let Some(vs) = self.virtual_screens.front_mut() { - vs.aux.extend(unstacked.into_iter()); - } - } - pub fn switch_stack_for_client(&mut self, key: &K) where K: ClientKey, { if let Some(vs) = self.get_mut_virtualscreen_for_client(key) { - match vs.master.iter().position(|&key| key == key.key()) { - Some(index) => { - vs.aux.extend(vs.master.drain(index..=index)); - } - None => { - let index = vs.aux.iter().position(|&key| key == key.key()).unwrap(); - vs.master.extend(vs.aux.drain(index..=index)); - } - } + vs.switch_stack_for_client(key); } } @@ -554,7 +536,7 @@ impl ClientState { // should be fine to unwrap since we will always have at least 1 virtual screen if let Some(vs) = self.virtual_screens.front_mut() { // if aux is empty -> width : width / 2 - let width = width / (1 + i32::from(!vs.aux.is_empty() && !vs.master.is_empty())); + let width = width / (1 + i32::from(!vs.aux.is_empty())); // make sure we dont devide by 0 let master_height = height @@ -583,7 +565,7 @@ impl ClientState { .zip(repeat(aux_height).zip(repeat(width))), ) { - let size = (width + gap * 2, height + gap * 2); + let size = (width - gap * 2, height - gap * 2); let position = (x + gap, height * i as i32 + gap); if let Some(client) = self.clients.get_mut(key) { @@ -595,6 +577,8 @@ impl ClientState { } } } + + debug!("{:#?}", self); } // Should have xlib send those changes back to the x server after this function @@ -617,6 +601,15 @@ impl VirtualScreen { self.master.contains(&key.key()) || self.aux.contains(&key.key()) } + fn insert(&mut self, key: &K) + where + K: ClientKey, + { + self.aux.push(key.key()); + + self.refresh(); + } + fn remove(&mut self, key: &K) where K: ClientKey, @@ -628,6 +621,23 @@ impl VirtualScreen { self.refresh(); } + fn switch_stack_for_client(&mut self, key: &K) + where + K: ClientKey, + { + match self.master.iter().position(|&k| k == key.key()) { + Some(index) => { + self.aux.extend(self.master.drain(index..=index)); + } + None => { + let index = self.aux.iter().position(|&k| k == key.key()).unwrap(); + self.master.extend(self.aux.drain(index..=index)); + } + } + + self.refresh(); + } + /** if `self.master` is empty but `self.aux` has at least one client, drain from aux to master this ensures that if only 1 `Client` is on this `VirtualScreen` it will be on the master stack diff --git a/src/state.rs b/src/state.rs index 265583d..3b99c1d 100644 --- a/src/state.rs +++ b/src/state.rs @@ -3,7 +3,10 @@ use std::rc::Rc; use log::{error, info}; use x11::xlib::{self, ShiftMask, Window, XButtonEvent, XEvent, XKeyEvent, XMotionEvent}; -use xlib::{ButtonPressMask, ButtonReleaseMask, Mod1Mask, PointerMotionMask}; +use xlib::{ + ButtonPressMask, ButtonReleaseMask, Mod1Mask, PointerMotionMask, XConfigureRequestEvent, + XCrossingEvent, XDestroyWindowEvent, XMapRequestEvent, XUnmapEvent, +}; use crate::{ clients::{Client, ClientKey, ClientState}, @@ -19,6 +22,7 @@ pub struct WindowManager { xlib: XLib, } +#[derive(Debug)] pub enum Direction { Left, Right, @@ -38,7 +42,7 @@ struct KeyBinding { impl WindowManager { pub fn new() -> Self { let clients = ClientState::with_virtualscreens(3); - let xlib = XLib::new().init(); + let xlib = XLib::new(); Self { clients, @@ -82,7 +86,7 @@ impl WindowManager { )); self.add_keybind(KeyBinding::new( - self.xlib.make_key("T", Mod1Mask | ShiftMask), + self.xlib.make_key("T", Mod1Mask), |wm, _| wm.spawn("xterm", &[]), )); @@ -101,6 +105,8 @@ impl WindowManager { |wm, _| wm.rotate_virtual_screen(Direction::Right), )); + self.xlib.init(); + self } @@ -113,9 +119,9 @@ impl WindowManager { loop { let event = self.xlib.next_event(); + self.handle_toggle_floating(&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), @@ -133,6 +139,8 @@ impl WindowManager { fn quit(&self) -> ! { self.xlib.close_dpy(); + info!("Goodbye."); + std::process::exit(0); } @@ -158,11 +166,13 @@ impl WindowManager { fn handle_toggle_floating(&mut self, event: &XEvent) { if event.get_type() == xlib::ButtonPress { - let event = unsafe { &event.button }; + let event: &XButtonEvent = event.as_ref(); 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) { + info!("toggleing floating for {:?}", event.subwindow); + self.clients.toggle_floating(&event.subwindow); self.arrange_clients(); @@ -190,9 +200,7 @@ impl WindowManager { && 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); - + if self.clients.set_floating(&event.subwindow) { self.arrange_clients(); } @@ -215,12 +223,14 @@ impl WindowManager { xlib::MotionNotify => { let event: &XMotionEvent = event.as_ref(); - if let Some(move_window) = &self.move_window { + if let Some(move_window) = &mut self.move_window { let (x, y) = ( event.x - move_window.cached_cursor_position.0, event.y - move_window.cached_cursor_position.1, ); + move_window.cached_cursor_position = (event.x, event.y); + if let Some(client) = self.clients.get_mut(&move_window.key).into_option() { let position = &mut client.position; position.0 += x; @@ -281,16 +291,19 @@ impl WindowManager { xlib::MotionNotify => { let event: &XMotionEvent = event.as_ref(); - if let Some(resize_window) = &self.resize_window { + if let Some(resize_window) = &mut self.resize_window { let (x, y) = ( event.x - resize_window.cached_cursor_position.0, event.y - resize_window.cached_cursor_position.1, ); + resize_window.cached_cursor_position = (event.x, event.y); + if let Some(client) = self.clients.get_mut(&resize_window.key).into_option() { let size = &mut client.size; - size.0 += x; - size.1 += y; + + size.0 = std::cmp::max(1, size.0 + x); + size.1 = std::cmp::max(1, size.1 + y); self.xlib.resize_client(client); } @@ -301,19 +314,19 @@ impl WindowManager { } fn rotate_virtual_screen(&mut self, dir: Direction) { + info!("rotateing VS: {:?}", dir); + match dir { Direction::Left => self.clients.rotate_left(), Direction::Right => self.clients.rotate_right(), } - self.clients - .iter_current_screen() - .for_each(|(_, c)| self.xlib.move_resize_client(c)); - self.clients .iter_hidden() .for_each(|(_, c)| self.xlib.hide_client(c)); + self.arrange_clients(); + // focus first client in all visible clients let to_focus = self.clients.iter_visible().next().map(|(k, _)| k).cloned(); @@ -324,11 +337,15 @@ impl WindowManager { fn arrange_clients(&mut self) { let (width, height) = self.xlib.dimensions(); - self.clients.arrange_virtual_screen(width, height, None); + self.clients.arrange_virtual_screen(width, height, Some(2)); self.clients .iter_current_screen() .for_each(|(_, c)| self.xlib.move_resize_client(c)); + + self.clients + .iter_floating() + .for_each(|(_, c)| self.xlib.raise_client(c)); } fn focus_client(&mut self, key: &K) @@ -337,13 +354,13 @@ impl WindowManager { { let (new, old) = self.clients.focus_client(key); - if let Some(new) = new.into_option() { - self.xlib.focus_client(new); - } - if let Some(old) = old.into_option() { self.xlib.unfocus_client(old); } + + if let Some(new) = new.into_option() { + self.xlib.focus_client(new); + } } #[allow(dead_code)] @@ -363,20 +380,17 @@ impl WindowManager { } fn map_request(&mut self, event: &XEvent) { - let event = unsafe { &event.map_request }; - - info!("MapRequest: {:?}", event); + let event: &XMapRequestEvent = event.as_ref(); if !self.clients.contains(&event.window) { - info!("MapRequest: new client"); + info!("MapRequest: new client: {:?}", event.window); self.new_client(event.window); } } fn unmap_notify(&mut self, event: &XEvent) { - let event = unsafe { &event.unmap }; - info!("UnmapNotify: {:?}", event); + let event: &XUnmapEvent = event.as_ref(); self.clients.remove(&event.window); @@ -384,8 +398,7 @@ impl WindowManager { } fn destroy_notify(&mut self, event: &XEvent) { - let event = unsafe { &event.destroy_window }; - info!("DestroyNotify: {:?}", event); + let event: &XDestroyWindowEvent = event.as_ref(); self.clients.remove(&event.window); @@ -393,8 +406,7 @@ impl WindowManager { } fn configure_request(&mut self, event: &XEvent) { - let event = unsafe { &event.configure_request }; - info!("ConfigureRequest: {:?}", event); + let event: &XConfigureRequestEvent = event.as_ref(); match self.clients.get(&event.window).into_option() { Some(client) => self.xlib.configure_client(client), @@ -403,15 +415,13 @@ impl WindowManager { } fn enter_notify(&mut self, event: &XEvent) { - let event = unsafe { &event.crossing }; - info!("EnterNotify: {:?}", event); + let event: &XCrossingEvent = event.as_ref(); self.focus_client(&event.window); } fn button_notify(&mut self, event: &XEvent) { - let event = unsafe { &event.button }; - info!("EnterNotify: {:?}", event); + let event: &XButtonEvent = event.as_ref(); self.focus_client(&event.subwindow); if let Some(client) = self.clients.get(&event.subwindow).into_option() { @@ -420,6 +430,7 @@ impl WindowManager { } pub fn spawn(&self, command: &str, args: &[&str]) { + info!("spawn: {:?} {:?}", command, args.join(" ")); match std::process::Command::new(command).args(args).spawn() { Ok(_) => {} Err(err) => { diff --git a/src/xlib.rs b/src/xlib.rs index bb04212..45ec091 100644 --- a/src/xlib.rs +++ b/src/xlib.rs @@ -11,6 +11,8 @@ use x11::xlib::{ }; use xlib::GrabModeAsync; +use log::error; + use crate::clients::Client; pub struct XLib { @@ -70,7 +72,7 @@ impl XLib { } } - pub fn init(self) -> Self { + pub fn init(&mut self) { unsafe { let mut window_attributes = std::mem::MaybeUninit::::zeroed().assume_init(); @@ -92,13 +94,19 @@ impl XLib { xlib::XSelectInput(self.dpy(), self.root, window_attributes.event_mask); } - self + self.grab_global_keybinds(self.root); } pub fn add_global_keybind(&mut self, key: KeyOrButton) { self.global_keybinds.push(key); } + fn grab_global_keybinds(&self, window: Window) { + for kb in self.global_keybinds.iter() { + self.grab_key_or_button(window, kb); + } + } + #[allow(dead_code)] pub fn remove_global_keybind(&mut self, key: &KeyOrButton) { if self.global_keybinds.contains(key) { @@ -206,18 +214,22 @@ impl XLib { stack_mode: 0, }; - unsafe { - xlib::XConfigureWindow( - self.dpy(), - client.window, - (xlib::CWY | xlib::CWX | xlib::CWHeight | xlib::CWWidth) as u32, - &mut windowchanges, - ); + if client.size.0 < 1 || client.size.1 < 1 { + error!("client {:?} size is less than 1 pixel!", client); + } else { + unsafe { + xlib::XConfigureWindow( + self.dpy(), + client.window, + (xlib::CWY | xlib::CWX | xlib::CWHeight | xlib::CWWidth) as u32, + &mut windowchanges, + ); - // I don't think I have to call this ~ - //self.configure(client); + // I don't think I have to call this ~ + //self.configure_client(client); - xlib::XSync(self.dpy(), 0); + xlib::XSync(self.dpy(), 0); + } } } @@ -232,15 +244,19 @@ impl XLib { stack_mode: 0, }; - unsafe { - xlib::XConfigureWindow( - self.dpy(), - client.window, - (xlib::CWWidth | xlib::CWHeight) as u32, - &mut wc, - ); + if client.size.0 < 1 || client.size.1 < 1 { + error!("client {:?} size is less than 1 pixel!", client); + } else { + unsafe { + xlib::XConfigureWindow( + self.dpy(), + client.window, + (xlib::CWX | xlib::CWY) as u32, + &mut wc, + ); - xlib::XSync(self.dpy(), 0); + xlib::XSync(self.dpy(), 0); + } } } @@ -255,15 +271,19 @@ impl XLib { stack_mode: 0, }; - unsafe { - xlib::XConfigureWindow( - self.dpy(), - client.window, - (xlib::CWX | xlib::CWY) as u32, - &mut wc, - ); + if client.size.0 < 1 || client.size.1 < 1 { + error!("client {:?} size is less than 1 pixel!", client); + } else { + unsafe { + xlib::XConfigureWindow( + self.dpy(), + client.window, + (xlib::CWWidth | xlib::CWHeight) as u32, + &mut wc, + ); - xlib::XSync(self.dpy(), 0); + xlib::XSync(self.dpy(), 0); + } } } @@ -278,15 +298,19 @@ impl XLib { stack_mode: 0, }; - unsafe { - xlib::XConfigureWindow( - self.dpy(), - client.window, - (xlib::CWWidth | xlib::CWHeight) as u32, - &mut wc, - ); + if client.size.0 < 1 || client.size.1 < 1 { + error!("client {:?} size is less than 1 pixel!", client); + } else { + unsafe { + xlib::XConfigureWindow( + self.dpy(), + client.window, + (xlib::CWX | xlib::CWY) as u32, + &mut wc, + ); - xlib::XSync(self.dpy(), 0); + xlib::XSync(self.dpy(), 0); + } } } @@ -352,9 +376,7 @@ impl XLib { ); } - for kb in self.global_keybinds.iter() { - self.grab_key_or_button(window, kb); - } + self.grab_global_keybinds(window); } pub fn dimensions(&self) -> (i32, i32) {