vim-like movement between and within stacks (S-[h,j,k,l])

This commit is contained in:
NoOneBtw 2021-05-04 23:13:37 +02:00
parent f7189c72b2
commit 984f0763c7
3 changed files with 222 additions and 57 deletions

View file

@ -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<Item = (&u64, &Client)> {
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<Item = (&u64, &Client)> {
self.clients
.iter()
@ -346,15 +356,17 @@ impl ClientState {
}
pub fn iter_master_stack(&self) -> impl Iterator<Item = (&u64, &Client)> {
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<Item = (&u64, &Client)> {
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<usize>) {
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<usize>) {
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<K>(&self, key: &K) -> Option<&Vec<u64>>
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<K>(
&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<K>(&self, key: &K) -> bool
where
K: ClientKey,
@ -773,6 +823,7 @@ impl<T> ClientEntry<T> {
}
}
#[allow(dead_code)]
pub fn is_floating(&self) -> bool {
match self {
ClientEntry::Floating(_) => true,
@ -787,6 +838,7 @@ impl<T> ClientEntry<T> {
}
}
#[allow(dead_code)]
pub fn is_occupied(&self) -> bool {
!self.is_vacant()
}

View file

@ -40,8 +40,10 @@ pub struct WindowManager {
#[derive(Debug, Clone, Copy)]
pub enum Direction {
Left(Option<usize>),
Right(Option<usize>),
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<K>(&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),
}
}
}

View file

@ -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<KeyOrButton>,
}
@ -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,
// );
}
}