wzrd

An ICCCM & EWMH compliant X11 reparenting, dynamic window manager, written in Rust
git clone git://git.deurzen.net/wzrd
Log | Files | Refs | LICENSE

commit fb0dff8589d4206fcddf029ecbf8a9f8c76a7106
parent bb8f9a10811797b750b6e384a933dab052e31d7c
Author: deurzen <m.deurzen@tum.de>
Date:   Sun, 28 Mar 2021 07:49:54 +0200

adds workspace and fullscreen rule parsing

Diffstat:
Msrc/core/binding.rs | 6+++---
Msrc/core/change.rs | 31+++++++++++++++++++++++++++++++
Msrc/core/client.rs | 277++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Msrc/core/cycle.rs | 32++++++++++++++++----------------
Msrc/core/decoration.rs | 16++++++++--------
Msrc/core/identify.rs | 8--------
Msrc/core/layout.rs | 7++++---
Msrc/core/macros.rs | 25++++++++++++++++---------
Msrc/core/main.rs | 94+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Msrc/core/model.rs | 3267+++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/core/partition.rs | 18++++++++++++++----
Msrc/core/rule.rs | 44+++++++++++---------------------------------
Msrc/core/util.rs | 27+++++++++++++++++++++++----
Msrc/core/workspace.rs | 232++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Msrc/core/zone.rs | 6+++---
Msrc/winsys/geometry.rs | 24++++++++++++++++++------
Msrc/winsys/screen.rs | 186++++++++++++++++++++++++++++++++++++++++++-------------------------------------
17 files changed, 2255 insertions(+), 2045 deletions(-)

diff --git a/src/core/binding.rs b/src/core/binding.rs @@ -7,8 +7,8 @@ use winsys::window::Window; use std::collections::HashMap; -pub type Action = Box<dyn FnMut(&mut Model)>; -pub type MouseEvents = Box<dyn FnMut(&mut Model, Option<Window>)>; -pub type KeyEvents = Box<dyn FnMut(&mut Model)>; +pub type Action = Box<dyn FnMut(&mut Model<'_>)>; +pub type MouseEvents = Box<dyn FnMut(&mut Model<'_>, Option<Window>)>; +pub type KeyEvents = Box<dyn FnMut(&mut Model<'_>)>; pub type KeyBindings = HashMap<KeyCode, KeyEvents>; pub type MouseBindings = HashMap<(MouseEventKey, MouseShortcut), (MouseEvents, bool)>; diff --git a/src/core/change.rs b/src/core/change.rs @@ -7,6 +7,37 @@ use std::ops::Mul; use std::ops::Sub; #[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum Toggle { + On, + Off, + Reverse, +} + +impl From<bool> for Toggle { + #[inline(always)] + fn from(toggle: bool) -> Self { + match toggle { + true => Toggle::On, + false => Toggle::Off, + } + } +} + +impl Toggle { + #[inline(always)] + pub fn eval( + self, + current: bool, + ) -> bool { + match self { + Toggle::On => true, + Toggle::Off => false, + Toggle::Reverse => !current, + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Direction { Forward, Backward, diff --git a/src/core/client.rs b/src/core/client.rs @@ -1,4 +1,6 @@ +use crate::change::Toggle; use crate::compare::MatchMethod; +use crate::decoration::Color; use crate::decoration::Decoration; use crate::identify::Ident; use crate::identify::Identify; @@ -17,6 +19,33 @@ use std::cell::Cell; use std::cell::RefCell; use std::time::SystemTime; +#[derive(Clone, Copy, Debug)] +pub enum OutsideState { + Focused, + FocusedDisowned, + FocusedSticky, + Unfocused, + UnfocusedDisowned, + UnfocusedSticky, + Urgent, +} + +impl std::ops::Not for OutsideState { + type Output = Self; + + fn not(self) -> Self::Output { + match self { + Self::Focused => Self::Unfocused, + Self::FocusedDisowned => Self::UnfocusedDisowned, + Self::FocusedSticky => Self::UnfocusedSticky, + Self::Unfocused => Self::Focused, + Self::UnfocusedDisowned => Self::FocusedDisowned, + Self::UnfocusedSticky => Self::FocusedSticky, + other => other, + } + } +} + pub struct Client { zone: ZoneId, window: Window, @@ -43,16 +72,17 @@ pub struct Client { focused: Cell<bool>, mapped: Cell<bool>, managed: Cell<bool>, - in_window: Cell<bool>, + urgent: Cell<bool>, floating: Cell<bool>, fullscreen: Cell<bool>, + contained: Cell<bool>, + invincible: Cell<bool>, + sticky: Cell<bool>, iconified: Cell<bool>, disowned: Cell<bool>, - sticky: Cell<bool>, - invincible: Cell<bool>, - urgent: Cell<bool>, consuming: Cell<bool>, producing: Cell<bool>, + outside_state: Cell<OutsideState>, pid: Option<Pid>, ppid: Option<Pid>, last_focused: Cell<SystemTime>, @@ -60,9 +90,17 @@ pub struct Client { expected_unmap_count: Cell<u8>, } +impl Identify for Window { + #[inline(always)] + fn id(&self) -> Ident { + *self + } +} + impl Identify for Client { + #[inline(always)] fn id(&self) -> Ident { - self.window as Ident + self.window } } @@ -104,16 +142,17 @@ impl Client { focused: Cell::new(false), mapped: Cell::new(false), managed: Cell::new(true), - in_window: Cell::new(false), + urgent: Cell::new(false), floating: Cell::new(false), fullscreen: Cell::new(false), + contained: Cell::new(false), + invincible: Cell::new(false), iconified: Cell::new(false), - disowned: Cell::new(false), sticky: Cell::new(false), - invincible: Cell::new(false), - urgent: Cell::new(false), + disowned: Cell::new(false), consuming: Cell::new(false), producing: Cell::new(true), + outside_state: Cell::new(OutsideState::Unfocused), pid, ppid, last_focused: Cell::new(SystemTime::now()), @@ -219,7 +258,7 @@ impl Client { &self, context: usize, ) { - self.context.replace(context); + self.context.set(context); } #[inline] @@ -232,7 +271,7 @@ impl Client { &self, workspace: usize, ) { - self.workspace.replace(workspace); + self.workspace.set(workspace); } #[inline] @@ -251,8 +290,8 @@ impl Client { active_region: Region, ) { self.set_inner_region(active_region); - let active_region = self.active_region.replace(active_region); - self.previous_region.replace(active_region); + self.previous_region + .set(self.active_region.replace(active_region)); } #[inline] @@ -271,7 +310,7 @@ impl Client { active_region: Region, ) { self.inner_region - .replace(if let Some(frame) = self.decoration.get().frame { + .set(if let Some(frame) = self.decoration.get().frame { let mut inner_region = active_region - frame.extents; inner_region.pos.x = frame.extents.left; @@ -298,11 +337,11 @@ impl Client { ) { match region { PlacementClass::Free(region) => { - self.free_region.replace(region); + self.free_region.set(region); self.set_active_region(region); }, PlacementClass::Tile(region) => { - self.tile_region.replace(region); + self.tile_region.set(region); self.set_active_region(region); }, } @@ -328,7 +367,7 @@ impl Client { &self, decoration: Decoration, ) { - self.decoration.replace(decoration); + self.decoration.set(decoration); } #[inline] @@ -336,6 +375,57 @@ impl Client { self.decoration.get().to_owned() } + #[inline(always)] + pub fn decoration_colors(&self) -> (Option<(u32, Color)>, Option<Color>) { + let outside_state = self.outside_state(); + let decoration = self.decoration.get(); + + match outside_state { + OutsideState::Focused => ( + decoration + .border + .map(|border| (border.width, border.colors.focused)), + decoration.frame.map(|frame| frame.colors.focused), + ), + OutsideState::FocusedDisowned => ( + decoration + .border + .map(|border| (border.width, border.colors.fdisowned)), + decoration.frame.map(|frame| frame.colors.fdisowned), + ), + OutsideState::FocusedSticky => ( + decoration + .border + .map(|border| (border.width, border.colors.fsticky)), + decoration.frame.map(|frame| frame.colors.fsticky), + ), + OutsideState::Unfocused => ( + decoration + .border + .map(|border| (border.width, border.colors.unfocused)), + decoration.frame.map(|frame| frame.colors.unfocused), + ), + OutsideState::UnfocusedDisowned => ( + decoration + .border + .map(|border| (border.width, border.colors.udisowned)), + decoration.frame.map(|frame| frame.colors.udisowned), + ), + OutsideState::UnfocusedSticky => ( + decoration + .border + .map(|border| (border.width, border.colors.usticky)), + decoration.frame.map(|frame| frame.colors.usticky), + ), + OutsideState::Urgent => ( + decoration + .border + .map(|border| (border.width, border.colors.urgent)), + decoration.frame.map(|frame| frame.colors.urgent), + ), + } + } + #[inline] pub fn frame_extents(&self) -> Extents { Extents { @@ -364,12 +454,12 @@ impl Client { &self, pointer_pos: Pos, ) { - self.warp_pos.replace(Some(pointer_pos)); + self.warp_pos.set(Some(pointer_pos)); } #[inline] pub fn unset_warp_pos(&self) { - self.warp_pos.replace(None); + self.warp_pos.set(None); } #[inline] @@ -465,16 +555,19 @@ impl Client { } #[inline] - pub fn is_free(&self) -> bool { - self.floating.get() || self.disowned.get() || !self.managed.get() + pub fn is_consuming(&self) -> bool { + self.producer.get().is_some() } #[inline] pub fn set_focused( &self, - focused: bool, + toggle: Toggle, ) { - self.focused.set(focused); + if Toggle::from(self.focused.get()) != toggle { + self.focused.set(toggle.eval(self.focused.get())); + self.outside_state.set(!self.outside_state.get()); + } } #[inline] @@ -485,9 +578,9 @@ impl Client { #[inline] pub fn set_mapped( &self, - mapped: bool, + toggle: Toggle, ) { - self.mapped.set(mapped); + self.mapped.set(toggle.eval(self.mapped.get())); } #[inline] @@ -498,9 +591,9 @@ impl Client { #[inline] pub fn set_managed( &self, - managed: bool, + toggle: Toggle, ) { - self.managed.set(managed); + self.managed.set(toggle.eval(self.managed.get())); } #[inline] @@ -509,24 +602,36 @@ impl Client { } #[inline] - pub fn set_in_window( + pub fn set_urgent( &self, - in_window: bool, + toggle: Toggle, ) { - self.in_window.set(in_window); + let urgent = toggle.eval(self.urgent.get()); + self.urgent.set(urgent); + + if urgent { + self.outside_state.set(OutsideState::Urgent); + } } #[inline] - pub fn is_in_window(&self) -> bool { - self.in_window.get() + pub fn is_urgent(&self) -> bool { + self.urgent.get() + } + + #[inline] + pub fn is_free(&self) -> bool { + self.floating.get() && (!self.fullscreen.get() || self.contained.get()) + || self.disowned.get() + || !self.managed.get() } #[inline] pub fn set_floating( &self, - floating: bool, + toggle: Toggle, ) { - self.floating.replace(floating); + self.floating.set(toggle.eval(self.floating.get())); } #[inline] @@ -537,9 +642,9 @@ impl Client { #[inline] pub fn set_fullscreen( &self, - fullscreen: bool, + toggle: Toggle, ) { - self.fullscreen.replace(fullscreen); + self.fullscreen.set(toggle.eval(self.fullscreen.get())); } #[inline] @@ -548,94 +653,108 @@ impl Client { } #[inline] - pub fn set_iconified( + pub fn set_contained( &self, - iconified: bool, + toggle: Toggle, ) { - self.iconified.replace(iconified); + self.contained.set(toggle.eval(self.contained.get())); } #[inline] - pub fn is_iconified(&self) -> bool { - self.iconified.get() + pub fn is_contained(&self) -> bool { + self.contained.get() } #[inline] - pub fn set_disowned( + pub fn set_invincible( &self, - disowned: bool, + toggle: Toggle, ) { - self.disowned.replace(disowned); + self.invincible.set(toggle.eval(self.invincible.get())); } #[inline] - pub fn is_disowned(&self) -> bool { - self.disowned.get() + pub fn is_invincible(&self) -> bool { + self.invincible.get() } #[inline] - pub fn set_sticky( + pub fn set_producing( &self, - sticky: bool, + toggle: Toggle, ) { - self.sticky.replace(sticky); + self.producing.set(toggle.eval(self.producing.get())); } #[inline] - pub fn is_sticky(&self) -> bool { - self.sticky.get() + pub fn is_producing(&self) -> bool { + self.producing.get() } #[inline] - pub fn set_invincible( + pub fn set_iconified( &self, - invincible: bool, + toggle: Toggle, ) { - self.invincible.replace(invincible); + self.iconified.set(toggle.eval(self.iconified.get())); } #[inline] - pub fn is_invincible(&self) -> bool { - self.invincible.get() + pub fn is_iconified(&self) -> bool { + self.iconified.get() } #[inline] - pub fn set_urgent( + pub fn set_sticky( &self, - urgent: bool, + toggle: Toggle, ) { - self.urgent.replace(urgent); + let sticky = toggle.eval(self.sticky.get()); + self.sticky.set(sticky); + + self.outside_state.set(match self.outside_state.get() { + OutsideState::Focused if sticky => OutsideState::FocusedSticky, + OutsideState::Unfocused if sticky => OutsideState::UnfocusedSticky, + OutsideState::FocusedSticky if !sticky => OutsideState::Focused, + OutsideState::UnfocusedSticky if !sticky => OutsideState::Unfocused, + _ => return, + }); } #[inline] - pub fn is_urgent(&self) -> bool { - self.urgent.get() + pub fn is_sticky(&self) -> bool { + self.sticky.get() } #[inline] - pub fn set_consuming( + pub fn set_disowned( &self, - consuming: bool, + toggle: Toggle, ) { - self.consuming.replace(consuming); - } + let disowned = toggle.eval(self.disowned.get()); + self.disowned.set(disowned); - #[inline] - pub fn is_consuming(&self) -> bool { - self.consuming.get() + self.outside_state.set(match self.outside_state.get() { + OutsideState::Focused if disowned => OutsideState::FocusedDisowned, + OutsideState::Unfocused if disowned => OutsideState::UnfocusedDisowned, + OutsideState::FocusedDisowned if !disowned => OutsideState::Focused, + OutsideState::UnfocusedDisowned if !disowned => OutsideState::Unfocused, + _ => return, + }); } #[inline] - pub fn set_producing( - &self, - producing: bool, - ) { - self.producing.replace(producing); + pub fn is_disowned(&self) -> bool { + self.disowned.get() } #[inline] - pub fn is_producing(&self) -> bool { - self.producing.get() + pub fn outside_state(&self) -> OutsideState { + if self.urgent.get() { + OutsideState::Urgent + } else { + self.outside_state.get() + } } #[inline] @@ -661,7 +780,7 @@ impl Client { #[inline] pub fn expect_unmap(&self) { self.expected_unmap_count - .replace(self.expected_unmap_count.get() + 1); + .set(self.expected_unmap_count.get() + 1); } #[inline] @@ -670,7 +789,7 @@ impl Client { let expecting = expected_unmap_count > 0; if expecting { - self.expected_unmap_count.replace(expected_unmap_count - 1); + self.expected_unmap_count.set(expected_unmap_count - 1); } expecting @@ -724,7 +843,7 @@ impl std::fmt::Debug for Client { .field("decoration", &self.decoration) .field("size_hints", &self.size_hints) .field("warp_pos", &self.warp_pos) - .field("parent", &self.parent.map(|parent| Hex32(parent))) + .field("parent", &self.parent.map(Hex32)) .field( "children", &self @@ -740,7 +859,7 @@ impl std::fmt::Debug for Client { .field("focused", &self.focused) .field("mapped", &self.mapped) .field("managed", &self.managed) - .field("in_window", &self.in_window) + .field("contained", &self.contained) .field("floating", &self.floating) .field("fullscreen", &self.fullscreen) .field("iconified", &self.iconified) diff --git a/src/core/cycle.rs b/src/core/cycle.rs @@ -192,7 +192,7 @@ where pub fn index_for( &self, - sel: &Selector<T>, + sel: &Selector<'_, T>, ) -> Option<Index> { match sel { Selector::AtActive => Some(self.index.get()), @@ -238,7 +238,7 @@ where self.indices.clear(); for (i, id) in self.elements.iter().enumerate().map(|(i, e)| (i, e.id())) { - self.indices.insert(id as Ident, i as Index); + self.indices.insert(id, i); } } } @@ -340,12 +340,12 @@ where } #[inline] - pub fn iter(&self) -> std::collections::vec_deque::Iter<T> { + pub fn iter(&self) -> std::collections::vec_deque::Iter<'_, T> { self.elements.iter() } #[inline] - pub fn iter_mut(&mut self) -> std::collections::vec_deque::IterMut<T> { + pub fn iter_mut(&mut self) -> std::collections::vec_deque::IterMut<'_, T> { self.elements.iter_mut() } @@ -367,7 +367,7 @@ where pub fn get_for( &self, - sel: &Selector<T>, + sel: &Selector<'_, T>, ) -> Option<&T> { match sel { Selector::AtActive => self.active_element(), @@ -387,7 +387,7 @@ where pub fn get_for_mut( &mut self, - sel: &Selector<T>, + sel: &Selector<'_, T>, ) -> Option<&mut T> { match sel { Selector::AtActive => self.active_element_mut(), @@ -409,7 +409,7 @@ where pub fn get_all_for( &self, - sel: &Selector<T>, + sel: &Selector<'_, T>, ) -> Vec<&T> { match sel { Selector::AtActive => self.active_element().into_iter().collect(), @@ -433,7 +433,7 @@ where pub fn get_all_for_mut( &mut self, - sel: &Selector<T>, + sel: &Selector<'_, T>, ) -> Vec<&mut T> { match sel { Selector::AtActive => self.active_element_mut().into_iter().collect(), @@ -494,7 +494,7 @@ where pub fn on_all_for<F: Fn(&T)>( &self, f: F, - sel: &Selector<T>, + sel: &Selector<'_, T>, ) { for element in self.get_all_for(sel) { f(element); @@ -504,7 +504,7 @@ where pub fn on_all_for_mut<F: FnMut(&mut T)>( &mut self, mut f: F, - sel: &Selector<T>, + sel: &Selector<'_, T>, ) { for element in self.get_all_for_mut(sel) { f(element); @@ -513,7 +513,7 @@ where pub fn activate_for( &self, - sel: &Selector<T>, + sel: &Selector<'_, T>, ) -> Option<&T> { match sel { Selector::AtActive => self.active_element(), @@ -553,7 +553,7 @@ where pub fn remove_for( &mut self, - sel: &Selector<T>, + sel: &Selector<'_, T>, ) -> Option<T> { let (index, element) = match sel { Selector::AtActive => (self.index.get(), self.elements.remove(self.index.get())), @@ -585,8 +585,8 @@ where pub fn swap( &mut self, - sel1: &Selector<T>, - sel2: &Selector<T>, + sel1: &Selector<'_, T>, + sel2: &Selector<'_, T>, ) { let index1 = self.index_for(sel1); @@ -790,8 +790,8 @@ where impl<T: PartialEq + Identify + std::fmt::Debug> Cycle<T> { pub fn equivalent_selectors( &self, - sel1: &Selector<T>, - sel2: &Selector<T>, + sel1: &Selector<'_, T>, + sel2: &Selector<'_, T>, ) -> bool { match (self.index_for(&sel1), self.index_for(&sel2)) { (Some(e), Some(f)) => e == f, diff --git a/src/core/decoration.rs b/src/core/decoration.rs @@ -7,24 +7,24 @@ pub type Color = u32; #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct ColorScheme { - pub regular: Color, pub focused: Color, - pub urgent: Color, - pub rdisowned: Color, pub fdisowned: Color, - pub rsticky: Color, pub fsticky: Color, + pub unfocused: Color, + pub udisowned: Color, + pub usticky: Color, + pub urgent: Color, } impl ColorScheme { pub const DEFAULT: Self = Self { - regular: 0x333333, focused: 0xe78a53, - urgent: 0xfbcb97, - rdisowned: 0x999999, fdisowned: 0xc1c1c1, - rsticky: 0x444444, fsticky: 0x5f8787, + unfocused: 0x333333, + udisowned: 0x999999, + usticky: 0x444444, + urgent: 0xfbcb97, }; } diff --git a/src/core/identify.rs b/src/core/identify.rs @@ -1,14 +1,6 @@ -use winsys::window::Window; - pub type Ident = u32; pub type Index = usize; pub trait Identify: PartialEq { fn id(&self) -> Ident; } - -impl Identify for Window { - fn id(&self) -> Ident { - *self as Ident - } -} diff --git a/src/core/layout.rs b/src/core/layout.rs @@ -805,8 +805,8 @@ impl Layout { ) -> Region { Region { pos: Pos { - x: region.pos.x + extents.left as i32, - y: region.pos.y + extents.top as i32, + x: region.pos.x + extents.left, + y: region.pos.y + extents.top, }, dim: Dim { w: region.dim.w - extents.left - extents.right, @@ -926,7 +926,7 @@ impl Apply for Layout { } } -impl std::cmp::PartialEq<Self> for Layout { +impl PartialEq<Self> for Layout { fn eq( &self, other: &Self, @@ -936,6 +936,7 @@ impl std::cmp::PartialEq<Self> for Layout { } impl Identify for Layout { + #[inline(always)] fn id(&self) -> Ident { self.kind as Ident } diff --git a/src/core/macros.rs b/src/core/macros.rs @@ -1,13 +1,20 @@ #[macro_export] +macro_rules! call( + ($($method:tt)+) => { + |arg| $($method)+(arg) + }; +); + +#[macro_export] macro_rules! do_internal( ($func:ident) => { - Box::new(|model: &mut $crate::model::Model| { + Box::new(|model: &mut $crate::model::Model<'_>| { drop(model.$func()); }) as $crate::binding::KeyEvents }; ($func:ident, $($arg:expr),+) => { - Box::new(move |model: &mut $crate::model::Model| { + Box::new(move |model: &mut $crate::model::Model<'_>| { drop(model.$func($($arg),+)); }) as $crate::binding::KeyEvents }; @@ -16,7 +23,7 @@ macro_rules! do_internal( #[macro_export] macro_rules! do_internal_block( ($model:ident, $body:block) => { - Box::new(|$model: &mut $crate::model::Model| { + Box::new(|$model: &mut $crate::model::Model<'_>| { $body }) as $crate::binding::KeyEvents }; @@ -25,20 +32,20 @@ macro_rules! do_internal_block( #[macro_export] macro_rules! do_nothing( () => { - Box::new(|_: &mut $crate::model::Model, _| {}) as $crate::binding::MouseEvents + Box::new(|_: &mut $crate::model::Model<'_>, _| {}) as $crate::binding::MouseEvents }; ); #[macro_export] macro_rules! do_internal_mouse( ($func:ident) => { - Box::new(|model: &mut $crate::model::Model, _| { + Box::new(|model: &mut $crate::model::Model<'_>, _| { drop(model.$func()); }) as $crate::binding::MouseEvents }; ($func:ident, $($arg:expr),+) => { - Box::new(|model: &mut $crate::model::Model, _| { + Box::new(|model: &mut $crate::model::Model<'_>, _| { drop(model.$func($($arg),+)); }) as $crate::binding::MouseEvents }; @@ -47,7 +54,7 @@ macro_rules! do_internal_mouse( #[macro_export] macro_rules! do_internal_mouse_block( ($model:ident, $window:ident, $body:block) => { - Box::new(|$model: &mut $crate::model::Model, $window: Option<winsys::window::Window>| { + Box::new(|$model: &mut $crate::model::Model<'_>, $window: Option<winsys::window::Window>| { $body }) as $crate::binding::MouseEvents }; @@ -57,7 +64,7 @@ macro_rules! do_internal_mouse_block( macro_rules! spawn_external( ($cmd:expr) => { { - Box::new(move |_: &mut $crate::model::Model| { + Box::new(move |_: &mut $crate::model::Model<'_>| { $crate::util::Util::spawn($cmd); }) as $crate::binding::KeyEvents } @@ -68,7 +75,7 @@ macro_rules! spawn_external( macro_rules! spawn_from_shell( ($cmd:expr) => { { - Box::new(move |_: &mut $crate::model::Model| { + Box::new(move |_: &mut $crate::model::Model<'_>| { $crate::util::Util::spawn_shell($cmd); }) as $crate::binding::KeyEvents } diff --git a/src/core/main.rs b/src/core/main.rs @@ -1,4 +1,14 @@ -#![deny(clippy::all)] +#![warn(clippy::all)] +#![warn(missing_copy_implementations)] +#![warn(missing_debug_implementations)] +#![warn(missing_debug_implementations)] +#![warn(rust_2018_idioms)] +#![warn(trivial_numeric_casts)] +#![warn(trivial_numeric_casts)] +#![warn(unsafe_code)] +#![warn(unused_extern_crates)] +#![warn(unused_import_braces)] +#![warn(unused_qualifications)] #![allow(dead_code)] #![recursion_limit = "256"] @@ -44,6 +54,7 @@ use binding::KeyBindings; use binding::MouseBindings; use change::Change; use change::Direction; +use change::Toggle; use compare::MatchMethod; use jump::JumpCriterium; use layout::LayoutKind; @@ -74,13 +85,13 @@ fn init_bindings() -> (MouseBindings, KeyBindings) { (Press, Client, true): "1-C-Right" => do_internal_mouse_block!(model, window, { if let Some(window) = window { - model.toggle_float_client(window); + model.set_floating_window(window, Toggle::Reverse); } }), (Press, Client, true): "1-C-S-Middle" => do_internal_mouse_block!(model, window, { if let Some(window) = window { - model.toggle_fullscreen_client(window); + model.set_fullscreen_window(window, Toggle::Reverse); } }), @@ -88,7 +99,7 @@ fn init_bindings() -> (MouseBindings, KeyBindings) { (Press, Client, true): "1-Middle" => do_internal_mouse_block!(model, window, { if let Some(window) = window { - model.center_client(window); + model.center_window(window); } }), (Press, Client, true): @@ -106,13 +117,13 @@ fn init_bindings() -> (MouseBindings, KeyBindings) { (Press, Client, false): "1-C-S-ScrollDown" => do_internal_mouse_block!(model, window, { if let Some(window) = window { - model.grow_ratio_client(window, -15); + model.grow_ratio_window(window, -15); } }), (Press, Client, false): "1-C-S-ScrollUp" => do_internal_mouse_block!(model, window, { if let Some(window) = window { - model.grow_ratio_client(window, 15); + model.grow_ratio_window(window, 15); } }), @@ -124,21 +135,21 @@ fn init_bindings() -> (MouseBindings, KeyBindings) { // workspace activators (Press, Global, false): - "1-S-ScrollDown" => do_internal_mouse!(activate_next_workspace), + "1-S-ScrollUp" => do_internal_mouse!(activate_next_workspace, Direction::Backward), (Press, Global, false): - "1-S-ScrollUp" => do_internal_mouse!(activate_prev_workspace), + "1-S-ScrollDown" => do_internal_mouse!(activate_next_workspace, Direction::Forward), // workspace client movement (Press, Client, false): "1-Forward" => do_internal_mouse_block!(model, window, { if let Some(window) = window { - model.move_client_to_next_workspace(window); + model.move_window_to_next_workspace(window, Direction::Forward); } }), (Press, Client, false): "1-Backward" => do_internal_mouse_block!(model, window, { if let Some(window) = window { - model.move_client_to_prev_workspace(window); + model.move_window_to_next_workspace(window, Direction::Backward); } }), @@ -155,17 +166,16 @@ fn init_bindings() -> (MouseBindings, KeyBindings) { // client state modifiers "1-c" => do_internal!(kill_focus), - "1-S-space" => do_internal!(toggle_float_focus), - "1-f" => do_internal!(toggle_fullscreen_focus), - "1-x" => do_internal!(toggle_stick_focus), - "1-2-C-f" => do_internal!(toggle_in_window_focus), - "1-2-C-i" => do_internal!(toggle_invincible_focus), - "1-2-C-p" => do_internal!(toggle_producing_focus), - "1-y" => do_internal!(iconify_focus), + "1-S-space" => do_internal!(set_floating_focus, Toggle::Reverse), + "1-f" => do_internal!(set_fullscreen_focus, Toggle::Reverse), + "1-x" => do_internal!(set_stick_focus, Toggle::Reverse), + "1-2-C-f" => do_internal!(set_contained_focus, Toggle::Reverse), + "1-2-C-i" => do_internal!(set_invincible_focus, Toggle::Reverse), + "1-2-C-p" => do_internal!(set_producing_focus, Toggle::Reverse), + "1-y" => do_internal!(set_iconify_focus, Toggle::On), "1-u" => do_internal!(pop_deiconify), "1-2-u" => do_internal_block!(model, { - let workspace_index = model.active_workspace(); - model.deiconify_all(workspace_index); + model.deiconify_all(model.active_workspace()); }), // free client arrangers @@ -201,8 +211,8 @@ fn init_bindings() -> (MouseBindings, KeyBindings) { "1-C-C" => do_internal!(delete_zone), // zone order modifiers - "1-C-j" => do_internal!(cycle_zones, Direction::Forward), - "1-C-k" => do_internal!(cycle_zones, Direction::Backward), + // "1-C-j" => do_internal!(cycle_zones, Direction::Forward), + // "1-C-k" => do_internal!(cycle_zones, Direction::Backward), // active workspace layout setters "1-S-f" => do_internal!(set_layout, LayoutKind::Float), @@ -246,8 +256,8 @@ fn init_bindings() -> (MouseBindings, KeyBindings) { // workspace activators "1-Escape" => do_internal!(toggle_workspace), - "1-bracketleft" => do_internal!(activate_prev_workspace), - "1-bracketright" => do_internal!(activate_next_workspace), + "1-bracketleft" => do_internal!(activate_next_workspace, Direction::Backward), + "1-bracketright" => do_internal!(activate_next_workspace, Direction::Forward), "1-1" => do_internal!(activate_workspace, 0), "1-2" => do_internal!(activate_workspace, 1), "1-3" => do_internal!(activate_workspace, 2), @@ -260,8 +270,8 @@ fn init_bindings() -> (MouseBindings, KeyBindings) { "1-0" => do_internal!(activate_workspace, 9), // workspace client movement - "1-S-bracketleft" => do_internal!(move_focus_to_prev_workspace), - "1-S-bracketright" => do_internal!(move_focus_to_next_workspace), + "1-S-bracketleft" => do_internal!(move_focus_to_next_workspace, Direction::Backward), + "1-S-bracketright" => do_internal!(move_focus_to_next_workspace, Direction::Forward), "1-S-1" => do_internal!(move_focus_to_workspace, 0), "1-S-2" => do_internal!(move_focus_to_workspace, 1), "1-S-3" => do_internal!(move_focus_to_workspace, 2), @@ -278,51 +288,45 @@ fn init_bindings() -> (MouseBindings, KeyBindings) { // client jump criteria "1-b" => do_internal!(jump_client, - &JumpCriterium::ByClass(MatchMethod::Equals("qutebrowser")) + JumpCriterium::ByClass(MatchMethod::Equals("qutebrowser")) ), "1-S-b" => do_internal!(jump_client, - &JumpCriterium::ByClass(MatchMethod::Equals("Firefox")) + JumpCriterium::ByClass(MatchMethod::Equals("Firefox")) ), "1-C-b" => do_internal!(jump_client, - &JumpCriterium::ByClass(MatchMethod::Equals("Chromium")) + JumpCriterium::ByClass(MatchMethod::Equals("Chromium")) ), "1-2-space" => do_internal!(jump_client, - &JumpCriterium::ByClass(MatchMethod::Equals("Spotify")) + JumpCriterium::ByClass(MatchMethod::Equals("Spotify")) ), "1-e" => do_internal_block!(model, { - model.jump_client(&JumpCriterium::ByName( + model.jump_client(JumpCriterium::ByName( MatchMethod::Contains("[vim]"), )); }), "1-slash" => do_internal_block!(model, { - let workspace_index = model.active_workspace(); - - model.jump_client(&JumpCriterium::OnWorkspaceBySelector( - workspace_index, + model.jump_client(JumpCriterium::OnWorkspaceBySelector( + model.active_workspace(), &ClientSelector::Last, )); }), "1-period" => do_internal_block!(model, { - let workspace_index = model.active_workspace(); - - model.jump_client(&JumpCriterium::OnWorkspaceBySelector( - workspace_index, + model.jump_client(JumpCriterium::OnWorkspaceBySelector( + model.active_workspace(), &ClientSelector::AtMaster, )); }), "1-comma" => do_internal_block!(model, { - let workspace_index = model.active_workspace(); - - model.jump_client(&JumpCriterium::OnWorkspaceBySelector( - workspace_index, + model.jump_client(JumpCriterium::OnWorkspaceBySelector( + model.active_workspace(), &ClientSelector::First, )); }), // external spawn commands - "XF86AudioPlay", "1-2-C-p" => spawn_external!("playerctl play-pause"), - "XF86AudioPrev", "1-2-C-k" => spawn_external!("playerctl previous"), - "XF86AudioNext", "1-2-C-j" => spawn_external!("playerctl next"), + "XF86AudioPlay", "1-2-p" => spawn_external!("playerctl play-pause"), + "XF86AudioPrev", "1-2-k" => spawn_external!("playerctl previous"), + "XF86AudioNext", "1-2-j" => spawn_external!("playerctl next"), "1-2-BackSpace" => spawn_external!("playerctl stop"), "XF86AudioMute" => spawn_external!("amixer -D pulse sset Master toggle"), "XF86AudioLowerVolume" => spawn_external!("amixer -D pulse sset Master 5%-"), diff --git a/src/core/model.rs b/src/core/model.rs @@ -5,6 +5,7 @@ use crate::binding::KeyBindings; use crate::binding::MouseBindings; use crate::change::Change; use crate::change::Direction; +use crate::change::Toggle; use crate::client::Client; use crate::consume::get_spawner_pid; use crate::cycle::Cycle; @@ -52,6 +53,7 @@ use winsys::input::KeyCode; use winsys::input::MouseEvent; use winsys::input::MouseEventKey; use winsys::input::MouseEventKind; +use winsys::input::MouseShortcut; use winsys::screen::Screen; use winsys::window::IcccmWindowState; use winsys::window::Window; @@ -132,7 +134,6 @@ impl<'model> Model<'model> { .partitions .active_element() .expect("no screen region found") - .screen() .placeable_region(); defaults::WORKSPACE_NAMES @@ -162,14 +163,11 @@ impl<'model> Model<'model> { .init_wm_properties(WM_NAME!(), &defaults::WORKSPACE_NAMES); model.conn.grab_bindings( - &key_bindings - .keys() - .into_iter() - .collect::<Vec<&winsys::input::KeyCode>>(), + &key_bindings.keys().into_iter().collect::<Vec<&KeyCode>>(), &mouse_bindings .keys() .into_iter() - .collect::<Vec<&(winsys::input::MouseEventKey, winsys::input::MouseShortcut)>>(), + .collect::<Vec<&(MouseEventKey, MouseShortcut)>>(), ); model @@ -177,9 +175,7 @@ impl<'model> Model<'model> { .top_level_windows() .into_iter() .for_each(|window| { - if model.conn.must_manage_window(window) { - model.manage(window, false); - } + model.manage(window, !model.conn.must_manage_window(window)); }); if cfg!(not(debug_assertions)) { @@ -194,114 +190,7 @@ impl<'model> Model<'model> { model } - pub fn run( - &mut self, - mut key_bindings: KeyBindings, - mut mouse_bindings: MouseBindings, - ) { - while self.running { - if let Some(event) = self.conn.step() { - trace!("received event: {:?}", event); - - match event { - Event::Mouse { - event, - } => self.handle_mouse(event, &mut mouse_bindings), - Event::Key { - key_code, - } => self.handle_key(key_code, &mut key_bindings), - Event::MapRequest { - window, - ignore, - } => self.handle_map_request(window, ignore), - Event::Map { - window, - ignore, - } => self.handle_map(window, ignore), - Event::Enter { - window, - root_rpos, - window_rpos, - } => self.handle_enter(window, root_rpos, window_rpos), - Event::Leave { - window, - root_rpos, - window_rpos, - } => self.handle_leave(window, root_rpos, window_rpos), - Event::Destroy { - window, - } => self.handle_destroy(window), - Event::Expose { - window, - } => self.handle_expose(window), - Event::Unmap { - window, - ignore, - } => self.handle_unmap(window, ignore), - Event::Configure { - window, - region, - on_root, - } => self.handle_configure(window, region, on_root), - Event::StateRequest { - window, - state, - action, - on_root, - } => self.handle_state_request(window, state, action, on_root), - Event::FocusRequest { - window, - on_root, - } => self.handle_focus_request(window, on_root), - Event::CloseRequest { - window, - on_root, - } => self.handle_close_request(window, on_root), - Event::WorkspaceRequest { - window, - index, - on_root, - } => self.handle_workspace_request(window, index, on_root), - Event::PlacementRequest { - window, - pos, - dim, - on_root, - } => self.handle_placement_request(window, pos, dim, on_root), - Event::GripRequest { - window, - pos, - grip, - on_root, - } => self.handle_grip_request(window, pos, grip, on_root), - Event::RestackRequest { - window, - sibling, - mode, - on_root, - } => self.handle_restack_request(window, sibling, mode, on_root), - Event::Property { - window, - kind, - on_root, - } => self.handle_property(window, kind, on_root), - Event::FrameExtentsRequest { - window, - on_root, - } => self.handle_frame_extents_request(window, on_root), - Event::Mapping { - request, - } => self.handle_mapping(request), - Event::ScreenChange => self.handle_screen_change(), - Event::Randr => self.handle_randr(), - } - } - - self.conn.flush(); - } - } - - #[inline] + #[inline(always)] fn window( &self, window: Window, @@ -310,10 +199,10 @@ impl<'model> Model<'model> { return Some(window); } - Some(self.frame_map.get(&window)?.to_owned()) + self.frame_map.get(&window).map(|window| window.to_owned()) } - #[inline] + #[inline(always)] fn frame( &self, window: Window, @@ -322,10 +211,10 @@ impl<'model> Model<'model> { return Some(window); } - Some(self.window_map.get(&window)?.to_owned()) + self.window_map.get(&window).map(|window| window.to_owned()) } - #[inline] + #[inline(always)] fn client_any( &self, mut window: Window, @@ -337,7 +226,19 @@ impl<'model> Model<'model> { self.client_map.get(&window) } - #[inline] + #[inline(always)] + fn client_any_unchecked( + &self, + mut window: Window, + ) -> &Client { + if let Some(&inside) = self.frame_map.get(&window) { + window = inside; + } + + self.client_map.get(&window).unwrap() + } + + #[inline(always)] fn client( &self, window: Window, @@ -345,12 +246,45 @@ impl<'model> Model<'model> { self.client_any(window).filter(|client| client.is_managed()) } - #[inline] + #[inline(always)] + fn client_unchecked( + &self, + window: Window, + ) -> &Client { + self.client_any(window) + .filter(|&client| client.is_managed()) + .unwrap() + } + + #[inline(always)] + fn active_partition(&self) -> usize { + self.partitions.active_index() + } + + #[inline(always)] + fn active_screen(&self) -> &Screen { + self.partitions.active_element().unwrap().screen() + } + + #[inline(always)] + pub fn active_workspace(&self) -> usize { + self.workspaces.active_index() + } + + #[inline(always)] + fn focused_client(&self) -> Option<&Client> { + self.focus + .get() + .or_else(|| self.workspace(self.active_workspace()).focused_client()) + .and_then(|focus| self.client_map.get(&focus)) + } + + #[inline(always)] fn workspace( &self, index: Index, ) -> &Workspace { - self.workspaces.get(index).unwrap() + &self.workspaces[index] } fn acquire_partitions(&mut self) { @@ -359,20 +293,42 @@ impl<'model> Model<'model> { .connected_outputs() .into_iter() .enumerate() - .map(|(i, mut s)| { + .map(|(i, s)| { s.compute_placeable_region(); Partition::new(s, i) }) .collect(); - info!("acquired partitions: {:#?}", partitions); - self.partitions = Cycle::new(partitions, false); + if partitions.is_empty() { + error!("no screen resources found, keeping old partitions"); + } else { + info!("acquired partitions: {:#?}", partitions); + self.partitions = Cycle::new(partitions, false); + } + } + + #[inline] + pub fn toggle_screen_struts(&self) { + let screen = self.active_screen(); + let show = !screen.showing_struts(); + + screen + .show_and_yield_struts(show) + .iter() + .for_each(|&strut| { + if show { + self.conn.map_window(strut); + } else { + self.conn.unmap_window(strut); + } + }); + + self.apply_layout(self.active_workspace()); } fn apply_layout( &self, index: Index, - must_apply_stack: bool, ) { if index != self.active_workspace() { return; @@ -389,7 +345,7 @@ impl<'model> Model<'model> { .arrange( &self.zone_manager, &self.client_map, - self.active_screen().placeable_region(), + self.partitions.active_element().unwrap().placeable_region(), |client| !Self::is_applyable(client) || client.is_iconified(), ) .into_iter() @@ -398,9 +354,11 @@ impl<'model> Model<'model> { show.iter().for_each(|placement| { match placement.kind { PlacementTarget::Client(window) => { - self.update_client_placement(&placement); - self.place_client(window, placement.method); - self.map_client(self.client(window).unwrap()); + let client = &self.client_map[&window]; + + self.update_client_placement(client, &placement); + self.place_client(client, placement.method); + self.map_client(client); }, PlacementTarget::Tab(_) => {}, PlacementTarget::Layout => {}, @@ -410,16 +368,12 @@ impl<'model> Model<'model> { hide.iter().for_each(|placement| { match placement.kind { PlacementTarget::Client(window) => { - self.unmap_client(self.client(window).unwrap()); + self.unmap_client(&self.client_map[&window]); }, PlacementTarget::Tab(_) => {}, PlacementTarget::Layout => {}, }; }); - - if must_apply_stack { - self.apply_stack(index); - } } fn apply_stack( @@ -451,13 +405,13 @@ impl<'model> Model<'model> { let (regular, fullscreen): (Vec<Window>, Vec<Window>) = stack.iter().partition(|&&window| { - let client = self.client(window).unwrap(); - !client.is_fullscreen() || client.is_in_window() + let client = self.client_unchecked(window); + !client.is_fullscreen() || client.is_contained() }); let (free, regular): (Vec<Window>, Vec<Window>) = regular .into_iter() - .partition(|&window| self.is_free(self.client(window).unwrap())); + .partition(|&window| self.is_free(self.client_unchecked(window))); let mut windows: Vec<Window> = desktop .into_iter() @@ -471,12 +425,16 @@ impl<'model> Model<'model> { .into_iter() .collect(); - // handle above-other relationships - self.stack_manager.above_other().keys().for_each(|&window| { - if let Some(index) = windows.iter().position(|&candidate| candidate == window) { - windows.remove(index); - } - }); + // handle {above,below}-other relationships + self.stack_manager + .above_other() + .keys() + .chain(self.stack_manager.below_other().keys()) + .for_each(|&window| { + if let Some(index) = windows.iter().position(|&candidate| candidate == window) { + windows.remove(index); + } + }); self.stack_manager .above_other() @@ -489,13 +447,6 @@ impl<'model> Model<'model> { } }); - // handle below-other relationships - self.stack_manager.below_other().keys().for_each(|&window| { - if let Some(index) = windows.iter().position(|&candidate| candidate == window) { - windows.remove(index); - } - }); - self.stack_manager .below_other() .iter() @@ -553,6 +504,7 @@ impl<'model> Model<'model> { self.conn.update_client_list_stacking(&client_list_stacking); } + #[inline] fn detect_rules( &self, instance: &str, @@ -563,25 +515,37 @@ impl<'model> Model<'model> { let mut rules: Rules = Default::default(); match (instance.get(..PREFIX_LEN), instance.get(PREFIX_LEN..)) { - (Some(PREFIX), flags) => { - if let Some(flags) = flags { - let mut invert = false; - - for i in 0..flags.len() { - let flag = &flags[i..=i]; - - match flag { - "!" => { - invert = true; - continue; - }, - "f" => rules.float = Some(!invert), - "c" => rules.center = Some(!invert), - _ => {}, - } - - invert = false; + (Some(PREFIX), Some(flags)) if !flags.is_empty() => { + let mut invert = false; + let mut workspace = false; + + for i in 0..flags.len() { + let flag = &flags[i..=i]; + + match flag { + "!" => { + invert = true; + continue; + }, + "w" => { + workspace = true; + continue; + }, + number if workspace => { + if let Ok(number) = number.parse::<usize>() { + if number < self.workspaces.len() { + rules.workspace = Some(number); + } + } + }, + "f" => rules.float = Some(!invert), + "F" => rules.fullscreen = Some(!invert), + "c" => rules.center = Some(!invert), + _ => {}, } + + invert = false; + workspace = false; } }, _ => {}, @@ -607,14 +571,8 @@ impl<'model> Model<'model> { } let pid = self.conn.get_window_pid(window); - let ppid = pid.and_then(|pid| { - get_spawner_pid( - pid, - std::process::id() as Pid, - &self.pid_map, - &self.client_map, - ) + get_spawner_pid(pid, std::process::id(), &self.pid_map, &self.client_map) }); let name = self.conn.get_icccm_window_name(window); @@ -624,64 +582,43 @@ impl<'model> Model<'model> { let preferred_state = self.conn.get_window_preferred_state(window); let preferred_type = self.conn.get_window_preferred_type(window); - let geometry = self.conn.get_window_geometry(window); - - if geometry.is_err() { - return; - } + let mut geometry = match self.conn.get_window_geometry(window) { + Ok(geometry) => geometry, + Err(_) => return, + }; self.stop_moving(); self.stop_resizing(); - let original_geometry = geometry.unwrap(); - let mut geometry = original_geometry; - + let at_origin = geometry.pos.is_origin(); let frame = self.conn.create_frame(geometry); let rules = self.detect_rules(&instance); let hints = self.conn.get_icccm_window_hints(window); - let (_, size_hints) = - self.conn - .get_icccm_window_size_hints(window, Some(Client::MIN_CLIENT_DIM), &None); - - geometry = if size_hints.is_some() { - geometry - .with_size_hints(&size_hints) - .with_extents(Decoration::FREE_DECORATION.extents()) - } else { - geometry + let size_hints = self + .conn + .get_icccm_window_size_hints(window, Some(Client::MIN_CLIENT_DIM), &None) + .1; + + geometry = match size_hints { + Some(size_hints) => geometry + .with_size_hints(&Some(size_hints)) + .with_extents(Decoration::FREE_DECORATION.extents()), + None => geometry .with_minimum_dim(&Client::MIN_CLIENT_DIM) - .with_extents(Decoration::FREE_DECORATION.extents()) + .with_extents(Decoration::FREE_DECORATION.extents()), }; let parent = self.conn.get_icccm_window_transient_for(window); - - // TODO: startup sequence/notification - // TODO: MOTIF decorations for old-style applications - - let context = 0; - let workspace = self - .conn - .get_window_desktop(window) - .map_or(self.active_workspace(), |d| { - if d < self.workspaces.len() { - d - } else { - self.active_workspace() - } - }); - - let mut center = false; - - // TODO: retrieve screen of new client's workspace let screen = self.active_screen(); + let context = 0; + let workspace = rules.workspace.unwrap_or_else(|| { + self.conn + .get_window_desktop(window) + .filter(|&workspace| workspace < self.workspaces.len()) + .unwrap_or_else(|| self.active_workspace()) + }); - if rules.center(&mut center) - || (size_hints.is_none() || !size_hints.unwrap().by_user) - && original_geometry.pos - == (Pos { - x: 0, - y: 0, - }) + if rules.center() || size_hints.map_or(true, |size_hints| !size_hints.by_user) && at_origin { geometry = screen .full_region() @@ -708,9 +645,9 @@ impl<'model> Model<'model> { ppid, ); - let fullscreen = self.conn.window_is_fullscreen(window); + let fullscreen = self.conn.window_is_fullscreen(window) | rules.fullscreen(); let sticky = self.conn.window_is_sticky(window); - let mut floating = self.conn.must_free_window(window); + let mut floating = self.conn.must_free_window(window) | rules.float(); if let Some(parent) = parent { floating = true; @@ -724,6 +661,7 @@ impl<'model> Model<'model> { if let Some(leader) = leader { let leader_window = leader.window(); + if leader_window != window { floating = true; client.set_leader(leader_window); @@ -731,85 +669,82 @@ impl<'model> Model<'model> { } if let Some(hints) = hints { - client.set_urgent(hints.urgent); + client.set_urgent(Toggle::from(hints.urgent)); } - rules.float(&mut floating); - - client.set_floating(floating); + client.set_floating(Toggle::from(floating)); client.set_region(PlacementClass::Free(geometry)); client.set_size_hints(size_hints); client.set_context(context); client.set_workspace(workspace); - let extents = Decoration::FREE_DECORATION.extents(); - self.conn.reparent_window(window, frame, Pos { - x: extents.left as i32, - y: extents.top as i32, + self.conn.reparent_window(window, frame, { + let extents = Decoration::FREE_DECORATION.extents(); + + Pos { + x: extents.left, + y: extents.top, + } }); - self.workspaces[workspace].add_client(window, &InsertPos::Back); + if let Some(parent) = parent.and_then(|parent| self.client_any(parent)) { + let parent_frame = parent.frame(); - if let Some(parent) = parent { - if let Some(parent) = self.client_any(parent) { - let parent_frame = parent.frame(); - parent.add_child(window); - self.stack_manager.add_above_other(frame, parent_frame); - } + parent.add_child(window); + self.stack_manager.add_above_other(frame, parent_frame); } if let Some(pid) = pid { self.pid_map.insert(pid, window); } - self.conn - .set_icccm_window_state(window, IcccmWindowState::Normal); + self.workspaces[workspace].add_client(window, &InsertPos::AfterActive); self.client_map.insert(window, client); self.frame_map.insert(frame, window); self.window_map.insert(window, frame); self.conn.insert_window_in_save_set(window); - self.conn.init_window(window, false); // TODO config.focus_follows_mouse - self.conn.init_frame(frame, false); // TODO: config.focus_follows_mouse + self.conn.init_window(window, false); + self.conn.init_frame(frame, false); self.conn.set_window_border_width(window, 0); self.conn.set_window_desktop(window, workspace); + self.conn + .set_icccm_window_state(window, IcccmWindowState::Normal); - self.apply_layout(workspace, false); + self.apply_layout(workspace); self.focus_window(window); - if let Some(ppid) = ppid { - if let Some(ppid_window) = self.pid_map.get(&ppid) { - let ppid_window = *ppid_window; - if let Some(ppid_client) = self.client(ppid_window) { - if ppid_client.is_producing() { - self.consume_client(window, ppid_window); - } - } - } + if let Some(WindowState::DemandsAttention) = preferred_state { + self.handle_state_request( + window, + WindowState::DemandsAttention, + ToggleAction::Add, + false, + ); } - if let Some(state) = preferred_state { - match state { - WindowState::DemandsAttention => { - self.handle_state_request(window, state, ToggleAction::Add, false) - }, - _ => {}, - } - } + let client = &self.client_map[&window]; if sticky { - self.stick(window); + self.stick(client); } if fullscreen { - self.fullscreen(window); + self.fullscreen(client); } - let client = self.client_any(window).unwrap(); - let active_region = client.active_region(); - let current_pos = self.conn.get_pointer_position(); + if let Some(&ppid_window) = ppid.and_then(|ppid| self.pid_map.get(&ppid)) { + if let Some(ppid_client) = self.client(ppid_window) { + if ppid_client.is_producing() { + self.consume_client(client, ppid_client); + } + } + } - if let Some(warp_pos) = active_region.quadrant_center_from_pos(current_pos) { + if let Some(warp_pos) = client + .active_region() + .quadrant_center_from_pos(self.conn.get_pointer_position()) + { self.conn.warp_pointer(warp_pos); } @@ -825,79 +760,61 @@ impl<'model> Model<'model> { return; } - info!("remanaging client with window {:#0x}", client.window()); - let window = client.window(); - let active_workspace = self.active_workspace(); - let mut workspace = active_workspace; + info!("remanaging client with window {:#0x}", window); - if must_alter_workspace { - let leader = client.leader(); + client.set_managed(Toggle::On); - if let Some(leader) = leader { - if let Some(leader) = self.client(leader) { - workspace = leader.workspace(); - } - } + if must_alter_workspace { + let workspace = client + .leader() + .and_then(|leader| self.client(leader)) + .map(|leader| { + let workspace = leader.workspace(); - { - let workspace = self.workspace(workspace); + client.set_workspace(workspace); + self.workspace(leader.workspace()) + }); + if let Some(workspace) = workspace { if !workspace.contains(window) { workspace.add_client(window, &InsertPos::Back); } } - - let client = self.client_any(window).unwrap(); - client.set_workspace(workspace); } - let client = self.client_any(window).unwrap(); - client.set_managed(true); - - let client = self.client_any(window).unwrap(); if client.is_sticky() { - let client = self.client_any(window).unwrap(); - client.set_sticky(false); - - self.stick(window); + client.set_sticky(Toggle::Off); + self.stick(client); self.map_client(client); } + + self.focus(client); } fn unmanage( - &mut self, - window: Window, + &self, + client: &Client, ) { - if let Some(client) = self.client(window) { - info!("unmanaging client with window {:#0x}", client.window()); - - if client.is_sticky() { - self.unstick(window); - - let client = self.client(window).unwrap(); - client.set_sticky(true); - } - - let client = self.client(window).unwrap(); - let window = client.window(); - let workspace = client.workspace(); - - self.unmap_client(client); + if !client.is_managed() { + return; + } - { - let workspace = self.workspace(workspace); + let window = client.window(); + info!("unmanaging client with window {:#0x}", window); - if workspace.contains(window) { - workspace.remove_client(window); - } - } + client.set_managed(Toggle::Off); - let client = self.client(window).unwrap(); - client.set_managed(false); + if client.is_sticky() { + self.unstick(client); + client.set_sticky(Toggle::On); } + + self.unmap_client(client); + self.workspace(client.workspace()).remove_client(window); } + // TODO: zones pub fn create_layout_zone(&mut self) { let workspace_index = self.active_workspace(); let workspace = self.workspace(workspace_index); @@ -911,9 +828,11 @@ impl<'model> Model<'model> { let workspace = self.workspace(workspace_index); workspace.add_zone(id, &InsertPos::Back); - self.apply_layout(workspace_index, true); + self.apply_layout(workspace_index); + self.apply_stack(workspace_index); } + // TODO: zones pub fn create_tab_zone(&mut self) { let workspace_index = self.active_workspace(); let workspace = self.workspace(workspace_index); @@ -926,9 +845,11 @@ impl<'model> Model<'model> { let workspace = self.workspace(workspace_index); workspace.add_zone(id, &InsertPos::Back); - self.apply_layout(workspace_index, true); + self.apply_layout(workspace_index); + self.apply_stack(workspace_index); } + // TODO: zones pub fn delete_zone(&mut self) { let workspace_index = self.active_workspace(); let workspace = self.workspace(workspace_index); @@ -946,80 +867,79 @@ impl<'model> Model<'model> { workspace.remove_zone(cycle); } + #[inline] fn is_applyable(client: &Client) -> bool { !client.is_floating() && !client.is_disowned() && client.is_managed() - && (!client.is_fullscreen() || client.is_in_window()) + && (!client.is_fullscreen() || client.is_contained()) } + #[inline] fn is_free( &self, client: &Client, ) -> bool { - client.is_floating() && (!client.is_fullscreen() || client.is_in_window()) || { - let id = client.zone(); - let zone = self.zone_manager.zone(id); - - zone.method() == PlacementMethod::Free - } + client.is_free() || self.zone_manager.zone(client.zone()).method() == PlacementMethod::Free } + #[inline] fn is_focusable( &self, - window: Window, + client: &Client, ) -> bool { - self.client(window).map_or(false, |client| { - !client.is_disowned() && !client.is_iconified() - }) + !client.is_disowned() && !client.is_iconified() } fn remove_window( &mut self, window: Window, ) { - let client = self.client(window); - - if client.is_none() { - return; - } + let client = match self.client_any(window) { + Some(client) if !client.consume_unmap_if_expecting() => client, + _ => return, + }; - let client = client.unwrap(); let (window, frame) = client.windows(); - let parent = client.parent(); - let producer = client.producer(); let workspace = client.workspace(); - let id = client.zone(); info!("removing client with window {:#0x}", window); + if !client.is_managed() { + self.remanage(client, true); + } + + if let Ok(geometry) = self.conn.get_window_geometry(frame) { + self.conn.unparent_window(window, geometry.pos); + } + + self.conn.cleanup_window(window); + self.conn.destroy_window(frame); + if client.is_sticky() { - self.unstick(window); + self.unstick(client); } if Some(window) == self.jumped_from.get() { self.jumped_from.set(None); } - if let Some(producer) = producer { - let producer = self.client_any(producer); - if let Some(producer) = producer { - self.unconsume_client(producer); - } + if client.producer().is_some() { + self.unconsume_client(client); } - if let Some(parent) = parent { - if let Some(parent) = self.client(parent) { - parent.remove_child(window); - } + if let Some(parent) = client.parent().and_then(|parent| self.client(parent)) { + parent.remove_child(window); } + let id = client.zone(); self.zone_manager.remove_zone(id); - self.workspaces.get_mut(workspace).map(|w| { - w.remove_client(window); - w.remove_icon(window); - }); + { + let workspace = self.workspace(workspace); + workspace.remove_client(window); + workspace.remove_icon(window); + } self.stack_manager.remove_window(window); self.frame_map.remove(&frame); @@ -1029,206 +949,145 @@ impl<'model> Model<'model> { self.fullscreen_regions.borrow_mut().remove(&window); self.sync_focus(); + self.apply_layout(workspace); } - fn redraw_client( + #[inline(always)] + fn render_decoration( &self, - window: Window, + client: &Client, ) { - if let Some(client) = self.client(window) { - let decoration = client.decoration(); - - if let Some(border) = decoration.border { - self.conn - .set_window_border_width(client.frame(), border.width); - - self.conn.set_window_border_color( - client.frame(), - if client.is_focused() { - if client.is_sticky() { - border.colors.fsticky - } else { - border.colors.focused - } - } else if client.is_urgent() { - border.colors.urgent - } else if client.is_sticky() { - border.colors.rsticky - } else { - border.colors.regular - }, - ); - } + let (border, frame_color) = client.decoration_colors(); - if let Some(frame) = decoration.frame { - self.conn.set_window_background_color( - client.frame(), - if client.is_focused() { - if client.is_sticky() { - frame.colors.fsticky - } else { - frame.colors.focused - } - } else if client.is_urgent() { - frame.colors.urgent - } else if client.is_sticky() { - frame.colors.rsticky - } else { - frame.colors.regular - }, - ); - } + if let Some((width, color)) = border { + self.conn.set_window_border_width(client.frame(), width); + self.conn.set_window_border_color(client.frame(), color); + } + + if let Some(color) = frame_color { + self.conn.set_window_background_color(client.frame(), color); } } + #[inline(always)] fn update_client_placement( &self, + client: &Client, placement: &Placement, ) { - match placement.kind { - PlacementTarget::Client(window) => { - let client = self.client(window).unwrap(); - - let region = match placement.region { - PlacementRegion::FreeRegion => client.free_region(), - PlacementRegion::NewRegion(region) => region, - PlacementRegion::NoRegion => return, - }; - - client.set_decoration(placement.decoration); - - match placement.method { - PlacementMethod::Free => { - let id = client.zone(); - client.set_region(PlacementClass::Free(region)); + let region = match placement.region { + PlacementRegion::FreeRegion => client.free_region(), + PlacementRegion::NewRegion(region) => region, + PlacementRegion::NoRegion => return, + }; - let zone = self.zone_manager.zone(id); - zone.set_region(region); - zone.set_method(placement.method); - }, - PlacementMethod::Tile => { - let id = client.zone(); - client.set_region(PlacementClass::Tile(region)); + let zone = self.zone_manager.zone(client.zone()); + zone.set_method(placement.method); - let zone = self.zone_manager.zone(id); - zone.set_method(placement.method); - }, - }; + client.set_decoration(placement.decoration); + client.set_region(match placement.method { + PlacementMethod::Free => { + zone.set_region(region); + PlacementClass::Free(region) }, - _ => panic!("attempting to update non-client placement"), - } + PlacementMethod::Tile => PlacementClass::Tile(region), + }); } + #[inline(always)] fn place_client( &self, - window: Window, + client: &Client, method: PlacementMethod, ) { - let client = self.client(window).unwrap(); - let (window, frame) = client.windows(); - let inner_region = client.inner_region(); - - self.conn.place_window(window, &inner_region); + self.conn.place_window(window, &client.inner_region()); self.conn.place_window(frame, &match method { PlacementMethod::Free => client.free_region(), PlacementMethod::Tile => client.tile_region(), }); - self.redraw_client(window); + self.render_decoration(client); self.conn.update_window_offset(window, frame); } + #[inline(always)] fn map_client( &self, client: &Client, ) { if !client.is_mapped() { let (window, frame) = client.windows(); - info!("mapping client with window {:#0x}", window); + self.conn.map_window(window); self.conn.map_window(frame); - self.redraw_client(window); - client.set_mapped(true); + self.render_decoration(client); + client.set_mapped(Toggle::On); } } + #[inline(always)] fn unmap_client( &self, client: &Client, ) { if client.is_mapped() { let (window, frame) = client.windows(); - info!("unmapping client with window {:#0x}", window); + self.conn.unmap_window(frame); client.expect_unmap(); - client.set_mapped(false); + client.set_mapped(Toggle::Off); } } fn consume_client( - &mut self, - consumer: Window, - producer: Window, + &self, + consumer: &Client, + producer: &Client, ) { - let consumer_window = consumer; - let producer_window = producer; - - let consumer = self.client_any(consumer_window); - let producer = self.client_any(producer_window); - - if consumer.is_none() || producer.is_none() { + if producer.is_iconified() || consumer.is_iconified() { return; } + let (cwindow, pwindow) = (consumer.window(), producer.window()); + let (cworkspace, pworkspace) = (consumer.workspace(), producer.workspace()); + info!( "consuming client with window {:#0x} and producer window {:#0x}", - consumer_window, producer_window + cwindow, pwindow ); - let consumer = consumer.unwrap(); - let producer = producer.unwrap(); - let producer_workspace_index = producer.workspace(); - - if producer.is_iconified() || consumer.is_iconified() { - return; - } - - let consumer_len = producer.consumer_len(); - let consumer_workspace_index = consumer.workspace(); - let consumer = self.client_any(consumer_window).unwrap(); - consumer.set_consuming(true); - consumer.set_producer(producer_window); + consumer.set_producer(pwindow); - if consumer_len == 0 { - let producer_workspace = self.workspace(producer_workspace_index); + if producer.consumer_len() == 0 { + let workspace = self.workspace(pworkspace); - if producer_workspace_index == consumer_workspace_index { - producer_workspace.replace_client(producer_window, consumer_window); + if pworkspace == cworkspace { + workspace.replace_client(pwindow, cwindow); } else { - producer_workspace.remove_client(producer_window); + workspace.remove_client(pwindow); } - self.apply_layout(consumer_workspace_index, true); + self.apply_layout(cworkspace); + self.apply_stack(cworkspace); } - let producer = self.client_any(producer_window).unwrap(); - producer.add_consumer(consumer_window); - self.unmanage(producer_window); + producer.add_consumer(cwindow); + self.unmanage(producer); } fn unconsume_client( &self, consumer: &Client, ) { - let (producer, workspace) = match consumer + let producer = match consumer .producer() - .and_then(|window| self.client_any(window)) + .and_then(|producer| self.client_any(producer)) { - Some(producer) => (producer, consumer.workspace()), + Some(producer) => producer, None => return, }; @@ -1241,7 +1100,7 @@ impl<'model> Model<'model> { producer.remove_consumer(consumer.window()); if producer.consumer_len() == 0 { - producer.set_workspace(workspace); + let workspace = consumer.workspace(); { let workspace = self.workspace(workspace); @@ -1253,307 +1112,216 @@ impl<'model> Model<'model> { } } + producer.set_workspace(workspace); self.remanage(producer, false); - - if workspace == self.active_workspace() { - self.map_client(producer); - } - - self.apply_layout(workspace, true); + self.apply_layout(workspace); + self.apply_stack(workspace); } consumer.unset_producer(); - consumer.set_consuming(false); } + #[inline(always)] pub fn kill_focus(&self) { if let Some(focus) = self.focus.get() { - self.kill_client(focus); + self.kill_window(focus); } } - pub fn kill_client( + #[inline] + pub fn kill_window( &self, - mut window: Window, + window: Window, ) { - if let Some(client) = self.client_any(window) { - window = client.window(); + match self.client_any(window) { + Some(client) if !client.is_invincible() => { + info!("killing client with window {:#0x}", window); - if client.is_invincible() { - return; - } - } else { - return; + self.conn.kill_window(window); + self.conn.flush(); + }, + _ => {}, } - - info!("killing client with window {:#0x}", window); - - self.conn.kill_window(window); - self.conn.flush(); } + #[inline(always)] pub fn cycle_zones( - &mut self, + &self, dir: Direction, ) { - let workspace = self.active_workspace(); - let zone_manager = &self.zone_manager; - - self.workspaces - .get_mut(workspace) - .and_then(|ws| ws.cycle_zones(dir, zone_manager)); + self.workspace(self.active_workspace()) + .cycle_zones(dir, &self.zone_manager); } + #[inline(always)] pub fn cycle_focus( - &mut self, + &self, dir: Direction, ) { - let workspace = self.active_workspace(); - let client_map = &self.client_map; - let zone_manager = &self.zone_manager; - - let windows = self - .workspaces - .get_mut(workspace) - .and_then(|ws| ws.cycle_focus(dir, client_map, zone_manager)); - - if let Some((_, window)) = windows { + if let Some((_, window)) = self.workspace(self.active_workspace()).cycle_focus( + dir, + &self.client_map, + &self.zone_manager, + ) { self.focus_window(window); self.sync_focus(); } } + #[inline(always)] pub fn drag_focus( - &mut self, + &self, dir: Direction, ) { if let Some(focus) = self.focus.get() { - let workspace_index = self.active_workspace(); - self.workspaces - .get_mut(workspace_index) - .and_then(|ws| ws.drag_focus(dir)); + let workspace = self.active_workspace(); - self.apply_layout(workspace_index, false); + self.workspace(workspace).drag_focus(dir); + self.apply_layout(workspace); self.focus_window(focus); } } + #[inline(always)] pub fn rotate_clients( - &mut self, + &self, dir: Direction, ) { - let workspace_index = self.active_workspace(); - let workspace = self.workspace(workspace_index); - let next_window = workspace.next_client(dir.rev()); + let workspace = self.workspace(self.active_workspace()); - workspace.rotate_clients(dir); - self.apply_layout(workspace_index, false); - - if let Some(window) = next_window { - self.focus_window(window); + if let Some(next) = workspace.next_client(dir.rev()) { + workspace.rotate_clients(dir); + self.apply_layout(self.active_workspace()); + self.focus_window(next); } } - pub fn center_client( - &mut self, - window: Window, + #[inline] + pub fn move_focus_to_next_workspace( + &self, + dir: Direction, ) { - if let Some(client) = self.client(window) { - if self.is_free(client) { - let screen = self.partitions.active_element().unwrap().screen(); - - let center = screen - .full_region() - .from_absolute_inner_center(client.free_region().dim); - - let mut free_region = client.free_region(); - free_region.pos = center.pos; - - info!("centering client with window {:#0x}", client.window()); - - self.conn.move_window(client.frame(), center.pos); - self.client(window) - .unwrap() - .set_region(PlacementClass::Free(free_region)); - } - } - } - - pub fn center_focus(&mut self) { if let Some(focus) = self.focus.get() { - self.center_client(focus); + self.move_window_to_next_workspace(focus, dir); } } - pub fn apply_float_retain_region(&mut self) { - let workspace_index = self.active_workspace(); - let workspace = self.workspace(workspace_index); - let windows = workspace.clients(); - - windows.into_iter().for_each(|w| { - let client = self.client(w).unwrap(); - let active_region = client.active_region(); - - let client = self.client(w).unwrap(); - client.set_region(PlacementClass::Free(active_region)); - }); - - drop(self.set_layout(LayoutKind::Float)); - self.apply_layout(workspace_index, false); - } - - pub fn move_focus_to_next_workspace(&mut self) { - if let Some(focus) = self.focus.get() { - self.move_client_to_next_workspace(focus); + #[inline] + pub fn move_window_to_next_workspace( + &self, + window: Window, + dir: Direction, + ) { + if let Some(client) = self.client(window) { + self.move_client_to_next_workspace(client, dir); } } - pub fn move_focus_to_prev_workspace(&mut self) { - if let Some(focus) = self.focus.get() { - self.move_client_to_prev_workspace(focus); - } + #[inline] + pub fn move_client_to_next_workspace( + &self, + client: &Client, + dir: Direction, + ) { + self.move_client_to_workspace( + client, + Util::next_index(self.workspaces.iter(), self.active_workspace(), dir), + ); } + #[inline] pub fn move_focus_to_workspace( - &mut self, - index: Index, + &self, + to: Index, ) { if let Some(focus) = self.focus.get() { - self.move_client_to_workspace(focus, index); + self.move_window_to_workspace(focus, to); } } - pub fn move_client_to_next_workspace( - &mut self, - window: Window, - ) { - let index = self.active_workspace() + 1; - let index = index % self.workspaces.len(); - - self.move_client_to_workspace(window, index); - } - - pub fn move_client_to_prev_workspace( - &mut self, + #[inline] + fn move_window_to_workspace( + &self, window: Window, + to: Index, ) { - let index = if self.active_workspace() == 0 { - self.workspaces.len() - 1 - } else { - self.active_workspace() - 1 - }; - - self.move_client_to_workspace(window, index); + if let Some(client) = self.client(window) { + self.move_client_to_workspace(client, to); + } } fn move_client_to_workspace( - &mut self, - window: Window, - index: Index, + &self, + client: &Client, + to: Index, ) { - if index == self.active_workspace() || index >= self.workspaces.len() { - return; - } - - let (window, current_index) = match self.client(window) { - Some(client) => { - if client.is_sticky() { - return; - } else { - (client.window(), client.workspace()) - } - }, - _ => return, - }; + let (window, from) = + if to != self.active_workspace() && to < self.workspaces.len() && !client.is_sticky() { + (client.window(), client.workspace()) + } else { + return; + }; info!( "moving client with window {:#0x} to workspace {}", - window, index + window, to ); - // add client to requested workspace - let workspace = self.workspace(index); - workspace.add_client(window, &InsertPos::Back); - - // remove client from current_index workspace - let workspace = self.workspace(current_index); - workspace.remove_client(window); - - let client = self.client(window).unwrap(); - client.set_workspace(index); + client.set_workspace(to); self.unmap_client(client); - self.apply_layout(current_index, true); - self.sync_focus(); - } - - pub fn toggle_screen_struts(&mut self) { - let screen = self.active_screen_mut(); - - if screen.showing_struts() { - let struts = screen.hide_and_yield_struts(); - - for strut in struts { - self.conn.unmap_window(strut); - } - } else { - let struts = screen.show_and_yield_struts(); + self.workspace(to).add_client(window, &InsertPos::Back); + self.apply_layout(to); + self.apply_stack(to); - for strut in struts { - self.conn.map_window(strut); - } - } + self.workspace(from).remove_client(window); + self.apply_layout(from); + self.apply_stack(from); - // TODO: apply layout to workspace active on screen - let workspace_index = self.active_workspace(); - self.apply_layout(workspace_index, false); + self.sync_focus(); } + #[inline(always)] pub fn toggle_workspace(&self) { self.activate_workspace(self.prev_workspace.get()); } - pub fn activate_next_workspace(&self) { - let index = self.active_workspace() + 1; - let index = index % self.workspaces.len(); - - self.activate_workspace(index); - } - - pub fn activate_prev_workspace(&self) { - let index = if self.active_workspace() == 0 { - self.workspaces.len() - 1 - } else { - self.active_workspace() - 1 - }; - - self.activate_workspace(index); + #[inline(always)] + pub fn activate_next_workspace( + &self, + dir: Direction, + ) { + self.activate_workspace(Util::next_index( + self.workspaces.iter(), + self.active_workspace(), + dir, + )); } pub fn activate_workspace( &self, - index: Index, + to: Index, ) { - if index == self.active_workspace() || index >= self.workspaces.len() { + if to == self.active_workspace() || to >= self.workspaces.len() { return; } - info!("activating workspace {}", index); + info!("activating workspace {}", to); self.stop_moving(); self.stop_resizing(); - let prev_workspace = self.workspaces.active_index(); - self.prev_workspace.set(prev_workspace); + let from = self.workspaces.active_index(); + self.prev_workspace.set(from); - self.workspace(index) + self.workspace(to) .on_each_client(&self.client_map, |client| { if !client.is_mapped() { self.map_client(client); } }); - self.workspace(prev_workspace) + self.workspace(from) .on_each_client(&self.client_map, |client| { if client.is_mapped() && !client.is_sticky() { self.unmap_client(client); @@ -1561,13 +1329,16 @@ impl<'model> Model<'model> { }); self.sticky_clients.borrow().iter().for_each(|&window| { - self.client(window).unwrap().set_workspace(index); + self.client_unchecked(window).set_workspace(to); }); - self.workspaces.activate_for(&Selector::AtIndex(index)); - self.apply_layout(index, true); + self.conn.set_current_desktop(to); + + self.workspaces.activate_for(&Selector::AtIndex(to)); + self.apply_layout(to); + self.apply_stack(to); + self.sync_focus(); - self.conn.set_current_desktop(index); } #[inline] @@ -1575,49 +1346,45 @@ impl<'model> Model<'model> { &mut self, change: Change<u32>, ) -> Result<(), StateChangeError> { - let workspace_index = self.active_workspace(); + let workspace = self.active_workspace(); - if let Some(workspace) = self.workspaces.get(workspace_index) { - workspace.change_gap_size(change, &mut self.zone_manager)?; - } + self.workspaces[workspace].change_gap_size(change, &mut self.zone_manager)?; + self.apply_layout(workspace); + self.apply_stack(workspace); - self.apply_layout(workspace_index, true); Ok(()) } #[inline] pub fn copy_prev_layout_data(&mut self) -> Result<(), StateChangeError> { - let workspace_index = self.active_workspace(); + let workspace = self.active_workspace(); - if let Some(workspace) = self.workspaces.get(workspace_index) { - workspace.copy_prev_layout_data(&mut self.zone_manager)?; - } + self.workspaces[workspace].copy_prev_layout_data(&mut self.zone_manager)?; + self.apply_layout(workspace); + self.apply_stack(workspace); - self.apply_layout(workspace_index, true); Ok(()) } #[inline] pub fn reset_layout_data(&mut self) -> Result<(), StateChangeError> { - let workspace_index = self.active_workspace(); + let workspace = self.active_workspace(); - if let Some(workspace) = self.workspaces.get(workspace_index) { - workspace.reset_layout_data(&mut self.zone_manager)?; - } + self.workspaces[workspace].reset_layout_data(&mut self.zone_manager)?; + self.apply_layout(workspace); + self.apply_stack(workspace); - self.apply_layout(workspace_index, true); Ok(()) } #[inline] pub fn reset_gap_size(&mut self) -> Result<(), StateChangeError> { - let workspace_index = self.active_workspace(); + let workspace = self.active_workspace(); - if let Some(workspace) = self.workspaces.get(workspace_index) { - workspace.reset_gap_size(&mut self.zone_manager)?; - } + self.workspaces[workspace].reset_gap_size(&mut self.zone_manager)?; + self.apply_layout(workspace); + self.apply_stack(workspace); - self.apply_layout(workspace_index, true); Ok(()) } @@ -1626,13 +1393,12 @@ impl<'model> Model<'model> { &mut self, change: Change<u32>, ) -> Result<(), StateChangeError> { - let workspace_index = self.active_workspace(); + let workspace = self.active_workspace(); - if let Some(workspace) = self.workspaces.get(workspace_index) { - workspace.change_main_count(change, &mut self.zone_manager)?; - } + self.workspaces[workspace].change_main_count(change, &mut self.zone_manager)?; + self.apply_layout(workspace); + self.apply_stack(workspace); - self.apply_layout(workspace_index, true); Ok(()) } @@ -1641,13 +1407,12 @@ impl<'model> Model<'model> { &mut self, change: Change<f32>, ) -> Result<(), StateChangeError> { - let workspace_index = self.active_workspace(); + let workspace = self.active_workspace(); - if let Some(workspace) = self.workspaces.get(workspace_index) { - workspace.change_main_factor(change, &mut self.zone_manager)?; - } + self.workspaces[workspace].change_main_factor(change, &mut self.zone_manager)?; + self.apply_layout(workspace); + self.apply_stack(workspace); - self.apply_layout(workspace_index, true); Ok(()) } @@ -1657,25 +1422,23 @@ impl<'model> Model<'model> { edge: Edge, change: Change<i32>, ) -> Result<(), StateChangeError> { - let workspace_index = self.active_workspace(); + let workspace = self.active_workspace(); - if let Some(workspace) = self.workspaces.get(workspace_index) { - workspace.change_margin(edge, change, &mut self.zone_manager)?; - } + self.workspaces[workspace].change_margin(edge, change, &mut self.zone_manager)?; + self.apply_layout(workspace); + self.apply_stack(workspace); - self.apply_layout(workspace_index, true); Ok(()) } #[inline] pub fn reset_margin(&mut self) -> Result<(), StateChangeError> { - let workspace_index = self.active_workspace(); + let workspace = self.active_workspace(); - if let Some(workspace) = self.workspaces.get(workspace_index) { - workspace.reset_margin(&mut self.zone_manager)?; - } + self.workspaces[workspace].reset_margin(&mut self.zone_manager)?; + self.apply_layout(workspace); + self.apply_stack(workspace); - self.apply_layout(workspace_index, true); Ok(()) } @@ -1684,17 +1447,14 @@ impl<'model> Model<'model> { &mut self, kind: LayoutKind, ) -> Result<(), StateChangeError> { - let workspace_index = self.active_workspace(); - let workspace = self.workspace(workspace_index); + let workspace = self.active_workspace(); - if let Some(id) = workspace.active_focus_zone() { - info!( - "activating layout {:?} on workspace {}", - kind, workspace_index - ); + if let Some(id) = self.workspaces[workspace].active_focus_zone() { + info!("activating layout {:?} on workspace {}", kind, workspace); self.zone_manager.set_kind(id, kind)?; - self.apply_layout(workspace_index, true); + self.apply_layout(workspace); + self.apply_stack(workspace); } Ok(()) @@ -1702,858 +1462,1005 @@ impl<'model> Model<'model> { #[inline] pub fn toggle_layout(&mut self) { - let workspace_index = self.active_workspace(); - let workspace = self.workspace(workspace_index); + let workspace = self.active_workspace(); - if let Some(id) = workspace.active_focus_zone() { + if let Some(id) = self.workspaces[workspace].active_focus_zone() { let prev_kind = self.zone_manager.set_prev_kind(id); info!( "activating layout {:?} on workspace {}", - prev_kind, workspace_index + prev_kind, workspace ); - self.apply_layout(workspace_index, true); + self.apply_layout(workspace); + self.apply_stack(workspace); } } - #[inline] - pub fn toggle_in_window_focus(&mut self) { + #[inline(always)] + pub fn set_floating_focus( + &self, + toggle: Toggle, + ) { if let Some(focus) = self.focus.get() { - if let Some(client) = self.client(focus) { - let must_in_window = !client.is_in_window(); - client.set_in_window(must_in_window); - - if must_in_window { - self.unfullscreen(focus); - } else { - self.fullscreen(focus); - } - } + self.set_floating_window(focus, toggle); } } - #[inline] - pub fn toggle_invincible_focus(&mut self) { - if let Some(focus) = self.focus.get() { - if let Some(client) = self.client(focus) { - let must_invincible = !client.is_invincible(); - client.set_invincible(must_invincible); - } + #[inline(always)] + pub fn set_floating_window( + &self, + window: Window, + toggle: Toggle, + ) { + if let Some(client) = self.client(window) { + self.set_floating_client(client, toggle); } } #[inline] - pub fn toggle_producing_focus(&mut self) { - if let Some(focus) = self.focus.get() { - if let Some(client) = self.client(focus) { - let must_producing = !client.is_producing(); - client.set_producing(must_producing); - } - } + fn set_floating_client( + &self, + client: &Client, + toggle: Toggle, + ) { + info!( + "{}floating client with window {:#0x}", + if toggle.eval(client.is_floating()) { + "" + } else { + "un" + }, + client.window() + ); + + let workspace = client.workspace(); + + client.set_floating(toggle); + self.apply_layout(workspace); + self.apply_stack(workspace); } #[inline] - pub fn toggle_float_focus(&mut self) { + pub fn set_fullscreen_focus( + &self, + toggle: Toggle, + ) { if let Some(focus) = self.focus.get() { - self.toggle_float_client(focus); + self.set_fullscreen_window(focus, toggle); } } - pub fn toggle_float_client( - &mut self, + #[inline] + pub fn set_fullscreen_window( + &self, window: Window, + toggle: Toggle, ) { if let Some(client) = self.client(window) { - let active_workspace_index = client.workspace(); - let workspace_index = client.workspace(); + self.set_fullscreen_client(client, toggle); + } + } - let client = self.client(window).unwrap(); - let must_float = !client.is_floating(); + fn set_fullscreen_client( + &self, + client: &Client, + toggle: Toggle, + ) { + if toggle.eval(client.is_fullscreen()) { + self.fullscreen(client); + } else { + self.unfullscreen(client); + } + } - info!( - "{}floating client with window {:#0x}", - if must_float { "" } else { "un" }, - client.window() - ); + #[inline(always)] + fn fullscreen( + &self, + client: &Client, + ) { + if client.is_fullscreen() { + return; + } - client.set_floating(must_float); + let window = client.window(); + let workspace = client.workspace(); + info!("enabling fullscreen for client with window {:#0x}", window); - if active_workspace_index == workspace_index { - self.apply_layout(workspace_index, true); - } - } - } + self.conn + .set_window_state(window, WindowState::Fullscreen, true); - #[inline] - fn active_partition(&self) -> usize { - self.partitions.active_index() - } + client.set_fullscreen(Toggle::On); + self.apply_layout(workspace); + self.apply_stack(workspace); - #[inline] - fn active_screen(&self) -> &Screen { - self.partitions.active_element().unwrap().screen() + self.fullscreen_regions + .borrow_mut() + .insert(window, client.free_region()); } - #[inline] - fn active_screen_mut(&mut self) -> &mut Screen { - self.partitions.active_element_mut().unwrap().screen_mut() - } + #[inline(always)] + fn unfullscreen( + &self, + client: &Client, + ) { + if !client.is_fullscreen() { + return; + } - #[inline] - pub fn active_workspace(&self) -> usize { - self.workspaces.active_index() + let window = client.window(); + let workspace = client.workspace(); + info!("disabling fullscreen for client with window {:#0x}", window); + + if let Some(free_region) = self.fullscreen_regions.borrow().get(&window).cloned() { + client.set_region(PlacementClass::Free(free_region)); + } + + self.conn + .set_window_state(window, WindowState::Fullscreen, false); + + client.set_fullscreen(Toggle::Off); + self.apply_layout(workspace); + self.apply_stack(workspace); + + self.fullscreen_regions.borrow_mut().remove(&window); } - #[inline] - fn focused_client(&self) -> Option<&Client> { - self.focus - .get() - .or_else(|| self.workspace(self.active_workspace()).focused_client()) - .and_then(|id| self.client_map.get(&id)) + #[inline(always)] + pub fn set_contained_focus( + &self, + toggle: Toggle, + ) { + if let Some(focus) = self.focus.get() { + self.set_contained_window(focus, toggle); + } } - #[inline] - fn focus_window( + #[inline(always)] + pub fn set_contained_window( &self, window: Window, + toggle: Toggle, ) { if let Some(client) = self.client(window) { - self.focus(client); + self.set_contained_client(client, toggle); } } - fn focus( + #[inline] + fn set_contained_client( &self, client: &Client, + toggle: Toggle, ) { - let (window, frame) = client.windows(); + client.set_contained(toggle); + self.set_fullscreen_client(client, Toggle::from(!client.is_contained())); + } - if Some(window) == self.focus.get() { - return; + #[inline(always)] + pub fn set_invincible_focus( + &self, + toggle: Toggle, + ) { + if let Some(focus) = self.focus.get() { + self.set_invincible_window(focus, toggle); } + } - info!("focusing client with window {:#0x}", window); - - let active_workspace_index = self.active_workspace(); - let client = self.client(window); - - if !self.is_focusable(window) { - return; + #[inline(always)] + pub fn set_invincible_window( + &self, + window: Window, + toggle: Toggle, + ) { + if let Some(client) = self.client(window) { + self.set_invincible_client(client, toggle); } + } - let client = client.unwrap(); - let client_workspace_index = client.workspace(); - let id = client.zone(); - - if client_workspace_index != active_workspace_index { - self.activate_workspace(client_workspace_index); - } + #[inline(always)] + fn set_invincible_client( + &self, + client: &Client, + toggle: Toggle, + ) { + client.set_invincible(toggle); + } - if let Some(prev_focus) = self.focus.get() { - self.unfocus(prev_focus); + #[inline(always)] + pub fn set_producing_focus( + &self, + toggle: Toggle, + ) { + if let Some(focus) = self.focus.get() { + self.set_producing_window(focus, toggle); } + } - self.conn.ungrab_buttons(frame); - + #[inline(always)] + pub fn set_producing_window( + &self, + window: Window, + toggle: Toggle, + ) { if let Some(client) = self.client(window) { - client.set_focused(true); - client.set_urgent(false); + self.set_producing_client(client, toggle); } + } - self.zone_manager.activate_zone(id); - let cycle = self.zone_manager.nearest_cycle(id); - - let workspace = self.workspace(client_workspace_index); - workspace.activate_zone(cycle); - workspace.focus_client(window); - - if self.zone_manager.is_within_persisent(id) { - self.apply_layout(client_workspace_index, false); - } + #[inline(always)] + fn set_producing_client( + &self, + client: &Client, + toggle: Toggle, + ) { + client.set_producing(toggle); + } - if self.conn.get_focused_window() != window { - self.conn.focus_window(window); + #[inline] + pub fn set_iconify_focus( + &self, + toggle: Toggle, + ) { + if let Some(focus) = self.focus.get() { + self.set_iconify_window(focus, toggle); } - - self.focus.set(Some(window)); - self.redraw_client(window); - self.apply_stack(client_workspace_index); } - fn unfocus( + #[inline] + pub fn set_iconify_window( &self, window: Window, + toggle: Toggle, ) { if let Some(client) = self.client(window) { - let (window, frame) = client.windows(); - let current_pos = self.conn.get_pointer_position(); - - info!("unfocusing client with window {:#0x}", window); - - self.conn.regrab_buttons(frame); - - let client = self.client(window).unwrap(); - client.set_warp_pos(current_pos); - client.set_focused(false); - self.redraw_client(window); + self.set_iconify_client(client, toggle); } } - fn sync_focus(&self) { - let workspace_index = self.active_workspace(); - let workspace = self.workspace(workspace_index); - - if !workspace.is_empty() { - if let Some(ws_focus) = workspace.focused_client() { - if Some(ws_focus) != self.focus.get() { - self.focus_window(ws_focus); - } - } + fn set_iconify_client( + &self, + client: &Client, + toggle: Toggle, + ) { + if toggle.eval(client.is_iconified()) { + self.iconify(client); } else { - self.conn.unfocus(); - self.focus.set(None); + self.deiconify(client); } } - pub fn toggle_fullscreen_focus(&mut self) { - if let Some(focus) = self.focus.get() { - self.toggle_fullscreen_client(focus); + #[inline] + pub fn pop_deiconify(&self) { + if let Some(icon) = self.workspaces[self.active_workspace()].focused_icon() { + self.set_iconify_window(icon, Toggle::Off); } } - pub fn toggle_fullscreen_client( - &mut self, - window: Window, + #[inline] + pub fn deiconify_all( + &self, + index: Index, ) { - if let Some(client) = self.client(window) { - let must_fullscreen = !client.is_fullscreen(); + if index >= self.workspaces.len() { + warn!( + "attempting to deicony_all from nonexistent workspace {}", + index + ); + return; + } - if must_fullscreen { - self.fullscreen(window); - } else { - self.unfullscreen(window); - } + let workspace = &self.workspaces[index]; + + while let Some(icon) = workspace.focused_icon() { + self.set_iconify_window(icon, Toggle::Off); } } - pub fn jump_client( - &mut self, - criterium: &JumpCriterium, + #[inline(always)] + fn iconify( + &self, + client: &Client, ) { - let mut window = match criterium { - JumpCriterium::OnWorkspaceBySelector(index, sel) => { - let index = *index; + if client.is_iconified() { + return; + } - if index >= self.workspaces.len() { - return; - } + let window = client.window(); + let workspace = client.workspace(); - let workspace = self.workspace(index); - let window = workspace.get_client_for(sel, &self.zone_manager); + info!("iconifying client with window {:#0x}", window); - if window.is_none() { - return; - } + self.workspaces[workspace].client_to_icon(window); - window.unwrap() - }, - JumpCriterium::ByName(match_method) => { - let mut clients = self - .client_map - .iter() - .filter(|&(_, client)| { - client.is_managed() && client.name_matches(*match_method) - }) - .map(|(_, client)| client) - .collect::<Vec<&Client>>(); + self.conn + .set_icccm_window_state(window, IcccmWindowState::Iconic); - clients.sort_by_key(|&c| c.last_focused()); + client.set_iconified(Toggle::On); + self.unmap_client(client); - if let Some(client) = clients.last() { - client.window() - } else { - return; - } - }, - JumpCriterium::ByClass(match_method) => { - let mut clients = self - .client_map - .iter() - .filter(|&(_, client)| { - client.is_managed() && client.class_matches(*match_method) - }) - .map(|(_, client)| client) - .collect::<Vec<&Client>>(); + self.apply_layout(workspace); + self.apply_stack(workspace); - clients.sort_by_key(|&c| c.last_focused()); + self.sync_focus(); + } - if let Some(client) = clients.last() { - client.window() - } else { - return; - } - }, - JumpCriterium::ByInstance(match_method) => { - let mut clients = self - .client_map - .iter() - .filter(|&(_, client)| { - client.is_managed() && client.instance_matches(*match_method) - }) - .map(|(_, client)| client) - .collect::<Vec<&Client>>(); + #[inline(always)] + fn deiconify( + &self, + client: &Client, + ) { + if !client.is_iconified() { + return; + } - clients.sort_by_key(|&c| c.last_focused()); + let window = client.window(); + let workspace = client.workspace(); - if let Some(client) = clients.last() { - client.window() - } else { - return; - } - }, - JumpCriterium::ForCond(cond) => { - let mut clients = self - .client_map - .iter() - .filter(|&(_, client)| client.is_managed() && cond(client)) - .map(|(_, client)| client) - .collect::<Vec<&Client>>(); + info!("deiconifying client with window {:#0x}", window); - clients.sort_by_key(|&c| c.last_focused()); + self.workspaces[workspace].icon_to_client(window); - if let Some(client) = clients.last() { - client.window() - } else { - return; - } - }, - }; + self.conn + .set_icccm_window_state(window, IcccmWindowState::Normal); - if let Some(focus) = self.focus.get() { - if window == focus { - let jumped_from = self.jumped_from.get(); + client.set_iconified(Toggle::Off); + self.map_client(client); - if jumped_from.is_none() || jumped_from == Some(focus) { - return; - } + self.apply_layout(workspace); + self.apply_stack(workspace); - if let Some(jumped_from) = jumped_from { - window = jumped_from; - } - } + self.sync_focus(); + } - self.jumped_from.set(Some(focus)); + #[inline] + pub fn set_stick_focus( + &self, + toggle: Toggle, + ) { + if let Some(focus) = self.focus.get() { + self.set_stick_window(focus, toggle); } - - info!("jumping to client with window {:#0x}", window); - self.focus_window(window); } - fn fullscreen( - &mut self, + #[inline] + pub fn set_stick_window( + &self, window: Window, + toggle: Toggle, ) { if let Some(client) = self.client(window) { - let free_region = client.free_region(); - let window = client.window(); - - info!("enabling fullscreen for client with window {:#0x}", window); - - self.conn - .set_window_state(window, WindowState::Fullscreen, true); - self.fullscreen_regions - .borrow_mut() - .insert(window, free_region); - - let client = self.client(window).unwrap(); - client.set_fullscreen(true); - - let workspace = client.workspace(); - if workspace == self.active_workspace() { - self.apply_layout(workspace, true); - } + self.set_stick_client(client, toggle); } } - fn unfullscreen( - &mut self, - window: Window, + fn set_stick_client( + &self, + client: &Client, + toggle: Toggle, ) { - if let Some(client) = self.client(window) { - let window = client.window(); - let free_region = self - .fullscreen_regions - .borrow() - .get(&window) - .map(|&region| region); + if toggle.eval(client.is_sticky()) { + self.stick(client); + } else { + self.unstick(client); + } + } - info!("disabling fullscreen for client with window {:#0x}", window); + #[inline(always)] + fn stick( + &self, + client: &Client, + ) { + if client.is_sticky() { + return; + } - self.conn - .set_window_state(window, WindowState::Fullscreen, false); + let window = client.window(); + info!("sticking client with window {:#0x}", window); - let client = self.client(window).unwrap(); - client.set_fullscreen(false); + self.workspaces + .iter() + .filter(|workspace| workspace.number() as Index != client.workspace()) + .for_each(|workspace| workspace.add_client(window, &InsertPos::Back)); - if let Some(free_region) = free_region { - client.set_region(PlacementClass::Free(free_region)); - } + self.conn + .set_window_state(window, WindowState::Sticky, true); - let workspace = client.workspace(); + client.set_sticky(Toggle::On); + self.render_decoration(client); - if workspace == self.active_workspace() { - self.apply_layout(workspace, true); - } + self.sticky_clients.borrow_mut().insert(window); + } - self.fullscreen_regions.borrow_mut().remove(&window); + #[inline(always)] + fn unstick( + &self, + client: &Client, + ) { + if !client.is_sticky() { + return; } + + let window = client.window(); + info!("unsticking client with window {:#0x}", window); + + self.workspaces + .iter() + .filter(|workspace| workspace.number() as Index != client.workspace()) + .for_each(|workspace| { + workspace.remove_client(window); + workspace.remove_icon(window); + }); + + self.conn + .set_window_state(window, WindowState::Sticky, false); + + client.set_sticky(Toggle::Off); + self.render_decoration(client); + + self.sticky_clients.borrow_mut().remove(&window); } - pub fn toggle_stick_focus(&mut self) { - if let Some(focus) = self.focus.get() { - if let Some(client) = self.client(focus) { - if client.is_sticky() { - self.unstick(focus); - } else { - self.stick(focus); - } - } + #[inline(always)] + fn focus_window( + &self, + window: Window, + ) { + if let Some(client) = self.client(window) { + self.focus(client); } } - fn stick( + fn focus( &self, - window: Window, + client: &Client, ) { - let client = self.client(window); + let (window, frame) = match client.windows() { + windows if self.is_focusable(client) && Some(windows.0) != self.focus.get() => windows, + _ => return, + }; - if client.is_none() { - return; + info!("focusing client with window {:#0x}", window); + + if self.active_workspace() != client.workspace() { + self.activate_workspace(client.workspace()); } - let client = client.unwrap(); - let window = client.window(); - let workspace_index = client.workspace(); + let workspace = client.workspace(); - info!("enabling sticky for client with window {:#0x}", window); + if let Some(prev_focus) = self.focus.get() { + self.unfocus_window(prev_focus); + } - client.set_sticky(true); - self.conn - .set_window_state(window, WindowState::Sticky, true); - self.sticky_clients.borrow_mut().insert(window); + self.conn.ungrab_buttons(frame); - for workspace in self.workspaces.iter() { - if workspace.number() as Index != workspace_index { - workspace.add_client(window, &InsertPos::Back); - } + if let Some(client) = self.client(window) { + client.set_focused(Toggle::On); + client.set_urgent(Toggle::Off); + } + + let id = client.zone(); + let cycle = self.zone_manager.nearest_cycle(id); + self.zone_manager.activate_zone(id); + + { + let workspace = self.workspace(workspace); + workspace.activate_zone(cycle); + workspace.focus_client(window); + } + + if self.zone_manager.is_within_persisent(id) { + self.apply_layout(workspace); + } + + if self.conn.get_focused_window() != window { + self.conn.focus_window(window); } - self.redraw_client(window); + self.focus.set(Some(window)); + self.render_decoration(client); + self.apply_stack(workspace); } - fn unstick( + #[inline] + fn unfocus_window( &self, window: Window, ) { - let client = self.client(window); - - if client.is_none() { - return; + if let Some(client) = self.client(window) { + self.unfocus(client); } + } - let client = client.unwrap(); - let window = client.window(); - let workspace_index = client.workspace(); + #[inline] + fn unfocus( + &self, + client: &Client, + ) { + let (window, frame) = client.windows(); + info!("unfocusing client with window {:#0x}", window); - info!("disabling sticky for client with window {:#0x}", window); + client.set_warp_pos(self.conn.get_pointer_position()); + client.set_focused(Toggle::Off); - client.set_sticky(false); - self.conn - .set_window_state(window, WindowState::Sticky, false); + self.conn.regrab_buttons(frame); + self.render_decoration(client); + } - self.sticky_clients.borrow_mut().remove(&window); + #[inline(always)] + fn sync_focus(&self) { + let workspace = self.workspace(self.active_workspace()); - for workspace in self.workspaces.iter() { - if workspace.number() as Index != workspace_index { - workspace.remove_client(window); - workspace.remove_icon(window); + match workspace.focused_client() { + Some(focus) if Some(focus) != self.focus.get() => { + self.focus_window(focus); + }, + _ if workspace.is_empty() => { + self.conn.unfocus(); + self.focus.set(None); + }, + _ => {}, + } + } + + pub fn jump_client( + &self, + criterium: JumpCriterium, + ) { + let mut window = match criterium { + JumpCriterium::OnWorkspaceBySelector(index, &sel) if index < self.workspaces.len() => { + match self.workspaces[index].get_client_for(sel, &self.zone_manager) { + Some(window) => window, + _ => return, + } + }, + JumpCriterium::ByName(method) => { + match self + .client_map + .values() + .filter(|&client| client.is_managed() && client.name_matches(method)) + .max_by_key(|client| client.last_focused()) + { + Some(client) => client.window(), + None => return, + } + }, + JumpCriterium::ByClass(method) => { + match self + .client_map + .values() + .filter(|&client| client.is_managed() && client.class_matches(method)) + .max_by_key(|client| client.last_focused()) + { + Some(client) => client.window(), + None => return, + } + }, + JumpCriterium::ByInstance(method) => { + match self + .client_map + .values() + .filter(|&client| client.is_managed() && client.instance_matches(method)) + .max_by_key(|client| client.last_focused()) + { + Some(client) => client.window(), + None => return, + } + }, + JumpCriterium::ForCond(cond) => { + match self + .client_map + .values() + .filter(|&client| client.is_managed() && cond(client)) + .max_by_key(|client| client.last_focused()) + { + Some(client) => client.window(), + None => return, + } + }, + _ => return, + }; + + if let Some(focus) = self.focus.get() { + if window == focus { + match self.jumped_from.get() { + Some(jumped_from) if jumped_from != focus => { + window = jumped_from; + }, + _ => {}, + } } + + self.jumped_from.set(Some(focus)); } - self.redraw_client(window); + info!("jumping to client with window {:#0x}", window); + self.focus_window(window); } - pub fn iconify_focus(&mut self) { + #[inline(always)] + pub fn center_focus(&self) { if let Some(focus) = self.focus.get() { - if let Some(client) = self.client(focus) { - if !client.is_iconified() { - self.iconify(focus); - } - } + self.center_window(focus); } } - pub fn pop_deiconify(&mut self) { - let workspace_index = self.active_workspace(); - let workspace = self.workspace(workspace_index); - - if let Some(icon) = workspace.focused_icon() { - self.deiconify(icon); + #[inline(always)] + pub fn center_window( + &self, + window: Window, + ) { + if let Some(client) = self.client(window) { + self.center_client(client); } } - pub fn deiconify_all( - &mut self, - index: Index, + pub fn center_client( + &self, + client: &Client, ) { - if index >= self.workspaces.len() { - warn!("attempting to deicony_all from nonexistent workspace"); + if !self.is_free(client) { return; } - let mut workspace = self.workspace(index); - - while let Some(icon) = workspace.focused_icon() { - self.deiconify(icon); - workspace = self.workspace(index); - } - } + info!("centering client with window {:#0x}", client.window()); - fn iconify( - &mut self, - window: Window, - ) { - let client = self.client(window); + let mut region = client.free_region(); + region.pos = self + .active_screen() + .full_region() + .from_absolute_inner_center(region.dim) + .pos; - if client.is_none() { - return; - } + self.conn.move_window(client.frame(), region.pos); + client.set_region(PlacementClass::Free(region)); + } - let client = self.client(window).unwrap(); - let window = client.window(); - let workspace_index = client.workspace(); + pub fn apply_float_retain_region(&mut self) { + let workspace = self.active_workspace(); - info!("iconifying client with window {:#0x}", window); + self.workspace(workspace) + .clients() + .into_iter() + .map(|window| self.client_unchecked(window)) + .for_each(|client| client.set_region(PlacementClass::Free(client.active_region()))); - client.set_iconified(true); - self.unmap_client(client); - self.conn - .set_icccm_window_state(window, IcccmWindowState::Iconic); + self.set_layout(LayoutKind::Float).ok(); + self.apply_layout(workspace); + } - let workspace = self.workspace(workspace_index); - workspace.client_to_icon(window); - self.sync_focus(); - self.apply_layout(workspace_index, true); + #[inline(always)] + pub fn snap_focus( + &self, + edge: Edge, + ) { + if let Some(focus) = self.focus.get() { + self.snap_window(focus, edge); + } } - fn deiconify( - &mut self, + #[inline(always)] + pub fn snap_window( + &self, window: Window, + edge: Edge, ) { - let client = self.client(window); + if let Some(client) = self.client(window) { + self.snap_client(client, edge); + } + } - if client.is_none() { + fn snap_client( + &self, + client: &Client, + edge: Edge, + ) { + if !self.is_free(client) { return; } - let client = self.client(window).unwrap(); let window = client.window(); - let workspace_index = client.workspace(); - info!("deiconifying client with window {:#0x}", window); + info!( + "snapping client with window {:#0x} to edge {:?}", + window, edge + ); - client.set_iconified(false); - self.map_client(client); - self.conn - .set_icccm_window_state(window, IcccmWindowState::Normal); + let placeable_region = self.active_screen().placeable_region(); + let mut region = client.free_region(); - let workspace = self.workspace(workspace_index); - workspace.icon_to_client(window); - self.sync_focus(); - self.apply_layout(workspace_index, true); + match edge { + Edge::Left => region.pos.x = placeable_region.pos.x, + Edge::Right => { + let x = placeable_region.dim.w + placeable_region.pos.x; + region.pos.x = std::cmp::max(0, x - region.dim.w) + }, + Edge::Top => region.pos.y = placeable_region.pos.y, + Edge::Bottom => { + let y = placeable_region.dim.h + placeable_region.pos.y; + region.pos.y = std::cmp::max(0, y - region.dim.h) + }, + } + + client.set_region(PlacementClass::Free(region)); + + let placement = Placement { + method: PlacementMethod::Free, + kind: PlacementTarget::Client(window), + zone: client.zone(), + region: PlacementRegion::FreeRegion, + decoration: client.decoration(), + }; + + self.update_client_placement(client, &placement); + self.place_client(client, placement.method); } - pub fn snap_focus( - &mut self, + #[inline(always)] + pub fn nudge_focus( + &self, edge: Edge, + step: i32, ) { if let Some(focus) = self.focus.get() { - self.snap_client(focus, edge); + self.nudge_window(focus, edge, step); } } - fn snap_client( - &mut self, + #[inline(always)] + pub fn nudge_window( + &self, window: Window, edge: Edge, + step: i32, ) { if let Some(client) = self.client(window) { - if self.is_free(client) { - let screen = self.active_screen(); - let placeable_region = screen.placeable_region(); - let mut region = client.free_region(); - let window = client.window(); - - info!( - "snapping client with window {:#0x} to edge {:?}", - window, edge - ); + self.nudge_client(client, edge, step); + } + } - match edge { - Edge::Left => region.pos.x = placeable_region.pos.x, - Edge::Right => { - let x = placeable_region.dim.w as i32 + placeable_region.pos.x; + fn nudge_client( + &self, + client: &Client, + edge: Edge, + step: i32, + ) { + if !self.is_free(client) { + return; + } - region.pos.x = std::cmp::max(0, x - region.dim.w as i32) - }, - Edge::Top => region.pos.y = placeable_region.pos.y, - Edge::Bottom => { - let y = placeable_region.dim.h as i32 + placeable_region.pos.y; + let window = client.window(); - region.pos.y = std::cmp::max(0, y - region.dim.h as i32) - }, - } + info!( + "nudging client with window {:#0x} at the {:?} by {}", + window, edge, step + ); - let placement = Placement { - method: PlacementMethod::Free, - kind: PlacementTarget::Client(window), - zone: client.zone(), - region: PlacementRegion::NewRegion(region), - decoration: client.decoration(), - }; + let mut region = client.free_region(); - self.update_client_placement(&placement); - self.place_client(window, placement.method); - } + match edge { + Edge::Left => region.pos.x -= step, + Edge::Right => region.pos.x += step, + Edge::Top => region.pos.y -= step, + Edge::Bottom => region.pos.y += step, } + + client.set_region(PlacementClass::Free(region)); + + let placement = Placement { + method: PlacementMethod::Free, + kind: PlacementTarget::Client(window), + zone: client.zone(), + region: PlacementRegion::FreeRegion, + decoration: client.decoration(), + }; + + self.update_client_placement(client, &placement); + self.place_client(client, placement.method); } - pub fn nudge_focus( - &mut self, - edge: Edge, + #[inline(always)] + pub fn grow_ratio_focus( + &self, step: i32, ) { if let Some(focus) = self.focus.get() { - self.nudge_client(focus, edge, step); + self.grow_ratio_window(focus, step); } } - fn nudge_client( - &mut self, + #[inline(always)] + pub fn grow_ratio_window( + &self, window: Window, - edge: Edge, step: i32, ) { if let Some(client) = self.client(window) { - if self.is_free(client) { - let mut region = client.free_region(); - let window = client.window(); + self.grow_ratio_client(client, step); + } + } - info!( - "nudging client with window {:#0x} at the {:?} by {}", - window, edge, step - ); + fn grow_ratio_client( + &self, + client: &Client, + step: i32, + ) { + if !self.is_free(client) { + return; + } - match edge { - Edge::Left => region.pos.x -= step, - Edge::Right => region.pos.x += step, - Edge::Top => region.pos.y -= step, - Edge::Bottom => region.pos.y += step, - } + let frame_extents = client.frame_extents(); - let placement = Placement { - method: PlacementMethod::Free, - kind: PlacementTarget::Client(window), - zone: client.zone(), - region: PlacementRegion::NewRegion(region), - decoration: client.decoration(), - }; + let original_region = client.free_region(); + let region = original_region; + let (width, height) = region.dim.values(); - self.update_client_placement(&placement); - self.place_client(window, placement.method); - } + let fraction = width as f64 / (width + height) as f64; + let width_inc = fraction * step as f64; + let height_inc = step as f64 - width_inc; + let width_inc = width_inc.round() as i32; + let height_inc = height_inc.round() as i32; + + let mut region = region.without_extents(frame_extents); + + if (width_inc.is_negative() && -width_inc >= region.dim.w) + || (height_inc.is_negative() && -height_inc >= region.dim.h) + || (region.dim.w + width_inc <= Client::MIN_CLIENT_DIM.w) + || (region.dim.h + height_inc <= Client::MIN_CLIENT_DIM.h) + { + return; } - } - pub fn grow_ratio_client( - &mut self, - window: Window, - step: i32, - ) { - if let Some(client) = self.client(window) { - if self.is_free(client) { - let frame_extents = client.frame_extents(); - let original_region = client.free_region(); - let region = original_region; - let window = client.window(); - let (width, height) = region.dim.values(); - - let fraction = width as f64 / (width + height) as f64; - let width_inc = fraction * step as f64; - let height_inc = step as f64 - width_inc; - let width_inc = width_inc.round() as i32; - let height_inc = height_inc.round() as i32; - - let mut region = region.without_extents(frame_extents); - - if (width_inc.is_negative() && -width_inc >= region.dim.w) - || (height_inc.is_negative() && -height_inc >= region.dim.h) - || (region.dim.w + width_inc <= Client::MIN_CLIENT_DIM.w) - || (region.dim.h + height_inc <= Client::MIN_CLIENT_DIM.h) - { - return; - } + let window = client.window(); - info!( - "{} client with window {:#0x} by {}", - if step >= 0 { "growing" } else { "shrinking" }, - window, - step.abs() - ); + info!( + "{} client with window {:#0x} by {}", + if step >= 0 { "growing" } else { "shrinking" }, + window, + step.abs() + ); - region.dim.w = region.dim.w + width_inc; - region.dim.h = region.dim.h + height_inc; + region.dim.w += width_inc; + region.dim.h += height_inc; - let mut region = region.with_extents(frame_extents); - let dx = region.dim.w - original_region.dim.w; - let dy = region.dim.h - original_region.dim.h; + region = region.with_extents(frame_extents); + let dx = region.dim.w - original_region.dim.w; + let dy = region.dim.h - original_region.dim.h; - let width_shift = (dx as f64 / 2f64) as i32; - let height_shift = (dy as f64 / 2f64) as i32; + let width_shift = (dx as f64 / 2f64) as i32; + let height_shift = (dy as f64 / 2f64) as i32; - region.pos.x -= width_shift; - region.pos.y -= height_shift; + region.pos.x -= width_shift; + region.pos.y -= height_shift; - let placement = Placement { - method: PlacementMethod::Free, - kind: PlacementTarget::Client(window), - zone: client.zone(), - region: PlacementRegion::NewRegion(region), - decoration: client.decoration(), - }; + client.set_region(PlacementClass::Free(region)); - self.update_client_placement(&placement); - self.place_client(window, placement.method); - } - } + let placement = Placement { + method: PlacementMethod::Free, + kind: PlacementTarget::Client(window), + zone: client.zone(), + region: PlacementRegion::FreeRegion, + decoration: client.decoration(), + }; + + self.update_client_placement(client, &placement); + self.place_client(client, placement.method); } + #[inline(always)] pub fn stretch_focus( - &mut self, + &self, edge: Edge, step: i32, ) { if let Some(focus) = self.focus.get() { - self.stretch_client(focus, edge, step); + self.stretch_window(focus, edge, step); } } - fn stretch_client( - &mut self, + #[inline(always)] + pub fn stretch_window( + &self, window: Window, edge: Edge, step: i32, ) { if let Some(client) = self.client(window) { - if self.is_free(client) { - let frame_extents = client.frame_extents(); - let window = client.window(); - let mut region = client.free_region().without_extents(frame_extents); - - info!( - "stretching client with window {:#0x} at the {:?} by {}", - window, edge, step - ); + self.stretch_client(client, edge, step); + } + } - match edge { - Edge::Left => { - if step.is_negative() && -step >= region.dim.w { - return; - } + fn stretch_client( + &self, + client: &Client, + edge: Edge, + step: i32, + ) { + if !self.is_free(client) { + return; + } - if region.dim.w + step <= Client::MIN_CLIENT_DIM.w { - region.pos.x -= Client::MIN_CLIENT_DIM.w - region.dim.w; - region.dim.w = Client::MIN_CLIENT_DIM.w; - } else { - region.pos.x -= step; - region.dim.w = region.dim.w + step; - } - }, - Edge::Right => { - if step.is_negative() && -step >= region.dim.w { - return; - } + let window = client.window(); - if region.dim.w + step <= Client::MIN_CLIENT_DIM.w { - region.dim.w = Client::MIN_CLIENT_DIM.w; - } else { - region.dim.w = region.dim.w + step; - } - }, - Edge::Top => { - if step.is_negative() && -step >= region.dim.h { - return; - } + info!( + "stretching client with window {:#0x} at the {:?} by {}", + window, edge, step + ); - if region.dim.h + step <= Client::MIN_CLIENT_DIM.h { - region.pos.y -= Client::MIN_CLIENT_DIM.h - region.dim.h; - region.dim.h = Client::MIN_CLIENT_DIM.h; - } else { - region.pos.y -= step; - region.dim.h = region.dim.h + step; - } - }, - Edge::Bottom => { - if step.is_negative() && -step >= region.dim.h { - return; - } + let frame_extents = client.frame_extents(); + let mut region = client.free_region().without_extents(frame_extents); - if region.dim.h + step <= Client::MIN_CLIENT_DIM.h { - region.dim.h = Client::MIN_CLIENT_DIM.h; - } else { - region.dim.h = region.dim.h + step; - } - }, + match edge { + Edge::Left if !(step.is_negative() && -step >= region.dim.w) => { + if region.dim.w + step <= Client::MIN_CLIENT_DIM.w { + region.pos.x -= Client::MIN_CLIENT_DIM.w - region.dim.w; + region.dim.w = Client::MIN_CLIENT_DIM.w; + } else { + region.pos.x -= step; + region.dim.w += step; } - - let window = client.window(); - let region = region.with_extents(frame_extents); - let placement = Placement { - method: PlacementMethod::Free, - kind: PlacementTarget::Client(window), - zone: client.zone(), - region: PlacementRegion::NewRegion(region), - decoration: client.decoration(), - }; - - self.update_client_placement(&placement); - self.place_client(window, placement.method); - } + }, + Edge::Right if !(step.is_negative() && -step >= region.dim.w) => { + if region.dim.w + step <= Client::MIN_CLIENT_DIM.w { + region.dim.w = Client::MIN_CLIENT_DIM.w; + } else { + region.dim.w += step; + } + }, + Edge::Top if !(step.is_negative() && -step >= region.dim.h) => { + if region.dim.h + step <= Client::MIN_CLIENT_DIM.h { + region.pos.y -= Client::MIN_CLIENT_DIM.h - region.dim.h; + region.dim.h = Client::MIN_CLIENT_DIM.h; + } else { + region.pos.y -= step; + region.dim.h += step; + } + }, + Edge::Bottom if (!step.is_negative() && -step >= region.dim.h) => { + if region.dim.h + step <= Client::MIN_CLIENT_DIM.h { + region.dim.h = Client::MIN_CLIENT_DIM.h; + } else { + region.dim.h += step; + } + }, + _ => return, } + + client.set_region(PlacementClass::Free(region.with_extents(frame_extents))); + + let placement = Placement { + method: PlacementMethod::Free, + kind: PlacementTarget::Client(window), + zone: client.zone(), + region: PlacementRegion::FreeRegion, + decoration: client.decoration(), + }; + + self.update_client_placement(client, &placement); + self.place_client(client, placement.method); } pub fn start_moving( &self, window: Window, ) { - if !self.move_buffer.is_occupied() && !self.resize_buffer.is_occupied() { - if let Some(client) = self.client(window) { - let current_pos = self.conn.get_pointer_position(); - let client_region = client.free_region(); + if self.move_buffer.is_occupied() || self.resize_buffer.is_occupied() { + return; + } - self.move_buffer.set( - window, - Grip::Corner(Corner::TopLeft), - current_pos, - client_region, - ); + if let Some(client) = self.client(window) { + self.move_buffer.set( + client.window(), + Grip::Corner(Corner::TopLeft), + self.conn.get_pointer_position(), + client.free_region(), + ); - self.conn.confine_pointer(self.move_buffer.handle()); - } + self.conn.confine_pointer(self.move_buffer.handle()); } } + #[inline(always)] pub fn stop_moving(&self) { if self.move_buffer.is_occupied() { self.conn.release_pointer(); @@ -2561,51 +2468,58 @@ impl<'model> Model<'model> { } } + #[inline(always)] pub fn handle_move( - &mut self, + &self, pos: &Pos, ) { - if let Some(client) = self.client(self.move_buffer.window().unwrap()) { - if self.is_free(client) { - if let Some(grip_pos) = self.move_buffer.grip_pos() { - if let Some(window_region) = self.move_buffer.window_region() { - let window = client.window(); - let region = Region { - pos: window_region.pos + grip_pos.dist(*pos), - dim: client.free_region().dim, - }; - - let placement = Placement { - method: PlacementMethod::Free, - kind: PlacementTarget::Client(window), - zone: client.zone(), - region: PlacementRegion::NewRegion(region), - decoration: client.decoration(), - }; - - self.update_client_placement(&placement); - self.place_client(window, placement.method); - } - } - } + let client = self + .move_buffer + .window() + .and_then(|window| self.client(window)) + .unwrap(); + + if !self.is_free(client) { + return; } + + client.set_region(PlacementClass::Free(Region { + pos: self.move_buffer.window_region().unwrap().pos + + self.move_buffer.grip_pos().unwrap().dist(*pos), + dim: client.free_region().dim, + })); + + let placement = Placement { + method: PlacementMethod::Free, + kind: PlacementTarget::Client(client.window()), + zone: client.zone(), + region: PlacementRegion::FreeRegion, + decoration: client.decoration(), + }; + + self.update_client_placement(client, &placement); + self.place_client(client, placement.method); } pub fn start_resizing( &self, window: Window, ) { - if !self.move_buffer.is_occupied() && !self.resize_buffer.is_occupied() { - if let Some(client) = self.client(window) { - let current_pos = self.conn.get_pointer_position(); - let client_region = client.free_region(); - let corner = client.free_region().nearest_corner(current_pos); + if self.move_buffer.is_occupied() || self.resize_buffer.is_occupied() { + return; + } - self.resize_buffer - .set(window, Grip::Corner(corner), current_pos, client_region); + if let Some(client) = self.client(window) { + let pos = self.conn.get_pointer_position(); - self.conn.confine_pointer(self.resize_buffer.handle()); - } + self.resize_buffer.set( + client.window(), + Grip::Corner(client.free_region().nearest_corner(pos)), + pos, + client.free_region(), + ); + + self.conn.confine_pointer(self.resize_buffer.handle()); } } @@ -2616,82 +2530,183 @@ impl<'model> Model<'model> { } } + #[inline(always)] pub fn handle_resize( - &mut self, + &self, pos: &Pos, ) { - let window = self.resize_buffer.window().unwrap(); + let client = self + .resize_buffer + .window() + .and_then(|window| self.client(window)) + .unwrap(); - if let Some(client) = self.client(window) { - if self.is_free(client) { - let grip_pos = self.resize_buffer.grip_pos().unwrap(); - let window_region = self.resize_buffer.window_region().unwrap(); - let grip = self.resize_buffer.grip().unwrap(); - - let current_pos = *pos; - let previous_region = client.previous_region(); - let decoration = client.decoration(); - let (pos, mut dim) = client - .free_region() - .without_extents(decoration.extents()) - .values(); - - let top_grip = grip.is_top_grip(); - let left_grip = grip.is_left_grip(); - let delta = grip_pos.dist(current_pos); - - let dest_w = if left_grip { - window_region.dim.w - delta.dx - } else { - window_region.dim.w + delta.dx - }; + if !self.is_free(client) { + return; + } - let dest_h = if top_grip { - window_region.dim.h - delta.dy - } else { - window_region.dim.h + delta.dy - }; + let mut region = client.free_region().without_extents(client.frame_extents()); - dim.w = std::cmp::max(0, dest_w); - dim.h = std::cmp::max(0, dest_h); + let window_region = self.resize_buffer.window_region().unwrap(); + let grip = self.resize_buffer.grip().unwrap(); - if let Some(size_hints) = client.size_hints() { - size_hints.apply(&mut dim); - } + let top_grip = grip.is_top_grip(); + let left_grip = grip.is_left_grip(); + let delta = self.resize_buffer.grip_pos().unwrap().dist(pos.to_owned()); - let mut region = (Region { - pos, - dim, - }) - .with_extents(decoration.extents()); + let dest_w = if left_grip { + window_region.dim.w - delta.dx + } else { + window_region.dim.w + delta.dx + }; - if top_grip { - region.pos.y = window_region.pos.y + (window_region.dim.h - region.dim.h); - } + let dest_h = if top_grip { + window_region.dim.h - delta.dy + } else { + window_region.dim.h + delta.dy + }; - if left_grip { - region.pos.x = window_region.pos.x + (window_region.dim.w - region.dim.w); - } + region.dim.w = std::cmp::max(0, dest_w); + region.dim.h = std::cmp::max(0, dest_h); + + if let Some(size_hints) = client.size_hints() { + size_hints.apply(&mut region.dim); + } + + region = region.with_extents(client.frame_extents()); + + if top_grip { + region.pos.y = window_region.pos.y + (window_region.dim.h - region.dim.h); + } + + if left_grip { + region.pos.x = window_region.pos.x + (window_region.dim.w - region.dim.w); + } + + if region == client.previous_region() { + return; + } + + let placement = Placement { + method: PlacementMethod::Free, + kind: PlacementTarget::Client(client.window()), + zone: client.zone(), + region: PlacementRegion::NewRegion(region), + decoration: client.decoration(), + }; + + self.update_client_placement(client, &placement); + self.place_client(client, placement.method); + } + + pub fn run( + &mut self, + mut key_bindings: KeyBindings, + mut mouse_bindings: MouseBindings, + ) { + while self.running { + if let Some(event) = self.conn.step() { + trace!("received event: {:?}", event); - if region == previous_region { - return; + match event { + Event::Mouse { + event, + } => self.handle_mouse(event, &mut mouse_bindings), + Event::Key { + key_code, + } => self.handle_key(key_code, &mut key_bindings), + Event::MapRequest { + window, + ignore, + } => self.handle_map_request(window, ignore), + Event::Map { + window, + ignore, + } => self.handle_map(window, ignore), + Event::Enter { + window, + root_rpos, + window_rpos, + } => self.handle_enter(window, root_rpos, window_rpos), + Event::Leave { + window, + root_rpos, + window_rpos, + } => self.handle_leave(window, root_rpos, window_rpos), + Event::Destroy { + window, + } => self.handle_destroy(window), + Event::Expose { + window, + } => self.handle_expose(window), + Event::Unmap { + window, + ignore, + } => self.handle_unmap(window, ignore), + Event::Configure { + window, + region, + on_root, + } => self.handle_configure(window, region, on_root), + Event::StateRequest { + window, + state, + action, + on_root, + } => self.handle_state_request(window, state, action, on_root), + Event::FocusRequest { + window, + on_root, + } => self.handle_focus_request(window, on_root), + Event::CloseRequest { + window, + on_root, + } => self.handle_close_request(window, on_root), + Event::WorkspaceRequest { + window, + index, + on_root, + } => self.handle_workspace_request(window, index, on_root), + Event::PlacementRequest { + window, + pos, + dim, + on_root, + } => self.handle_placement_request(window, pos, dim, on_root), + Event::GripRequest { + window, + pos, + grip, + on_root, + } => self.handle_grip_request(window, pos, grip, on_root), + Event::RestackRequest { + window, + sibling, + mode, + on_root, + } => self.handle_restack_request(window, sibling, mode, on_root), + Event::Property { + window, + kind, + on_root, + } => self.handle_property(window, kind, on_root), + Event::FrameExtentsRequest { + window, + on_root, + } => self.handle_frame_extents_request(window, on_root), + Event::Mapping { + request, + } => self.handle_mapping(request), + Event::ScreenChange => self.handle_screen_change(), + Event::Randr => self.handle_randr(), } - - let window = client.window(); - let placement = Placement { - method: PlacementMethod::Free, - kind: PlacementTarget::Client(window), - zone: client.zone(), - region: PlacementRegion::NewRegion(region), - decoration, - }; - - self.update_client_placement(&placement); - self.place_client(window, placement.method); } + + self.conn.flush(); } } + #[inline(always)] fn handle_mouse( &mut self, event: MouseEvent, @@ -2758,7 +2773,7 @@ impl<'model> Model<'model> { kind: event.kind, target: EventTarget::Root, }, - event.shortcut.clone(), + event.shortcut, )); if let Some((action, _)) = binding { @@ -2805,6 +2820,7 @@ impl<'model> Model<'model> { } } + #[inline(always)] fn handle_key( &mut self, key_code: KeyCode, @@ -2816,6 +2832,7 @@ impl<'model> Model<'model> { } } + #[inline(always)] fn handle_map_request( &mut self, window: Window, @@ -2823,16 +2840,19 @@ impl<'model> Model<'model> { ) { debug!("MAP_REQUEST for window {:#0x}", window); + let workspace = self.active_workspace(); + if ignore { if let Some(struts) = self.conn.get_window_strut(window) { - let screen = self.active_screen_mut(); + let screen = self.active_screen(); screen.add_struts(struts); if !screen.showing_struts() { self.conn.unmap_window(window); } else { screen.compute_placeable_region(); - self.apply_layout(self.active_workspace(), true); + self.apply_layout(workspace); + self.apply_stack(workspace); } } @@ -2840,30 +2860,27 @@ impl<'model> Model<'model> { let preferred_type = self.conn.get_window_preferred_type(window); let geometry = self.conn.get_window_geometry(window); - match (preferred_state, preferred_type) { + if let Some(layer) = match (preferred_state, preferred_type) { (Some(WindowState::Below), _) => Some(StackLayer::Below), (_, WindowType::Desktop) => Some(StackLayer::Desktop), (_, WindowType::Dock) => { if let Ok(geometry) = geometry { - let screen = self.active_screen_mut(); + let screen = self.active_screen(); + let full_region = screen.full_region(); if !screen.contains_window(window) { let strut = match ( (geometry.pos.x, geometry.pos.y), (geometry.dim.w, geometry.dim.h), ) { - ((0, 0), (w, h)) if w == screen.full_region().dim.w => { - Some((Edge::Top, h)) - }, - ((0, 0), (w, h)) if h == screen.full_region().dim.h => { - Some((Edge::Left, w)) - }, + ((0, 0), (w, h)) if w == full_region.dim.w => Some((Edge::Top, h)), + ((0, 0), (w, h)) if h == full_region.dim.h => Some((Edge::Left, w)), ((0, 0), (w, h)) if w > h => Some((Edge::Top, h)), ((0, 0), (w, h)) if w < h => Some((Edge::Left, w)), - ((_, y), (_, h)) if y == screen.full_region().dim.h - h => { + ((_, y), (_, h)) if y == full_region.dim.h - h => { Some((Edge::Bottom, h)) }, - ((x, _), (w, _)) if x == screen.full_region().dim.w - w => { + ((x, _), (w, _)) if x == full_region.dim.w - w => { Some((Edge::Right, w)) }, _ => None, @@ -2876,7 +2893,8 @@ impl<'model> Model<'model> { self.conn.unmap_window(window); } else { screen.compute_placeable_region(); - self.apply_layout(self.active_workspace(), true); + self.apply_layout(workspace); + self.apply_stack(workspace); } } } @@ -2887,8 +2905,9 @@ impl<'model> Model<'model> { (_, WindowType::Notification) => Some(StackLayer::Notification), (Some(WindowState::Above), _) => Some(StackLayer::Above), (..) => None, - } - .map(|layer| self.stack_manager.add_window(window, layer)); + } { + self.stack_manager.add_window(window, layer) + }; self.apply_stack(self.active_workspace()); } @@ -2900,102 +2919,78 @@ impl<'model> Model<'model> { self.manage(window, ignore); } + #[inline] fn handle_map( - &mut self, + &self, window: Window, _ignore: bool, ) { debug!("MAP for window {:#0x}", window); } + #[inline] fn handle_enter( - &mut self, + &self, window: Window, _root_rpos: Pos, _window_rpos: Pos, ) { debug!("ENTER for window {:#0x}", window); - if let Some(window) = self.window(window) { + if let Some(client) = self.client(window) { if let Some(focus) = self.focus.get() { - if focus != window { - self.unfocus(focus); + if client.window() != focus { + self.unfocus_window(focus); + } else { + return; } } - self.focus_window(window); + self.focus(client); } } + #[inline] fn handle_leave( - &mut self, + &self, window: Window, _root_rpos: Pos, _window_rpos: Pos, ) { debug!("LEAVE for window {:#0x}", window); - self.unfocus(window); + self.unfocus_window(window); } + #[inline] fn handle_destroy( &mut self, window: Window, ) { debug!("DESTROY for window {:#0x}", window); - let active_workspace = self.active_workspace(); - let screen = self.active_screen_mut(); + let screen = self.active_screen(); if screen.has_strut_window(window) { screen.remove_window_strut(window); screen.compute_placeable_region(); - self.apply_layout(active_workspace, true); - } - - self.unmanaged_windows.borrow_mut().remove(&window); - let client = self.client_any(window); - - if client.is_none() { - return; - } - - let client = client.unwrap(); - let is_managed = client.is_managed(); - let (window, frame) = client.windows(); - - let client = self.client_any(window).unwrap(); - if client.consume_unmap_if_expecting() { - return; - } - - if !is_managed { - self.remanage(client, true); - } - - let client = self.client_any(window).unwrap(); - let workspace = client.workspace(); - - if let Ok(geometry) = self.conn.get_window_geometry(frame) { - self.conn.unparent_window(window, geometry.pos); + let workspace = self.active_workspace(); + self.apply_layout(workspace); + self.apply_stack(workspace); } - self.conn.cleanup_window(window); - self.conn.destroy_window(frame); - + self.unmanaged_windows.borrow_mut().remove(&window); self.remove_window(window); - - if workspace == active_workspace { - self.apply_layout(workspace, false); - } } + #[inline] fn handle_expose( - &mut self, + &self, _window: Window, ) { } + #[inline] fn handle_unmap( &mut self, window: Window, @@ -3010,6 +3005,7 @@ impl<'model> Model<'model> { self.handle_destroy(window); } + #[inline] fn handle_configure( &mut self, window: Window, @@ -3022,63 +3018,57 @@ impl<'model> Model<'model> { } } + #[inline] fn handle_state_request( - &mut self, + &self, window: Window, state: WindowState, action: ToggleAction, on_root: bool, ) { + let client = match self.client_any(window) { + Some(client) => client, + _ => return, + }; + debug!( "STATE_REQUEST for window {:#0x}, with state {:?} and action {:?}", window, state, action ); - let client = self.client_any(window); - - if client.is_none() { - return; - } - - let client = client.unwrap(); - match action { ToggleAction::Add => match state { - WindowState::Fullscreen => self.fullscreen(window), - WindowState::Sticky => self.stick(window), + WindowState::Fullscreen => self.fullscreen(client), + WindowState::Sticky => self.stick(client), WindowState::DemandsAttention => { - let hints = Hints { + self.conn.set_icccm_window_hints(window, Hints { urgent: true, input: None, initial_state: None, group: None, - }; - - self.conn.set_icccm_window_hints(window, hints); + }); if let Some(client) = self.client_any(window) { - client.set_urgent(true); - self.redraw_client(window); + client.set_urgent(Toggle::On); + self.render_decoration(client); } }, _ => {}, }, ToggleAction::Remove => match state { - WindowState::Fullscreen => self.unfullscreen(window), - WindowState::Sticky => self.unstick(window), + WindowState::Fullscreen => self.unfullscreen(client), + WindowState::Sticky => self.unstick(client), WindowState::DemandsAttention => { - let hints = Hints { + self.conn.set_icccm_window_hints(window, Hints { urgent: false, input: None, initial_state: None, group: None, - }; - - self.conn.set_icccm_window_hints(window, hints); + }); if let Some(client) = self.client_any(window) { - client.set_urgent(false); - self.redraw_client(window); + client.set_urgent(Toggle::Off); + self.render_decoration(client); } }, _ => {}, @@ -3100,8 +3090,9 @@ impl<'model> Model<'model> { } } + #[inline] fn handle_focus_request( - &mut self, + &self, window: Window, on_root: bool, ) { @@ -3112,8 +3103,9 @@ impl<'model> Model<'model> { } } + #[inline] fn handle_close_request( - &mut self, + &self, window: Window, on_root: bool, ) { @@ -3124,103 +3116,45 @@ impl<'model> Model<'model> { } } + #[inline] fn handle_workspace_request( - &mut self, - _window: Option<Window>, + &self, + window: Option<Window>, index: usize, on_root: bool, ) { - debug!("WORKSPACE_REQUEST for workspace {}", index); + debug!( + "WORKSPACE_REQUEST for workspace {} by window {:?}", + index, + window.map(|window| format!("{:#0x}", window)) + ); if on_root { self.activate_workspace(index); } } + #[inline] fn handle_placement_request( - &mut self, + &self, window: Window, pos: Option<Pos>, dim: Option<Dim>, _on_root: bool, ) { + if pos.is_none() && dim.is_none() { + return; + } + debug!( "PLACEMENT_REQUEST for window {:#0x} with pos {:?} and dim {:?}", window, pos, dim ); - if pos.is_some() || dim.is_some() { - let event_window = window; - - if let Some(client) = self.client(window) { - if self.is_free(client) { - let window = client.window(); - let frame_extents = client.frame_extents(); - - let region = if event_window == window { - Some(Region { - pos: if let Some(pos) = pos { - Pos { - x: pos.x - frame_extents.left as i32, - y: pos.y - frame_extents.top as i32, - } - } else { - client.free_region().pos - }, - dim: if let Some(dim) = dim { - Dim { - w: dim.w + frame_extents.left + frame_extents.right, - h: dim.h + frame_extents.top + frame_extents.bottom, - } - } else { - client.free_region().dim - }, - }) - } else { - Some(Region { - pos: if let Some(pos) = pos { - pos - } else { - client.free_region().pos - }, - dim: if let Some(dim) = dim { - dim - } else { - client.free_region().dim - }, - }) - } - .map(|region| { - if client.size_hints().is_some() { - region - .without_extents(frame_extents) - .with_size_hints(&client.size_hints()) - .with_extents(frame_extents) - } else { - region - .without_extents(frame_extents) - .with_minimum_dim(&Client::MIN_CLIENT_DIM) - .with_extents(frame_extents) - } - }); - - if let Some(region) = region { - let placement = Placement { - method: PlacementMethod::Free, - kind: PlacementTarget::Client(window), - zone: client.zone(), - region: PlacementRegion::NewRegion(region), - decoration: client.decoration(), - }; - - self.update_client_placement(&placement); - self.place_client(window, placement.method); - } - } - } else { - let geometry = self.conn.get_window_geometry(window); - - if let Ok(mut geometry) = geometry { + let client = match self.client(window) { + Some(client) if self.is_free(client) => client, + None => { + if let Ok(mut geometry) = self.conn.get_window_geometry(window) { if let Some(pos) = pos { geometry.pos = pos; } @@ -3231,12 +3165,65 @@ impl<'model> Model<'model> { self.conn.place_window(window, &geometry); } - } + + return; + }, + _ => return, + }; + + let extents = client.frame_extents(); + let region = if window == client.window() { + Some(Region { + pos: if let Some(pos) = pos { + Pos { + x: pos.x - extents.left, + y: pos.y - extents.top, + } + } else { + client.free_region().pos + }, + dim: if let Some(dim) = dim { + Dim { + w: dim.w + extents.left + extents.right, + h: dim.h + extents.top + extents.bottom, + } + } else { + client.free_region().dim + }, + }) + } else { + Some(Region { + pos: pos.unwrap_or(client.free_region().pos), + dim: dim.unwrap_or(client.free_region().dim), + }) + } + .map(|region| { + region + .without_extents(extents) + .with_size_hints(&client.size_hints()) + .with_minimum_dim(&Client::MIN_CLIENT_DIM) + .with_extents(extents) + }); + + if let Some(region) = region { + client.set_region(PlacementClass::Free(region)); + + let placement = Placement { + method: PlacementMethod::Free, + kind: PlacementTarget::Client(window), + zone: client.zone(), + region: PlacementRegion::FreeRegion, + decoration: client.decoration(), + }; + + self.update_client_placement(client, &placement); + self.place_client(client, placement.method); } } + #[inline] fn handle_grip_request( - &mut self, + &self, window: Window, pos: Pos, grip: Option<Grip>, @@ -3248,25 +3235,25 @@ impl<'model> Model<'model> { ); if let Some(grip) = grip { - // initiate resize from grip self.move_buffer.unset(); self.resize_buffer.unset(); if let Some(client) = self.client(window) { - let current_pos = self.conn.get_pointer_position(); - let client_region = client.free_region(); - - self.resize_buffer - .set(window, grip, current_pos, client_region); + self.resize_buffer.set( + client.window(), + grip, + self.conn.get_pointer_position(), + client.free_region(), + ); self.conn.confine_pointer(self.resize_buffer.handle()); } } else { - // initiate move self.start_moving(window); } } + #[inline] fn handle_restack_request( &mut self, window: Window, @@ -3287,8 +3274,9 @@ impl<'model> Model<'model> { self.apply_stack(self.active_workspace()); } + #[inline] fn handle_property( - &mut self, + &self, window: Window, kind: PropertyKind, _on_root: bool, @@ -3297,73 +3285,67 @@ impl<'model> Model<'model> { match kind { PropertyKind::Name => { - let name = self.conn.get_icccm_window_name(window); - if let Some(client) = self.client_any(window) { - client.set_name(name); + client.set_name(self.conn.get_icccm_window_name(window)); } }, PropertyKind::Class => { - let class = self.conn.get_icccm_window_class(window); - let instance = self.conn.get_icccm_window_instance(window); - if let Some(client) = self.client_any(window) { - client.set_class(class); - client.set_instance(instance); + client.set_class(self.conn.get_icccm_window_class(window)); + client.set_instance(self.conn.get_icccm_window_instance(window)); } }, PropertyKind::Size => { if let Some(client) = self.client_any(window) { let window = client.window(); - let workspace = client.workspace(); - let geometry = self.conn.get_window_geometry(window); - if geometry.is_err() { - return; + let size_hints = self + .conn + .get_icccm_window_size_hints( + window, + Some(Client::MIN_CLIENT_DIM), + &client.size_hints(), + ) + .1; + + let mut geometry = match self.conn.get_window_geometry(window) { + Ok(geometry) => geometry, + Err(_) => return, } + .with_size_hints(&size_hints) + .with_minimum_dim(&Client::MIN_CLIENT_DIM); - let frame_extents = client.frame_extents(); - let mut geometry = geometry.unwrap(); - let (_, size_hints) = self.conn.get_icccm_window_size_hints( - window, - Some(Client::MIN_CLIENT_DIM), - &client.size_hints(), - ); - - geometry = if size_hints.is_some() { - geometry.with_size_hints(&size_hints) - } else { - geometry.with_minimum_dim(&Client::MIN_CLIENT_DIM) - }; - + let extents = client.frame_extents(); geometry.pos = client.free_region().pos; - geometry.dim.w += frame_extents.left + frame_extents.right; - geometry.dim.h += frame_extents.top + frame_extents.bottom; + geometry.dim.w += extents.left + extents.right; + geometry.dim.h += extents.top + extents.bottom; - let client = self.client_any(window).unwrap(); client.set_size_hints(size_hints); client.set_region(PlacementClass::Free(geometry)); - if client.is_managed() && workspace == self.active_workspace() { - self.apply_layout(workspace, true); + if client.is_managed() { + let workspace = client.workspace(); + self.apply_layout(workspace); + self.apply_stack(workspace); } } }, PropertyKind::Strut => { if let Some(struts) = self.conn.get_window_strut(window) { - // TODO: screen of window - let screen = self.active_screen_mut(); - + let screen = self.active_screen(); screen.remove_window_strut(window); screen.add_struts(struts); screen.compute_placeable_region(); - self.apply_layout(self.active_workspace(), true); + let workspace = self.active_workspace(); + self.apply_layout(workspace); + self.apply_stack(workspace); } }, } } + #[inline] fn handle_frame_extents_request( &self, window: Window, @@ -3376,30 +3358,25 @@ impl<'model> Model<'model> { if let Some(client) = self.client_any(window) { client.frame_extents() } else { - if self.conn.must_manage_window(window) { - Decoration::FREE_DECORATION - } else { - Decoration::NO_DECORATION - } - .extents() + Decoration::NO_DECORATION.extents() }, ); } + #[inline] fn handle_mapping( - &mut self, + &self, request: u8, ) { debug!("MAPPING with request {}", request); - if self.conn.is_mapping_request(request) {} // TODO + if self.conn.is_mapping_request(request) {} } #[cold] - fn handle_screen_change(&mut self) { + fn handle_screen_change(&self) { debug!("SCREEN_CHANGE"); - - let workspace = self.partitions.active_element().unwrap().screen().number(); - self.workspaces.activate_for(&Selector::AtIndex(workspace)); + self.workspaces + .activate_for(&Selector::AtIndex(self.active_screen().number())); } #[cold] @@ -3410,19 +3387,19 @@ impl<'model> Model<'model> { #[cold] pub fn exit(&mut self) { - info!("exit called, shutting down window manager"); + info!("exit called, shutting down {}", WM_NAME!()); - for index in 0..self.workspaces.len() { - self.deiconify_all(index); - } + (0..self.workspaces.len()).for_each(|workspace| { + self.deiconify_all(workspace); + }); - for (window, client) in self.client_map.drain() { + self.client_map.iter().for_each(|(&window, client)| { self.conn.unparent_window(window, client.free_region().pos); - } - - self.running = false; + }); self.conn.cleanup(); self.conn.flush(); + + self.running = false; } } diff --git a/src/core/partition.rs b/src/core/partition.rs @@ -2,6 +2,7 @@ use crate::identify::Ident; use crate::identify::Identify; use crate::identify::Index; +use winsys::geometry::Region; use winsys::screen::Screen; #[derive(Clone)] @@ -21,20 +22,29 @@ impl Partition { } } + #[inline] pub fn screen(&self) -> &Screen { &self.screen } - pub fn screen_mut(&mut self) -> &mut Screen { - &mut self.screen - } - + #[inline] pub fn index(&self) -> Index { self.index } + + #[inline] + pub fn full_region(&self) -> Region { + self.screen.full_region() + } + + #[inline] + pub fn placeable_region(&self) -> Region { + self.screen.placeable_region() + } } impl Identify for Partition { + #[inline(always)] fn id(&self) -> Ident { self.screen.number() as Ident } diff --git a/src/core/rule.rs b/src/core/rule.rs @@ -1,5 +1,7 @@ +use crate::change::Toggle; use crate::client::Client; +#[derive(Debug)] pub struct Rules { pub float: Option<bool>, pub center: Option<bool>, @@ -11,14 +13,14 @@ pub struct Rules { impl Rules { pub fn propagate( &self, - client: &mut Client, + client: &Client, ) { if let Some(float) = self.float { - client.set_floating(float); + client.set_floating(Toggle::from(float)); } if let Some(fullscreen) = self.fullscreen { - client.set_fullscreen(fullscreen); + client.set_fullscreen(Toggle::from(fullscreen)); } if let Some(workspace) = self.workspace { @@ -30,40 +32,16 @@ impl Rules { } } - pub fn float( - &self, - must_float: &mut bool, - ) -> bool { - if let Some(float) = self.float { - *must_float = float; - return float; - } - - false + pub fn float(&self) -> bool { + self.float.map_or(false, |float| float) } - pub fn center( - &self, - must_center: &mut bool, - ) -> bool { - if let Some(center) = self.center { - *must_center = center; - return center; - } - - false + pub fn center(&self) -> bool { + self.center.map_or(false, |center| center) } - pub fn fullscreen( - &self, - must_fullscreen: &mut bool, - ) -> bool { - if let Some(fullscreen) = self.fullscreen { - *must_fullscreen = fullscreen; - return fullscreen; - } - - false + pub fn fullscreen(&self) -> bool { + self.fullscreen.map_or(false, |fullscreen| fullscreen) } } diff --git a/src/core/util.rs b/src/core/util.rs @@ -1,4 +1,5 @@ use crate::change::Change; +use crate::change::Direction; use crate::identify::Index; use winsys::input::Button; @@ -57,7 +58,7 @@ impl BuildHasher for BuildIdHasher { } } -pub struct Util {} +pub struct Util; impl Util { #[inline] @@ -70,6 +71,24 @@ impl Util { } #[inline] + pub fn next_index( + iter: impl ExactSizeIterator, + index: Index, + dir: Direction, + ) -> Index { + match dir { + Direction::Forward => (index + 1) % iter.len(), + Direction::Backward => { + if index == 0 { + iter.len() - 1 + } else { + index - 1 + } + }, + } + } + + #[inline] pub fn change_within_range<T>( min: T, max: T, @@ -163,7 +182,7 @@ impl Util { let mut constituents: Vec<&str> = s.split('-').collect(); match keycodes.get(constituents.remove(constituents.len() - 1)) { - Some(code) => { + Some(&code) => { let mask = constituents .iter() .map(|&modifier| match modifier { @@ -186,8 +205,8 @@ impl Util { .fold(0, |acc, modifier| acc | modifier); Some(KeyCode { - mask: mask as u16, - code: *code, + mask, + code, }) }, None => None, diff --git a/src/core/workspace.rs b/src/core/workspace.rs @@ -71,6 +71,7 @@ impl Buffer { } } + #[inline(always)] pub fn set( &self, window: Window, @@ -84,6 +85,7 @@ impl Buffer { self.window_region.set(Some(region)); } + #[inline(always)] pub fn unset(&self) { self.window.set(None); self.grip.set(None); @@ -91,26 +93,32 @@ impl Buffer { self.window_region.set(None); } + #[inline(always)] pub fn is_occupied(&self) -> bool { self.window.get().is_some() } + #[inline(always)] pub fn handle(&self) -> Window { self.handle } + #[inline(always)] pub fn window(&self) -> Option<Window> { self.window.get() } + #[inline(always)] pub fn grip(&self) -> Option<Grip> { self.grip.get() } + #[inline(always)] pub fn grip_pos(&self) -> Option<Pos> { self.grip_pos.get() } + #[inline(always)] pub fn set_grip_pos( &self, pos: Pos, @@ -118,10 +126,12 @@ impl Buffer { self.grip_pos.set(Some(pos)); } + #[inline(always)] pub fn window_region(&self) -> Option<Region> { self.window_region.get() } + #[inline(always)] pub fn set_window_region( &self, region: Region, @@ -165,22 +175,27 @@ impl Workspace { } } + #[inline(always)] pub fn number(&self) -> Ident { self.number } + #[inline(always)] pub fn name(&self) -> &str { &self.name } + #[inline(always)] pub fn root_zone(&self) -> ZoneId { self.root_zone } + #[inline(always)] pub fn len(&self) -> usize { self.clients.borrow().len() } + #[inline(always)] pub fn contains( &self, window: Window, @@ -188,10 +203,12 @@ impl Workspace { self.clients.borrow().contains(&window) } + #[inline(always)] pub fn is_empty(&self) -> bool { self.clients.borrow().is_empty() } + #[inline(always)] pub fn clients(&self) -> Vec<Window> { self.clients .borrow() @@ -200,6 +217,7 @@ impl Workspace { .collect::<Vec<Window>>() } + #[inline(always)] pub fn on_each_client<F>( &self, client_map: &HashMap<Window, Client, BuildIdHasher>, @@ -213,6 +231,7 @@ impl Workspace { .for_each(|window| func(client_map.get(window).unwrap())); } + #[inline(always)] pub fn on_each_client_mut<F>( &self, client_map: &HashMap<Window, Client, BuildIdHasher>, @@ -226,55 +245,61 @@ impl Workspace { .for_each(|window| func(client_map.get(window).unwrap())); } + #[inline(always)] pub fn stack(&self) -> VecDeque<Window> { self.clients.borrow().stack().clone() } + #[inline(always)] pub fn stack_after_focus(&self) -> Vec<Window> { self.clients.borrow().stack_after_focus() } + #[inline(always)] pub fn active_focus_zone(&self) -> Option<ZoneId> { self.focus_zones.borrow().active_element().copied() } + #[inline(always)] pub fn active_spawn_zone(&self) -> Option<ZoneId> { self.spawn_zones.borrow().active_element().copied() } + #[inline(always)] pub fn focused_client(&self) -> Option<Window> { self.clients.borrow().active_element().copied() } + #[inline(always)] pub fn get_client_for( &self, - sel: &ClientSelector, + sel: ClientSelector, zone_manager: &ZoneManager, ) -> Option<Window> { - let sel = match sel { - ClientSelector::AtActive => Selector::AtActive, - ClientSelector::AtMaster => { - if let Some(&id) = self.focus_zones.borrow().active_element() { - let cycle = zone_manager.nearest_cycle(id); - let cycle = zone_manager.zone(cycle); - - Selector::AtIndex(std::cmp::min( - cycle.data().unwrap().main_count as usize, - self.clients.borrow().len(), - )) - } else { - return None; - } - }, - ClientSelector::AtIndex(index) => Selector::AtIndex(*index), - ClientSelector::AtIdent(window) => Selector::AtIdent(*window as Ident), - ClientSelector::First => Selector::First, - ClientSelector::Last => Selector::Last, - }; - - self.clients.borrow().get_for(&sel).cloned() + self.clients + .borrow() + .get_for(&match sel { + ClientSelector::AtActive => Selector::AtActive, + ClientSelector::AtMaster => { + self.focus_zones.borrow().active_element().map(|&id| { + let cycle = zone_manager.nearest_cycle(id); + let cycle = zone_manager.zone(cycle); + + Selector::AtIndex(std::cmp::min( + cycle.data().unwrap().main_count as usize, + self.clients.borrow().len(), + )) + })? + }, + ClientSelector::AtIndex(index) => Selector::AtIndex(index), + ClientSelector::AtIdent(window) => Selector::AtIdent(window), + ClientSelector::First => Selector::First, + ClientSelector::Last => Selector::Last, + }) + .cloned() } + #[inline(always)] pub fn next_client( &self, dir: Direction, @@ -282,6 +307,7 @@ impl Workspace { self.clients.borrow().next_element(dir).copied() } + #[inline(always)] pub fn add_zone( &self, id: ZoneId, @@ -291,6 +317,7 @@ impl Workspace { self.spawn_zones.borrow_mut().insert_at(insert, id); } + #[inline(always)] pub fn add_client( &self, window: Window, @@ -299,6 +326,7 @@ impl Workspace { self.clients.borrow_mut().insert_at(insert, window); } + #[inline(always)] pub fn replace_client( &self, window: Window, @@ -307,44 +335,45 @@ impl Workspace { self.clients .borrow_mut() .remove_for(&Selector::AtIdent(replacement)); + self.clients .borrow_mut() .insert_at(&InsertPos::BeforeIdent(window), replacement); + self.clients .borrow_mut() .remove_for(&Selector::AtIdent(window)); } + #[inline(always)] pub fn activate_zone( &self, id: ZoneId, ) -> Option<ZoneId> { - let prev_active = match self.focus_zones.borrow().active_element() { - Some(z) => *z, - None => return None, - }; + let prev_active = self.focus_zones.borrow().active_element()?.to_owned(); self.focus_zones .borrow_mut() .activate_for(&Selector::AtIdent(id)); + Some(prev_active) } + #[inline(always)] pub fn focus_client( &self, window: Window, ) -> Option<Window> { - let prev_active = match self.clients.borrow().active_element() { - Some(c) => *c, - None => return None, - }; + let prev_active = self.clients.borrow().active_element()?.to_owned(); self.clients .borrow_mut() .activate_for(&Selector::AtIdent(window)); + Some(prev_active) } + #[inline(always)] pub fn remove_zone( &self, id: ZoneId, @@ -352,11 +381,13 @@ impl Workspace { self.focus_zones .borrow_mut() .remove_for(&Selector::AtIdent(id)); + self.spawn_zones .borrow_mut() .remove_for(&Selector::AtIdent(id)); } + #[inline(always)] pub fn remove_client( &self, window: Window, @@ -366,6 +397,7 @@ impl Workspace { .remove_for(&Selector::AtIdent(window)) } + #[inline(always)] pub fn remove_focused_client(&self) -> Option<Window> { self.clients.borrow_mut().remove_for(&Selector::AtActive) } @@ -380,59 +412,59 @@ impl Workspace { where F: Fn(&Client) -> bool, { - if !self.clients.borrow().is_empty() { - let zone = zone_manager.zone(self.root_zone); - zone.set_region(screen_region); - - let (to_ignore_ids, to_ignore_clients): (Vec<_>, Vec<_>) = self - .clients - .borrow() - .iter() - .chain(self.icons.borrow().iter()) - .map(|window| client_map.get(window).unwrap()) - .filter(|&client| ignore_filter(client)) - .map(|client| (client.zone(), client)) - .unzip(); - - zone_manager - .arrange(self.root_zone, &to_ignore_ids) - .into_iter() - .chain(to_ignore_clients.into_iter().map(|client| { - let (method, region, decoration) = - if client.is_fullscreen() && !client.is_in_window() { - ( - PlacementMethod::Tile, - PlacementRegion::NewRegion(screen_region), - Decoration::NO_DECORATION, - ) - } else if client.is_iconified() { - ( - PlacementMethod::Tile, - PlacementRegion::NoRegion, - Decoration::NO_DECORATION, - ) - } else { - ( - PlacementMethod::Free, - PlacementRegion::FreeRegion, - Decoration::FREE_DECORATION, - ) - }; - - Placement { - method, - kind: PlacementTarget::Client(client.window()), - zone: client.zone(), - region, - decoration, - } - })) - .collect() - } else { - Vec::with_capacity(0) + if self.clients.borrow().is_empty() { + return Vec::with_capacity(0); } + + zone_manager.zone(self.root_zone).set_region(screen_region); + + let (to_ignore_ids, to_ignore_clients): (Vec<_>, Vec<_>) = self + .clients + .borrow() + .iter() + .chain(self.icons.borrow().iter()) + .map(|window| client_map.get(window).unwrap()) + .filter(|&client| ignore_filter(client)) + .map(|client| (client.zone(), client)) + .unzip(); + + zone_manager + .arrange(self.root_zone, &to_ignore_ids) + .into_iter() + .chain(to_ignore_clients.into_iter().map(|client| { + let (method, region, decoration) = + if client.is_fullscreen() && !client.is_contained() { + ( + PlacementMethod::Tile, + PlacementRegion::NewRegion(screen_region), + Decoration::NO_DECORATION, + ) + } else if client.is_iconified() { + ( + PlacementMethod::Tile, + PlacementRegion::NoRegion, + Decoration::NO_DECORATION, + ) + } else { + ( + PlacementMethod::Free, + PlacementRegion::FreeRegion, + Decoration::FREE_DECORATION, + ) + }; + + Placement { + method, + kind: PlacementTarget::Client(client.window()), + zone: client.zone(), + region, + decoration, + } + })) + .collect() } + #[inline(always)] pub fn cycle_zones( &self, dir: Direction, @@ -442,18 +474,19 @@ impl Workspace { return None; } - let prev_active = *self.spawn_zones.borrow().active_element()?; - let mut now_active = *self.spawn_zones.borrow_mut().cycle_active(dir)?; + let prev_active = self.spawn_zones.borrow().active_element()?.to_owned(); + let mut now_active = self.spawn_zones.borrow_mut().cycle_active(dir)?.to_owned(); loop { if zone_manager.is_cycle(now_active) { return Some((prev_active, now_active)); } - now_active = *self.spawn_zones.borrow_mut().cycle_active(dir)?; + now_active = self.spawn_zones.borrow_mut().cycle_active(dir)?.to_owned(); } } + #[inline(always)] pub fn cycle_focus( &self, dir: Direction, @@ -464,7 +497,7 @@ impl Workspace { return None; } - let prev_active = *self.clients.borrow().active_element()?; + let prev_active = self.clients.borrow().active_element()?.to_owned(); let id = client_map.get(&prev_active).unwrap().zone(); let config = zone_manager.active_layoutconfig(id); @@ -474,7 +507,7 @@ impl Workspace { } } - let now_active = *self.clients.borrow_mut().cycle_active(dir)?; + let now_active = self.clients.borrow_mut().cycle_active(dir)?.to_owned(); if prev_active != now_active { Some((prev_active, now_active)) @@ -483,6 +516,7 @@ impl Workspace { } } + #[inline(always)] pub fn drag_focus( &self, dir: Direction, @@ -490,6 +524,7 @@ impl Workspace { self.clients.borrow_mut().drag_active(dir).copied() } + #[inline(always)] pub fn rotate_clients( &self, dir: Direction, @@ -498,9 +533,9 @@ impl Workspace { return None; } - let prev_active = *self.clients.borrow().active_element()?; + let prev_active = self.clients.borrow().active_element()?.to_owned(); self.clients.borrow_mut().rotate(dir); - let now_active = *self.clients.borrow().active_element()?; + let now_active = self.clients.borrow().active_element()?.to_owned(); if prev_active != now_active { Some((prev_active, now_active)) @@ -509,6 +544,7 @@ impl Workspace { } } + #[inline(always)] pub fn copy_prev_layout_data( &self, zone_manager: &mut ZoneManager, @@ -519,9 +555,10 @@ impl Workspace { .active_element() .ok_or(StateChangeError::EarlyStop)?; - let prev_data = *zone_manager + let prev_data = zone_manager .active_prev_data(id) - .ok_or(StateChangeError::EarlyStop)?; + .ok_or(StateChangeError::EarlyStop)? + .to_owned(); let data = zone_manager .active_data_mut(id) @@ -530,6 +567,7 @@ impl Workspace { Ok(*data = prev_data) } + #[inline(always)] pub fn reset_layout_data( &self, zone_manager: &mut ZoneManager, @@ -551,6 +589,7 @@ impl Workspace { Ok(*data = default_data) } + #[inline(always)] pub fn change_gap_size( &self, change: Change<u32>, @@ -578,6 +617,7 @@ impl Workspace { Ok(data.gap_size = new_gap_size) } + #[inline(always)] pub fn reset_gap_size( &self, zone_manager: &mut ZoneManager, @@ -599,6 +639,7 @@ impl Workspace { Ok(data.gap_size = default_data.gap_size) } + #[inline(always)] pub fn change_main_count( &self, change: Change<u32>, @@ -626,6 +667,7 @@ impl Workspace { } } + #[inline(always)] pub fn change_main_factor( &self, change: Change<f32>, @@ -655,6 +697,7 @@ impl Workspace { Ok(()) } + #[inline(always)] pub fn change_margin( &self, edge: Edge, @@ -683,7 +726,7 @@ impl Workspace { Edge::Bottom => (&mut data.margin.bottom, Layout::MAX_MARGIN.bottom), }; - let edge_changed = *edge_value + delta_change as i32; + let edge_changed = *edge_value + delta_change; let edge_changed = std::cmp::max(edge_changed, 0); let edge_changed = std::cmp::min(edge_changed, edge_max); @@ -694,6 +737,7 @@ impl Workspace { } } + #[inline(always)] pub fn reset_margin( &self, zone_manager: &mut ZoneManager, @@ -715,10 +759,12 @@ impl Workspace { Ok(data.margin = default_data.margin) } + #[inline(always)] pub fn focused_icon(&self) -> Option<Window> { self.icons.borrow().active_element().copied() } + #[inline(always)] pub fn icon_to_client( &self, window: Window, @@ -728,6 +774,7 @@ impl Workspace { } } + #[inline(always)] pub fn client_to_icon( &self, window: Window, @@ -737,6 +784,7 @@ impl Workspace { } } + #[inline(always)] pub fn add_icon( &self, window: Window, @@ -744,6 +792,7 @@ impl Workspace { self.icons.borrow_mut().insert_at(&InsertPos::Back, window); } + #[inline(always)] pub fn remove_icon( &self, window: Window, @@ -755,6 +804,7 @@ impl Workspace { } impl Identify for Workspace { + #[inline(always)] fn id(&self) -> Ident { self.number } diff --git a/src/core/zone.rs b/src/core/zone.rs @@ -49,7 +49,7 @@ pub struct Zone { impl Zone { fn next_id() -> ZoneId { - INSTANCE_COUNT.fetch_add(1, atomic::Ordering::Relaxed) as ZoneId + INSTANCE_COUNT.fetch_add(1, atomic::Ordering::Relaxed) } fn new( @@ -674,7 +674,7 @@ impl ZoneManager { } } -impl std::cmp::PartialEq<Self> for Zone { +impl PartialEq<Self> for Zone { fn eq( &self, other: &Self, @@ -685,6 +685,6 @@ impl std::cmp::PartialEq<Self> for Zone { impl Identify for Zone { fn id(&self) -> Ident { - self.id as Ident + self.id } } diff --git a/src/winsys/geometry.rs b/src/winsys/geometry.rs @@ -77,6 +77,14 @@ impl Pos { y: self.y - pos.y, } } + + pub fn is_origin(&self) -> bool { + *self + == Pos { + x: 0, + y: 0, + } + } } impl Add<Pos> for Pos { @@ -408,14 +416,18 @@ impl Region { self, dim: Dim, ) -> Self { - if dim.w > self.dim.w || dim.h > self.dim.h { - return self; - } - Self { pos: Pos { - x: self.pos.x + ((self.dim.w - dim.w) as f32 / 2f32) as i32, - y: self.pos.y + ((self.dim.h - dim.h) as f32 / 2f32) as i32, + x: if dim.w > self.dim.w { + self.pos.x + } else { + self.pos.x + ((self.dim.w - dim.w) as f32 / 2f32) as i32 + }, + y: if dim.h > self.dim.h { + self.pos.y + } else { + self.pos.y + ((self.dim.h - dim.h) as f32 / 2f32) as i32 + }, }, dim, } diff --git a/src/winsys/screen.rs b/src/winsys/screen.rs @@ -5,20 +5,21 @@ use crate::geometry::Region; use crate::geometry::Strut; use crate::window::Window; -use std::{ - collections::HashMap, - sync::atomic::{AtomicUsize, Ordering}, - vec::Vec, -}; +use std::cell::Cell; +use std::cell::RefCell; +use std::collections::HashMap; +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering; +use std::vec::Vec; #[derive(Debug, Clone)] pub struct Screen { - number: usize, - full_region: Region, - placeable_region: Region, - windows: HashMap<Window, Vec<Edge>>, - struts: HashMap<Edge, Vec<Strut>>, - showing_struts: bool, + number: Cell<usize>, + full_region: Cell<Region>, + placeable_region: Cell<Region>, + windows: RefCell<HashMap<Window, Vec<Edge>>>, + struts: RefCell<HashMap<Edge, Vec<Strut>>>, + showing_struts: Cell<bool>, } impl std::cmp::PartialEq<Self> for Screen { @@ -36,109 +37,126 @@ impl Screen { number: usize, ) -> Self { Screen::init(Self { - number, - full_region: region, - placeable_region: region, - windows: HashMap::new(), - struts: HashMap::with_capacity(4), - showing_struts: true, + number: Cell::new(number), + full_region: Cell::new(region), + placeable_region: Cell::new(region), + windows: RefCell::new(HashMap::new()), + struts: RefCell::new(HashMap::with_capacity(4)), + showing_struts: Cell::new(true), }) } - fn init(mut self) -> Self { - self.struts.insert(Edge::Left, Vec::with_capacity(1)); - self.struts.insert(Edge::Right, Vec::with_capacity(1)); - self.struts.insert(Edge::Top, Vec::with_capacity(1)); - self.struts.insert(Edge::Bottom, Vec::with_capacity(1)); + fn init(self) -> Self { + let mut struts = self.struts.borrow_mut(); + struts.insert(Edge::Left, Vec::with_capacity(1)); + struts.insert(Edge::Right, Vec::with_capacity(1)); + struts.insert(Edge::Top, Vec::with_capacity(1)); + struts.insert(Edge::Bottom, Vec::with_capacity(1)); + + drop(struts); self } + #[inline] pub fn showing_struts(&self) -> bool { - self.showing_struts + self.showing_struts.get() } + #[inline] pub fn number(&self) -> usize { - self.number + self.number.get() } + #[inline] pub fn set_number( - &mut self, + &self, number: usize, ) { - self.number = number - } - - pub fn show_and_yield_struts(&mut self) -> Vec<Window> { - self.showing_struts = true; - self.compute_placeable_region(); - - self.windows.keys().cloned().collect() + self.number.set(number) } - pub fn hide_and_yield_struts(&mut self) -> Vec<Window> { - self.showing_struts = false; + #[inline] + pub fn show_and_yield_struts( + &self, + show: bool, + ) -> Vec<Window> { + self.showing_struts.set(show); self.compute_placeable_region(); - self.windows.keys().cloned().collect() + self.windows.borrow().keys().cloned().collect() } + #[inline] pub fn full_region(&self) -> Region { - self.full_region + self.full_region.get() } + #[inline] pub fn placeable_region(&self) -> Region { - self.placeable_region + self.placeable_region.get() } + #[inline] pub fn contains_window( &self, window: Window, ) -> bool { - self.windows.contains_key(&window) + self.windows.borrow().contains_key(&window) } - pub fn compute_placeable_region(&mut self) { - let mut region = self.full_region; + #[inline] + pub fn compute_placeable_region(&self) { + let mut region = self.full_region.get(); + + if self.showing_struts.get() { + let struts = self.struts.borrow(); - if self.showing_struts { - if let Some(strut) = self.struts.get(&Edge::Left).unwrap().last() { + if let Some(strut) = struts.get(&Edge::Left).unwrap().last() { region.pos.x += strut.width as i32; region.dim.w -= strut.width as i32; } - if let Some(strut) = self.struts.get(&Edge::Right).unwrap().last() { + if let Some(strut) = struts.get(&Edge::Right).unwrap().last() { region.dim.w -= strut.width as i32; } - if let Some(strut) = self.struts.get(&Edge::Top).unwrap().last() { + if let Some(strut) = struts.get(&Edge::Top).unwrap().last() { region.pos.y += strut.width as i32; region.dim.h -= strut.width as i32; } - if let Some(strut) = self.struts.get(&Edge::Bottom).unwrap().last() { + if let Some(strut) = struts.get(&Edge::Bottom).unwrap().last() { region.dim.h -= strut.width as i32; } } - self.placeable_region = region; + self.placeable_region.set(region); } + #[inline] pub fn add_strut( - &mut self, + &self, edge: Edge, window: Window, width: u32, ) { - let strut = self.struts.get_mut(&edge).unwrap(); - let index = strut.binary_search_by(|s| s.width.cmp(&width)); + let mut struts = self.struts.borrow_mut(); + let strut = struts.get_mut(&edge).unwrap(); + let index = strut.binary_search_by(|s| s.width.cmp(&width)); strut.insert(index.unwrap_or_else(|e| e), Strut::new(window, width)); - self.windows.entry(window).or_insert(vec![edge]).push(edge); + + self.windows + .borrow_mut() + .entry(window) + .or_insert(vec![edge]) + .push(edge); } + #[inline] pub fn add_struts( - &mut self, + &self, struts: Vec<Option<Strut>>, ) { if let Some(left_strut) = struts[0] { @@ -158,20 +176,22 @@ impl Screen { } } + #[inline] pub fn remove_window_strut( - &mut self, + &self, window: Window, ) { - for (_, struts) in &mut self.struts { + self.struts.borrow_mut().iter_mut().for_each(|(_, struts)| { // a window may have strut at multiple screen edges struts.retain(|s| s.window != window); - } + }); - self.windows.remove(&window); + self.windows.borrow_mut().remove(&window); } + #[inline] pub fn update_strut( - &mut self, + &self, edge: Edge, window: Window, width: u32, @@ -180,82 +200,72 @@ impl Screen { self.add_strut(edge, window, width); } + #[inline] pub fn max_strut_val( &self, edge: Edge, ) -> Option<u32> { - match edge { - Edge::Left => { - if let Some(strut) = self.struts.get(&Edge::Left).unwrap().last() { - return Some(strut.width); - } - }, - Edge::Right => { - if let Some(strut) = self.struts.get(&Edge::Right).unwrap().last() { - return Some(strut.width); - } - }, - Edge::Top => { - if let Some(strut) = self.struts.get(&Edge::Top).unwrap().last() { - return Some(strut.width); - } - }, - Edge::Bottom => { - if let Some(strut) = self.struts.get(&Edge::Bottom).unwrap().last() { - return Some(strut.width); - } - }, - }; - - None + self.struts + .borrow() + .get(&edge) + .unwrap() + .last() + .map(|strut| strut.width) } + #[inline] pub fn has_strut_window( &self, window: Window, ) -> bool { - self.windows.contains_key(&window) + self.windows.borrow().contains_key(&window) } + #[inline] pub fn full_encompasses( &self, pos: Pos, ) -> bool { - self.full_region.encompasses(pos) + self.full_region.get().encompasses(pos) } + #[inline] pub fn placeable_encompasses( &self, pos: Pos, ) -> bool { - self.placeable_region.encompasses(pos) + self.placeable_region.get().encompasses(pos) } + #[inline] pub fn full_contains( &self, region: Region, ) -> bool { - self.full_region.contains(region) + self.full_region.get().contains(region) } + #[inline] pub fn placeable_contains( &self, region: Region, ) -> bool { - self.placeable_region.contains(region) + self.placeable_region.get().contains(region) } + #[inline] pub fn full_occludes( &self, region: Region, ) -> bool { - self.full_region.occludes(region) + self.full_region.get().occludes(region) } + #[inline] pub fn placeable_occludes( &self, region: Region, ) -> bool { - self.placeable_region.occludes(region) + self.placeable_region.get().occludes(region) } }