diff --git a/rustfmt.toml b/rustfmt.toml index 4b8f37a..385cc57 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,2 +1,3 @@ imports_granularity = "Crate" -wrap_comments = true \ No newline at end of file +wrap_comments = true +max_width = 80 \ No newline at end of file diff --git a/src/clients.rs b/src/clients.rs index 881c344..3c712e5 100644 --- a/src/clients.rs +++ b/src/clients.rs @@ -32,7 +32,11 @@ mod client { } impl Client { - pub fn new(window: Window, size: (i32, i32), position: (i32, i32)) -> Self { + pub fn new( + window: Window, + size: (i32, i32), + position: (i32, i32), + ) -> Self { Self { window, size, @@ -41,7 +45,11 @@ mod client { } } - pub fn new_transient(window: Window, size: (i32, i32), transient: Window) -> Self { + pub fn new_transient( + window: Window, + size: (i32, i32), + transient: Window, + ) -> Self { Self { window, size, @@ -300,13 +308,17 @@ impl ClientState { pub fn insert(&mut self, mut client: Client) -> Option<&Client> { let key = client.key(); - if client.is_transient() && self.contains(&client.transient_for.unwrap()) { + if client.is_transient() + && self.contains(&client.transient_for.unwrap()) + { let transient = self.get(&client.transient_for.unwrap()).unwrap(); client.position = { ( - transient.position.0 + (transient.size.0 - client.size.0) / 2, - transient.position.1 + (transient.size.1 - client.size.1) / 2, + transient.position.0 + + (transient.size.0 - client.size.0) / 2, + transient.position.1 + + (transient.size.1 - client.size.1) / 2, ) }; @@ -347,7 +359,8 @@ impl ClientState { { let key = key.key(); - self.clients.contains_key(&key) || self.floating_clients.contains_key(&key) + self.clients.contains_key(&key) + || self.floating_clients.contains_key(&key) } pub fn iter_floating(&self) -> impl Iterator { @@ -378,6 +391,18 @@ impl ClientState { .filter(move |&(k, _)| self.current_vs().contains(k)) } + pub fn iter_master_stack(&self) -> impl Iterator { + self.clients + .iter() + .filter(move |&(k, _)| self.current_vs().is_in_master(k)) + } + + pub fn iter_aux_stack(&self) -> impl Iterator { + self.clients + .iter() + .filter(move |&(k, _)| self.current_vs().is_in_aux(k)) + } + /// Returns reference to the current `VirtualScreen`. fn current_vs(&self) -> &VirtualScreen { // there is always at least one (1) virtual screen. @@ -510,31 +535,39 @@ impl ClientState { where K: ClientKey, { - self.virtual_screens - .iter() - .find_map(|vs| if vs.contains(key) { Some(vs) } else { None }) + self.virtual_screens.iter().find_map(|vs| { + if vs.contains(key) { + Some(vs) + } else { + None + } + }) } - fn get_mut_virtualscreen_for_client(&mut self, key: &K) -> Option<&mut VirtualScreen> + fn get_mut_virtualscreen_for_client( + &mut self, + key: &K, + ) -> Option<&mut VirtualScreen> where K: ClientKey, { - self.virtual_screens.iter_mut().find_map( - |vs| { - if vs.contains(key) { - Some(vs) - } else { - None - } - }, - ) + self.virtual_screens.iter_mut().find_map(|vs| { + if vs.contains(key) { + Some(vs) + } else { + None + } + }) } /** focuses client `key` if it contains `key` and returns a reference to the newly and the previously focused clients if any. */ - pub fn focus_client(&mut self, key: &K) -> (ClientEntry<&Client>, ClientEntry<&Client>) + pub fn focus_client( + &mut self, + key: &K, + ) -> (ClientEntry<&Client>, ClientEntry<&Client>) where K: ClientKey, { @@ -613,7 +646,12 @@ impl ClientState { screen width and screen height. Optionally adds a gap between windows `gap.unwrap_or(0)` pixels wide. */ - pub fn arrange_virtual_screen(&mut self, width: i32, height: i32, gap: Option) { + pub fn arrange_virtual_screen( + &mut self, + width: i32, + height: i32, + gap: Option, + ) { let gap = gap.unwrap_or(0); // should be fine to unwrap since we will always have at least 1 virtual screen @@ -687,6 +725,20 @@ impl VirtualScreen { self.master.contains(&key.key()) || self.aux.contains(&key.key()) } + fn is_in_master(&self, key: &K) -> bool + where + K: ClientKey, + { + self.master.contains(&key.key()) + } + + fn is_in_aux(&self, key: &K) -> bool + where + K: ClientKey, + { + self.aux.contains(&key.key()) + } + fn insert(&mut self, key: &K) where K: ClientKey, @@ -716,7 +768,8 @@ impl VirtualScreen { self.aux.extend(self.master.drain(index..=index)); } None => { - let index = self.aux.iter().position(|&k| k == key.key()).unwrap(); + let index = + self.aux.iter().position(|&k| k == key.key()).unwrap(); self.master.extend(self.aux.drain(index..=index)); } } diff --git a/src/main.rs b/src/main.rs index d06c1ab..2a56f87 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ use log4rs::{ encode::pattern::PatternEncoder, Config, }; +use state::WMConfig; mod clients; mod state; @@ -44,7 +45,7 @@ fn main() { log_prologue(); - state::WindowManager::new().init().run(); + state::WindowManager::new(WMConfig::default()).init().run(); } fn log_prologue() { diff --git a/src/state.rs b/src/state.rs index f6fe9bd..3c7a1b7 100644 --- a/src/state.rs +++ b/src/state.rs @@ -2,10 +2,14 @@ use std::rc::Rc; use log::{error, info}; -use x11::xlib::{self, ShiftMask, Window, XButtonEvent, XEvent, XKeyEvent, XMotionEvent}; +use x11::xlib::{ + self, Mod4Mask, ShiftMask, Window, XButtonEvent, XEvent, XKeyEvent, + XMotionEvent, +}; use xlib::{ - ButtonPressMask, ButtonReleaseMask, Mod1Mask, PointerMotionMask, XConfigureRequestEvent, - XCrossingEvent, XDestroyWindowEvent, XMapRequestEvent, XUnmapEvent, + ButtonPressMask, ButtonReleaseMask, PointerMotionMask, + XConfigureRequestEvent, XCrossingEvent, XDestroyWindowEvent, + XMapRequestEvent, XUnmapEvent, }; use crate::{ @@ -14,15 +18,28 @@ use crate::{ xlib::XLib, }; +/** +Contains static config data for the window manager, the sort of stuff you might want to +be able to configure in a config file. +*/ +pub struct WMConfig { + num_virtualscreens: usize, + mod_key: u32, + gap: Option, +} + pub struct WindowManager { clients: ClientState, move_window: Option, resize_window: Option, keybinds: Vec, xlib: XLib, + + last_rotation: Option, + config: WMConfig, } -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub enum Direction { Left, Right, @@ -40,8 +57,9 @@ struct KeyBinding { } impl WindowManager { - pub fn new() -> Self { - let clients = ClientState::with_virtualscreens(3); + pub fn new(config: WMConfig) -> Self { + let clients = + ClientState::with_virtualscreens(config.num_virtualscreens); let xlib = XLib::new(); Self { @@ -50,28 +68,30 @@ impl WindowManager { resize_window: None, keybinds: Vec::new(), xlib, + last_rotation: None, + config, } } pub fn init(mut self) -> Self { self.xlib.add_global_keybind(KeyOrButton::button( 1, - Mod1Mask, + self.config.mod_key, ButtonPressMask | ButtonReleaseMask | PointerMotionMask, )); self.xlib.add_global_keybind(KeyOrButton::button( 2, - Mod1Mask, + self.config.mod_key, ButtonPressMask | ButtonReleaseMask | PointerMotionMask, )); self.xlib.add_global_keybind(KeyOrButton::button( 3, - Mod1Mask, + self.config.mod_key, ButtonPressMask | ButtonReleaseMask | PointerMotionMask, )); self.add_keybind(KeyBinding::new( - self.xlib.make_key("P", Mod1Mask), + self.xlib.make_key("P", self.config.mod_key), |wm, _| { wm.spawn( "dmenu_run", @@ -94,37 +114,63 @@ impl WindowManager { )); self.add_keybind(KeyBinding::new( - self.xlib.make_key("M", Mod1Mask), + self.xlib.make_key("M", self.config.mod_key), Self::handle_switch_stack, )); self.add_keybind(KeyBinding::new( - self.xlib.make_key("Q", Mod1Mask), + self.xlib.make_key("Q", self.config.mod_key), Self::kill_client, )); self.add_keybind(KeyBinding::new( - self.xlib.make_key("Q", Mod1Mask | ShiftMask), + self.xlib.make_key("Q", self.config.mod_key | ShiftMask), |wm, _| wm.quit(), )); self.add_keybind(KeyBinding::new( - self.xlib.make_key("T", Mod1Mask), + self.xlib.make_key("T", self.config.mod_key), |wm, _| wm.spawn("alacritty", &[]), )); self.add_keybind(KeyBinding::new( - self.xlib.make_key("Return", Mod1Mask | ShiftMask), + self.xlib + .make_key("Return", self.config.mod_key | ShiftMask), |wm, _| wm.spawn("alacritty", &[]), )); self.add_keybind(KeyBinding::new( - self.xlib.make_key("Left", Mod1Mask), + self.xlib.make_key("Left", self.config.mod_key), |wm, _| wm.rotate_virtual_screen(Direction::Left), )); self.add_keybind(KeyBinding::new( - self.xlib.make_key("Right", Mod1Mask), + self.xlib.make_key("Right", self.config.mod_key), + |wm, _| wm.rotate_virtual_screen(Direction::Right), + )); + + self.add_keybind(KeyBinding::new( + self.xlib.make_key("Tab", self.config.mod_key), + |wm, _| wm.rotate_virtual_screen_back(), + )); + + self.add_keybind(KeyBinding::new( + self.xlib.make_key("J", self.config.mod_key), + |wm, _| wm.focus_master_stack(), + )); + + self.add_keybind(KeyBinding::new( + self.xlib.make_key("K", self.config.mod_key), + |wm, _| wm.focus_aux_stack(), + )); + + self.add_keybind(KeyBinding::new( + self.xlib.make_key("J", self.config.mod_key | ShiftMask), + |wm, _| wm.rotate_virtual_screen(Direction::Left), + )); + + self.add_keybind(KeyBinding::new( + self.xlib.make_key("K", self.config.mod_key | ShiftMask), |wm, _| wm.rotate_virtual_screen(Direction::Right), )); @@ -192,7 +238,9 @@ impl WindowManager { 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 event.button == 2 + && event.state & clean_mask == self.config.mod_key & clean_mask + { if self.clients.contains(&event.subwindow) { info!("toggleing floating for {:?}", event.subwindow); @@ -221,7 +269,8 @@ impl WindowManager { if self.move_window.is_none() && event.button == 1 - && event.state & clean_mask == Mod1Mask & clean_mask + && event.state & clean_mask + == self.config.mod_key & clean_mask && self.clients.contains(&event.subwindow) { // if client is tiled, set to floating @@ -257,7 +306,9 @@ impl WindowManager { move_window.cached_cursor_position = (event.x, event.y); - if let Some(client) = self.clients.get_mut(&move_window.key).into_option() { + if let Some(client) = + self.clients.get_mut(&move_window.key).into_option() + { let position = &mut client.position; position.0 += x; position.1 += y; @@ -279,7 +330,8 @@ impl WindowManager { if self.resize_window.is_none() && event.button == 3 - && event.state & clean_mask == Mod1Mask & clean_mask + && event.state & clean_mask + == self.config.mod_key & clean_mask && self.clients.contains(&event.subwindow) { // if client is tiled, set to floating @@ -329,7 +381,9 @@ impl WindowManager { resize_window.cached_cursor_position = (event.x, event.y); - if let Some(client) = self.clients.get_mut(&resize_window.key).into_option() { + if let Some(client) = + self.clients.get_mut(&resize_window.key).into_option() + { let size = &mut client.size; size.0 = std::cmp::max(1, size.0 + x); @@ -343,9 +397,17 @@ impl WindowManager { } } + fn rotate_virtual_screen_back(&mut self) { + if let Some(dir) = self.last_rotation { + self.rotate_virtual_screen(!dir); + } + } + fn rotate_virtual_screen(&mut self, dir: Direction) { info!("rotateing VS: {:?}", dir); + self.last_rotation = Some(dir); + match dir { Direction::Left => self.clients.rotate_left(), Direction::Right => self.clients.rotate_right(), @@ -354,13 +416,40 @@ impl WindowManager { self.arrange_clients(); // focus first client in all visible clients - let to_focus = self.clients.iter_visible().next().map(|(k, _)| k).cloned(); + let to_focus = + self.clients.iter_visible().next().map(|(k, _)| k).cloned(); if let Some(key) = to_focus { self.focus_client(&key); } } + fn focus_master_stack(&mut self) { + let k = self + .clients + .iter_master_stack() + .map(|(k, _)| k) + .next() + .cloned(); + + if let Some(k) = k { + self.focus_client(&k); + } + } + + fn focus_aux_stack(&mut self) { + let k = self + .clients + .iter_aux_stack() + .map(|(k, _)| k) + .next() + .cloned(); + + if let Some(k) = k { + self.focus_client(&k); + } + } + fn hide_hidden_clients(&self) { self.clients .iter_hidden() @@ -379,14 +468,15 @@ impl WindowManager { fn arrange_clients(&mut self) { let (width, height) = self.xlib.dimensions(); - self.clients.arrange_virtual_screen(width, height, Some(2)); - - self.hide_hidden_clients(); + self.clients + .arrange_virtual_screen(width, height, self.config.gap); self.clients .iter_visible() .for_each(|(_, c)| self.xlib.move_resize_client(c)); + self.hide_hidden_clients(); + self.raise_floating_clients(); } @@ -410,7 +500,9 @@ impl WindowManager { fn new_client(&mut self, window: Window) { info!("new client: {:?}", window); - let client = if let Some(transient_window) = self.xlib.get_transient_for_window(window) { + let client = if let Some(transient_window) = + self.xlib.get_transient_for_window(window) + { Client::new_transient( window, self.xlib.get_window_size(window).unwrap_or((100, 100)), @@ -502,3 +594,24 @@ impl KeyBinding { } } } + +impl Default for WMConfig { + fn default() -> Self { + Self { + num_virtualscreens: 5, + mod_key: Mod4Mask, + gap: Some(2), + } + } +} + +impl std::ops::Not for Direction { + type Output = Self; + + fn not(self) -> Self::Output { + match self { + Direction::Left => Direction::Right, + Direction::Right => Direction::Left, + } + } +} diff --git a/src/xlib.rs b/src/xlib.rs index 968c6a6..cd97a88 100644 --- a/src/xlib.rs +++ b/src/xlib.rs @@ -3,12 +3,14 @@ use std::ptr::{null, null_mut}; use std::{ffi::CString, rc::Rc}; use x11::xlib::{ - self, Atom, ButtonPressMask, ButtonReleaseMask, CWEventMask, ControlMask, CurrentTime, - EnterWindowMask, FocusChangeMask, LockMask, Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask, - PointerMotionMask, PropertyChangeMask, ShiftMask, StructureNotifyMask, SubstructureNotifyMask, - SubstructureRedirectMask, Window, XCloseDisplay, XConfigureRequestEvent, XDefaultScreen, - XEvent, XGetTransientForHint, XGrabPointer, XInternAtom, XKillClient, XMapWindow, XOpenDisplay, - XRaiseWindow, XRootWindow, XSetErrorHandler, XSync, XUngrabPointer, XWarpPointer, + self, Atom, ButtonPressMask, ButtonReleaseMask, CWEventMask, ControlMask, + CurrentTime, EnterWindowMask, FocusChangeMask, LockMask, Mod1Mask, + Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask, PointerMotionMask, + PropertyChangeMask, ShiftMask, StructureNotifyMask, SubstructureNotifyMask, + SubstructureRedirectMask, Window, XCloseDisplay, XConfigureRequestEvent, + XDefaultScreen, XEvent, XGetTransientForHint, XGrabPointer, XInternAtom, + XKillClient, XMapWindow, XOpenDisplay, XRaiseWindow, XRootWindow, + XSetErrorHandler, XSync, XUngrabPointer, XWarpPointer, }; use xlib::GrabModeAsync; @@ -76,7 +78,8 @@ impl XLib { pub fn init(&mut self) { unsafe { let mut window_attributes = - std::mem::MaybeUninit::::zeroed().assume_init(); + std::mem::MaybeUninit::::zeroed() + .assume_init(); window_attributes.event_mask = SubstructureRedirectMask | StructureNotifyMask @@ -92,7 +95,11 @@ impl XLib { &mut window_attributes, ); - xlib::XSelectInput(self.dpy(), self.root, window_attributes.event_mask); + xlib::XSelectInput( + self.dpy(), + self.root, + window_attributes.event_mask, + ); XSetErrorHandler(Some(xlib_error_handler)); } @@ -123,9 +130,12 @@ impl XLib { pub fn squash_event(&self, event_type: i32) -> XEvent { unsafe { - let mut event = std::mem::MaybeUninit::::zeroed().assume_init(); + let mut event = + std::mem::MaybeUninit::::zeroed().assume_init(); - while xlib::XCheckTypedEvent(self.dpy(), event_type, &mut event) != 0 {} + while xlib::XCheckTypedEvent(self.dpy(), event_type, &mut event) + != 0 + {} event } @@ -133,7 +143,8 @@ impl XLib { pub fn next_event(&self) -> XEvent { unsafe { - let mut event = std::mem::MaybeUninit::::zeroed().assume_init(); + let mut event = + std::mem::MaybeUninit::::zeroed().assume_init(); xlib::XNextEvent(self.dpy(), &mut event); event @@ -142,7 +153,8 @@ impl XLib { pub fn grab_key_or_button(&self, window: Window, key: &KeyOrButton) { let numlock_mask = self.get_numlock_mask(); - let modifiers = vec![0, LockMask, numlock_mask, LockMask | numlock_mask]; + let modifiers = + vec![0, LockMask, numlock_mask, LockMask | numlock_mask]; for modifier in modifiers.iter() { match key { @@ -215,7 +227,11 @@ impl XLib { xlib::CurrentTime, ); - xlib::XDeleteProperty(self.dpy(), self.root, self.atoms.active_window); + xlib::XDeleteProperty( + self.dpy(), + self.root, + self.atoms.active_window, + ); } } @@ -237,7 +253,8 @@ impl XLib { xlib::XConfigureWindow( self.dpy(), client.window, - (xlib::CWY | xlib::CWX | xlib::CWHeight | xlib::CWWidth) as u32, + (xlib::CWY | xlib::CWX | xlib::CWHeight | xlib::CWWidth) + as u32, &mut windowchanges, ); @@ -338,10 +355,14 @@ impl XLib { } pub fn get_window_size(&self, window: Window) -> Option<(i32, i32)> { - let mut wa = - unsafe { std::mem::MaybeUninit::::zeroed().assume_init() }; + let mut wa = unsafe { + std::mem::MaybeUninit::::zeroed() + .assume_init() + }; - if unsafe { xlib::XGetWindowAttributes(self.dpy(), window, &mut wa) != 0 } { + if unsafe { + xlib::XGetWindowAttributes(self.dpy(), window, &mut wa) != 0 + } { Some((wa.width, wa.height)) } else { None @@ -351,7 +372,9 @@ impl XLib { pub fn get_transient_for_window(&self, window: Window) -> Option { let mut transient_for: Window = 0; - if unsafe { XGetTransientForHint(self.dpy(), window, &mut transient_for) != 0 } { + if unsafe { + XGetTransientForHint(self.dpy(), window, &mut transient_for) != 0 + } { Some(transient_for) } else { None @@ -370,7 +393,12 @@ impl XLib { }; unsafe { - xlib::XConfigureWindow(self.dpy(), event.window, event.value_mask as u32, &mut wc); + xlib::XConfigureWindow( + self.dpy(), + event.window, + event.value_mask as u32, + &mut wc, + ); } } @@ -409,7 +437,10 @@ impl XLib { xlib::XSelectInput( self.dpy(), window, - EnterWindowMask | FocusChangeMask | PropertyChangeMask | StructureNotifyMask, + EnterWindowMask + | FocusChangeMask + | PropertyChangeMask + | StructureNotifyMask, ); } @@ -445,7 +476,8 @@ impl XLib { self.dpy(), self.root, 0, - (ButtonPressMask | ButtonReleaseMask | PointerMotionMask) as u32, + (ButtonPressMask | ButtonReleaseMask | PointerMotionMask) + as u32, GrabModeAsync, GrabModeAsync, 0, @@ -463,7 +495,17 @@ impl XLib { pub fn move_cursor(&self, window: Window, position: (i32, i32)) { unsafe { - XWarpPointer(self.dpy(), 0, window, 0, 0, 0, 0, position.0, position.1); + XWarpPointer( + self.dpy(), + 0, + window, + 0, + 0, + 0, + 0, + position.0, + position.1, + ); } } @@ -472,7 +514,13 @@ impl XLib { let mut num_protos: i32 = 0; unsafe { - if xlib::XGetWMProtocols(self.dpy(), client.window, &mut protos, &mut num_protos) != 0 { + if xlib::XGetWMProtocols( + self.dpy(), + client.window, + &mut protos, + &mut num_protos, + ) != 0 + { for i in 0..num_protos { if *protos.offset(i as isize) == proto { return true; @@ -503,7 +551,13 @@ impl XLib { }; unsafe { - xlib::XSendEvent(self.dpy(), client.window, 0, xlib::NoEventMask, &mut event); + xlib::XSendEvent( + self.dpy(), + client.window, + 0, + xlib::NoEventMask, + &mut event, + ); } true @@ -543,7 +597,10 @@ impl XLib { if *(*modmap) .modifiermap .offset((i * max_keypermod + j) as isize) - == xlib::XKeysymToKeycode(self.dpy(), x11::keysym::XK_Num_Lock as u64) + == xlib::XKeysymToKeycode( + self.dpy(), + x11::keysym::XK_Num_Lock as u64, + ) { return 1 << i; } @@ -556,7 +613,13 @@ impl XLib { pub fn get_clean_mask(&self) -> u32 { !(self.get_numlock_mask() | LockMask) - & (ShiftMask | ControlMask | Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) + & (ShiftMask + | ControlMask + | Mod1Mask + | Mod2Mask + | Mod3Mask + | Mod4Mask + | Mod5Mask) } }