vim-like movement between and within stacks (S-[h,j,k,l]
)
This commit is contained in:
parent
f7189c72b2
commit
984f0763c7
|
@ -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()
|
||||
}
|
||||
|
|
143
src/state.rs
143
src/state.rs
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
46
src/xlib.rs
46
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<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,
|
||||
// );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue