From 984f0763c779a7816dbb15b338bfed1768ece0b4 Mon Sep 17 00:00:00 2001 From: NoOneBtw Date: Tue, 4 May 2021 23:13:37 +0200 Subject: [PATCH] vim-like movement between and within stacks (`S-[h,j,k,l]`) --- src/clients.rs | 90 ++++++++++++++++++++++++------- src/state.rs | 143 ++++++++++++++++++++++++++++++++++++++++++------- src/xlib.rs | 46 +++++++++------- 3 files changed, 222 insertions(+), 57 deletions(-) diff --git a/src/clients.rs b/src/clients.rs index ed90371..25493a7 100644 --- a/src/clients.rs +++ b/src/clients.rs @@ -1,7 +1,5 @@ -#![allow(dead_code)] - use std::num::NonZeroI32; -use std::{collections::HashMap, ops::Rem, usize}; +use std::{ops::Rem, usize}; use indexmap::IndexMap; use log::{error, info}; @@ -33,6 +31,7 @@ mod client { } impl Client { + #[allow(dead_code)] pub fn new( window: Window, size: (i32, i32), @@ -279,8 +278,6 @@ impl ClientState { } } - self.focus_client(&key); - // adding a client changes the liling layout, rearrange self.arrange_virtual_screen(); @@ -339,6 +336,19 @@ impl ClientState { .filter(move |&(k, _)| self.is_client_visible(k)) } + #[allow(dead_code)] + pub fn iter_visible_ordered( + &self, + ) -> impl Iterator { + self.iter_master_stack() + .chain( + self.iter_floating() + .filter(move |&(k, _)| self.is_client_visible(k)), + ) + .chain(self.iter_aux_stack()) + } + + #[allow(dead_code)] pub fn iter_current_screen(&self) -> impl Iterator { self.clients .iter() @@ -346,15 +356,17 @@ impl ClientState { } pub fn iter_master_stack(&self) -> impl Iterator { - self.clients + self.current_vs() + .master .iter() - .filter(move |&(k, _)| self.current_vs().is_in_master(k)) + .map(move |k| (k, self.get(k).unwrap())) } pub fn iter_aux_stack(&self) -> impl Iterator { - self.clients + self.current_vs() + .aux .iter() - .filter(move |&(k, _)| self.current_vs().is_in_aux(k)) + .map(move |k| (k, self.get(k).unwrap())) } /// Returns reference to the current `VirtualScreen`. @@ -414,16 +426,16 @@ impl ClientState { } } - pub fn rotate_right(&mut self, n: Option) { + pub fn rotate_right(&mut self, n: usize) { self.virtual_screens - .rotate_right(n.unwrap_or(1).rem(self.virtual_screens.len())); + .rotate_right(n.rem(self.virtual_screens.len())); self.arrange_virtual_screen(); } - pub fn rotate_left(&mut self, n: Option) { + pub fn rotate_left(&mut self, n: usize) { self.virtual_screens - .rotate_left(n.unwrap_or(1).rem(self.virtual_screens.len())); + .rotate_left(n.rem(self.virtual_screens.len())); self.arrange_virtual_screen(); } @@ -529,6 +541,23 @@ impl ClientState { }) } + pub fn get_stack_for_client(&self, key: &K) -> Option<&Vec> + where + K: ClientKey, + { + if let Some(vs) = self.get_virtualscreen_for_client(key) { + if vs.is_in_aux(key) { + Some(&vs.aux) + } else if vs.is_in_master(key) { + Some(&vs.master) + } else { + None + } + } else { + None + } + } + /** focuses client `key` if it contains `key` and returns a reference to the newly and the previously focused clients if any. @@ -540,11 +569,29 @@ impl ClientState { where K: ClientKey, { + let (new, old) = self.focus_client_inner(key); + + info!("Swapping focus: new({:?}) old({:?})", new, old); + + (new, old) + } + + fn focus_client_inner( + &mut self, + key: &K, + ) -> (ClientEntry<&Client>, ClientEntry<&Client>) + where + K: ClientKey, + { + // if `key` is not a valid entry into the client list, do nothing if self.contains(key) { + // check if we currently have a client focused match self.focused { Some(focused) => { - // If we are trying to focus the focused client, do nothing if focused == key.key() { + // if `key` is a reference to the focused client + // dont bother unfocusing and refocusing the same client + (ClientEntry::Vacant, ClientEntry::Vacant) } else { // focus the new client and return reference to it @@ -554,17 +601,18 @@ impl ClientState { (self.get(key), self.get(&focused)) } } - /* - not currently focusing any client - focus the new client and return reference to it. - */ + None => { + // not currently focusing any client + // just focus and return the client `key` references + self.focused = Some(key.key()); (self.get(key), ClientEntry::Vacant) } } } else { - // key is not a reference to a valid client + // nothing happened, return nothing + (ClientEntry::Vacant, ClientEntry::Vacant) } } @@ -573,6 +621,7 @@ impl ClientState { sets `self.focused` to `None` and returns a reference to the previously focused Client if any. */ + #[allow(dead_code)] pub fn unfocus(&mut self) -> ClientEntry<&Client> { match self.focused { Some(focused) => { @@ -583,6 +632,7 @@ impl ClientState { } } + #[allow(dead_code)] pub fn is_focused(&self, key: &K) -> bool where K: ClientKey, @@ -773,6 +823,7 @@ impl ClientEntry { } } + #[allow(dead_code)] pub fn is_floating(&self) -> bool { match self { ClientEntry::Floating(_) => true, @@ -787,6 +838,7 @@ impl ClientEntry { } } + #[allow(dead_code)] pub fn is_occupied(&self) -> bool { !self.is_vacant() } diff --git a/src/state.rs b/src/state.rs index 24194ca..7a24a81 100644 --- a/src/state.rs +++ b/src/state.rs @@ -40,8 +40,10 @@ pub struct WindowManager { #[derive(Debug, Clone, Copy)] pub enum Direction { - Left(Option), - Right(Option), + West(usize), + East(usize), + North(usize), + South(usize), } enum MoveResizeInfo { @@ -156,12 +158,12 @@ impl WindowManager { self.add_keybind(KeyBinding::new( self.xlib.make_key("Left", self.config.mod_key), - |wm, _| wm.rotate_virtual_screen(Direction::Left(None)), + |wm, _| wm.rotate_virtual_screen(Direction::west()), )); self.add_keybind(KeyBinding::new( self.xlib.make_key("Right", self.config.mod_key), - |wm, _| wm.rotate_virtual_screen(Direction::Right(None)), + |wm, _| wm.rotate_virtual_screen(Direction::east()), )); self.add_keybind(KeyBinding::new( @@ -171,22 +173,32 @@ impl WindowManager { self.add_keybind(KeyBinding::new( self.xlib.make_key("J", self.config.mod_key), - |wm, _| wm.focus_master_stack(), + |wm, _| wm.move_focus(Direction::south()), )); self.add_keybind(KeyBinding::new( self.xlib.make_key("K", self.config.mod_key), - |wm, _| wm.focus_aux_stack(), + |wm, _| wm.move_focus(Direction::north()), + )); + + self.add_keybind(KeyBinding::new( + self.xlib.make_key("H", self.config.mod_key), + |wm, _| wm.move_focus(Direction::west()), + )); + + self.add_keybind(KeyBinding::new( + self.xlib.make_key("L", self.config.mod_key), + |wm, _| wm.move_focus(Direction::east()), )); self.add_keybind(KeyBinding::new( self.xlib.make_key("J", self.config.mod_key | ShiftMask), - |wm, _| wm.rotate_virtual_screen(Direction::Left(None)), + |wm, _| wm.rotate_virtual_screen(Direction::west()), )); self.add_keybind(KeyBinding::new( self.xlib.make_key("K", self.config.mod_key | ShiftMask), - |wm, _| wm.rotate_virtual_screen(Direction::Right(None)), + |wm, _| wm.rotate_virtual_screen(Direction::east()), )); self.xlib.init(); @@ -226,8 +238,8 @@ impl WindowManager { std::process::exit(0); } - fn kill_client(&mut self, event: &XKeyEvent) { - if let Some(client) = self.clients.get(&event.subwindow).into_option() { + fn kill_client(&mut self, _event: &XKeyEvent) { + if let Some(client) = self.clients.get_focused().into_option() { self.xlib.kill_client(client); } } @@ -246,10 +258,13 @@ impl WindowManager { } } - fn handle_switch_stack(&mut self, event: &XKeyEvent) { - info!("Switching stack for window{:?}", event.subwindow); - - self.clients.switch_stack_for_client(&event.subwindow); + fn handle_switch_stack(&mut self, _event: &XKeyEvent) { + if let Some(client) = + self.clients.get_focused().into_option().map(|c| c.key()) + { + info!("Switching stack for window {:?}", client); + self.clients.switch_stack_for_client(&client); + } self.arrange_clients(); } @@ -266,12 +281,16 @@ impl WindowManager { self.last_rotation = Some(dir); match dir { - Direction::Left(n) => self.clients.rotate_left(n), - Direction::Right(n) => self.clients.rotate_right(n), + Direction::West(n) => self.clients.rotate_left(n), + Direction::East(n) => self.clients.rotate_right(n), + _ => {} } self.arrange_clients(); + self.focus_any(); + } + fn focus_any(&mut self) { // focus first client in all visible clients let to_focus = self.clients.iter_visible().next().map(|(k, _)| k).cloned(); @@ -282,10 +301,14 @@ impl WindowManager { } fn focus_master_stack(&mut self) { + let focused = self.clients.get_focused().into_option().map(|c| c.key()); + let k = self .clients .iter_master_stack() .map(|(k, _)| k) + // get the first client on the stack thats not already focused + .filter(|&&k| focused.map(|f| f != k).unwrap_or(true)) .next() .cloned(); @@ -295,10 +318,14 @@ impl WindowManager { } fn focus_aux_stack(&mut self) { + let focused = self.clients.get_focused().into_option().map(|c| c.key()); + let k = self .clients .iter_aux_stack() .map(|(k, _)| k) + // get the first client on the stack thats not already focused + .filter(|&&k| focused.map(|f| f != k).unwrap_or(true)) .next() .cloned(); @@ -307,6 +334,58 @@ impl WindowManager { } } + fn focus_up(&mut self) { + let focused = self.clients.get_focused().into_option().map(|c| c.key()); + + let k = focused.and_then(|focused| { + self.clients + .get_stack_for_client(&focused) + .and_then(|stack| { + stack + .iter() + .rev() + .skip_while(|&&k| k != focused) + .skip(1) + .next() + .cloned() + }) + }); + + if let Some(k) = k { + self.focus_client(&k, false); + } + } + + fn focus_down(&mut self) { + let focused = self.clients.get_focused().into_option().map(|c| c.key()); + + let k = focused.and_then(|focused| { + self.clients + .get_stack_for_client(&focused) + .and_then(|stack| { + stack + .iter() + .skip_while(|&&k| k != focused) + .skip(1) + .next() + .cloned() + }) + }); + + if let Some(k) = k { + self.focus_client(&k, false); + } + } + + fn move_focus(&mut self, dir: Direction) { + match dir { + Direction::East(_) => self.focus_aux_stack(), + Direction::West(_) => self.focus_master_stack(), + Direction::North(_) => self.focus_up(), + Direction::South(_) => self.focus_down(), + } + } + fn hide_hidden_clients(&self) { self.clients .iter_hidden() @@ -331,6 +410,10 @@ impl WindowManager { self.hide_hidden_clients(); self.raise_floating_clients(); + + if self.clients.get_focused().is_vacant() { + self.focus_any(); + } } fn focus_client(&mut self, key: &K, try_raise: bool) @@ -373,11 +456,11 @@ impl WindowManager { }; self.clients.insert(client).unwrap(); + self.arrange_clients(); + self.xlib.map_window(window); self.focus_client(&window, true); - - self.arrange_clients(); } fn map_request(&mut self, event: &XEvent) { @@ -585,13 +668,33 @@ impl Default for WMConfig { } } +impl Direction { + fn west() -> Self { + Direction::West(1) + } + + fn east() -> Self { + Direction::East(1) + } + + fn north() -> Self { + Direction::North(1) + } + + fn south() -> Self { + Direction::South(1) + } +} + impl std::ops::Not for Direction { type Output = Self; fn not(self) -> Self::Output { match self { - Direction::Left(n) => Direction::Right(n), - Direction::Right(n) => Direction::Left(n), + Direction::West(n) => Direction::East(n), + Direction::East(n) => Direction::West(n), + Direction::North(n) => Direction::North(n), + Direction::South(n) => Direction::South(n), } } } diff --git a/src/xlib.rs b/src/xlib.rs index 131170b..65c3928 100644 --- a/src/xlib.rs +++ b/src/xlib.rs @@ -3,14 +3,15 @@ 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, AnyButton, AnyKey, AnyModifier, 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, + XUngrabButton, XUngrabKey, XUngrabPointer, XWarpPointer, }; use xlib::GrabModeAsync; @@ -21,7 +22,7 @@ use crate::clients::Client; pub struct XLib { display: Display, root: Window, - screen: i32, + _screen: i32, atoms: Atoms, global_keybinds: Vec, } @@ -54,7 +55,7 @@ pub struct Display(Rc<*mut xlib::Display>); impl XLib { pub fn new() -> Self { - let (display, screen, root) = unsafe { + let (display, _screen, root) = unsafe { let display = XOpenDisplay(null()); assert_ne!(display, null_mut()); @@ -70,7 +71,7 @@ impl XLib { atoms: Atoms::init(display.clone()), global_keybinds: Vec::new(), root, - screen, + _screen, display, } } @@ -113,6 +114,14 @@ impl XLib { self.global_keybinds.push(key); } + #[allow(dead_code)] + fn ungrab_global_keybings(&self, window: Window) { + unsafe { + XUngrabButton(self.dpy(), AnyButton as u32, AnyModifier, window); + XUngrabKey(self.dpy(), AnyKey, AnyModifier, window); + } + } + fn grab_global_keybinds(&self, window: Window) { for kb in self.global_keybinds.iter() { self.grab_key_or_button(window, kb); @@ -218,21 +227,22 @@ impl XLib { self.send_event(client, self.atoms.take_focus); } - pub fn unfocus_client(&self, client: &Client) { + pub fn unfocus_client(&self, _client: &Client) { //info!("unfocusing client: {:?}", client); + unsafe { xlib::XSetInputFocus( self.dpy(), - client.window, + self.root, xlib::RevertToPointerRoot, xlib::CurrentTime, ); - xlib::XDeleteProperty( - self.dpy(), - self.root, - self.atoms.active_window, - ); + // xlib::XDeleteProperty( + // self.dpy(), + // self.root, + // self.atoms.active_window, + // ); } }