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 986b9e3e483242fbdb6e3d34f9d395a5782d721e
parent 5405ebc2e12856ed1fef241aad3c076a8ba76e43
Author: deurzen <m.deurzen@tum.de>
Date:   Mon, 15 Mar 2021 08:53:06 +0100

implements state change layout reapplication check

Diffstat:
Msrc/core/common.rs | 6++++++
Msrc/core/macros.rs | 8++++----
Msrc/core/model.rs | 36++++++++++++++++++++++--------------
Msrc/core/workspace.rs | 207+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Msrc/core/zone.rs | 80++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
5 files changed, 221 insertions(+), 116 deletions(-)

diff --git a/src/core/common.rs b/src/core/common.rs @@ -33,6 +33,12 @@ pub const FREE_DECORATION: Decoration = Decoration { }), }; +pub enum StateChangeError { + EarlyStop, + LimitReached, + StateUnchanged, +} + pub type Color = u32; #[derive(Debug, Copy, Clone, PartialEq, Eq)] diff --git a/src/core/macros.rs b/src/core/macros.rs @@ -2,13 +2,13 @@ macro_rules! do_internal( ($func:ident) => { Box::new(|model: &mut $crate::model::Model| { - model.$func(); + drop(model.$func()); }) as $crate::binding::KeyEvents }; ($func:ident, $($arg:expr),+) => { Box::new(move |model: &mut $crate::model::Model| { - model.$func($($arg),+); + drop(model.$func($($arg),+)); }) as $crate::binding::KeyEvents }; ); @@ -33,13 +33,13 @@ macro_rules! do_nothing( macro_rules! do_internal_mouse( ($func:ident) => { Box::new(|model: &mut $crate::model::Model, _| { - model.$func(); + drop(model.$func()); }) as $crate::binding::MouseEvents }; ($func:ident, $($arg:expr),+) => { Box::new(|model: &mut $crate::model::Model, _| { - model.$func($($arg),+); + drop(model.$func($($arg),+)); }) as $crate::binding::MouseEvents }; ); diff --git a/src/core/model.rs b/src/core/model.rs @@ -4,6 +4,7 @@ use crate::client::Client; use crate::common::Change; use crate::common::Direction; use crate::common::Index; +use crate::common::StateChangeError; use crate::common::FREE_DECORATION; use crate::common::MIN_WINDOW_DIM; use crate::common::NO_DECORATION; @@ -1579,84 +1580,91 @@ impl<'a> Model<'a> { pub fn change_gap_size( &mut self, change: Change, - ) { + ) -> Result<(), StateChangeError> { let workspace_index = self.active_workspace(); if let Some(workspace) = self.workspaces.get(workspace_index) { - workspace.change_gap_size(change, 5, &mut self.zone_manager); + workspace.change_gap_size(change, 5, &mut self.zone_manager)?; } self.apply_layout(workspace_index, true); + Ok(()) } - pub fn reset_layout(&mut self) { + pub fn reset_layout(&mut self) -> Result<(), StateChangeError> { let workspace_index = self.active_workspace(); if let Some(workspace) = self.workspaces.get(workspace_index) { - workspace.reset_layout(&mut self.zone_manager); + workspace.reset_layout(&mut self.zone_manager)?; } self.apply_layout(workspace_index, true); + Ok(()) } - pub fn reset_gap_size(&mut self) { + pub fn reset_gap_size(&mut self) -> Result<(), StateChangeError> { let workspace_index = self.active_workspace(); if let Some(workspace) = self.workspaces.get(workspace_index) { - workspace.reset_gap_size(&mut self.zone_manager); + workspace.reset_gap_size(&mut self.zone_manager)?; } self.apply_layout(workspace_index, true); + Ok(()) } pub fn change_main_count( &mut self, change: Change, - ) { + ) -> Result<(), StateChangeError> { let workspace_index = self.active_workspace(); if let Some(workspace) = self.workspaces.get(workspace_index) { - workspace.change_main_count(change, &mut self.zone_manager); + workspace.change_main_count(change, &mut self.zone_manager)?; } self.apply_layout(workspace_index, true); + Ok(()) } pub fn change_main_factor( &mut self, change: Change, - ) { + ) -> Result<(), StateChangeError> { let workspace_index = self.active_workspace(); if let Some(workspace) = self.workspaces.get(workspace_index) { - workspace.change_main_factor(change, 0.05f32, &mut self.zone_manager); + workspace.change_main_factor(change, 0.05f32, &mut self.zone_manager)?; } self.apply_layout(workspace_index, true); + Ok(()) } pub fn change_margin( &mut self, edge: Edge, change: Change, - ) { + ) -> Result<(), StateChangeError> { let workspace_index = self.active_workspace(); if let Some(workspace) = self.workspaces.get(workspace_index) { - workspace.change_margin(edge, change, 5, &mut self.zone_manager); + workspace.change_margin(edge, change, 5, &mut self.zone_manager)?; } self.apply_layout(workspace_index, true); + Ok(()) } - pub fn reset_margin(&mut self) { + pub fn reset_margin(&mut self) -> Result<(), StateChangeError> { let workspace_index = self.active_workspace(); if let Some(workspace) = self.workspaces.get(workspace_index) { - workspace.reset_margin(&mut self.zone_manager); + workspace.reset_margin(&mut self.zone_manager)?; } self.apply_layout(workspace_index, true); + Ok(()) } pub fn set_layout( diff --git a/src/core/workspace.rs b/src/core/workspace.rs @@ -4,6 +4,7 @@ use crate::common::Direction; use crate::common::Ident; use crate::common::Identify; use crate::common::Index; +use crate::common::StateChangeError; use crate::common::FREE_DECORATION; use crate::common::NO_DECORATION; use crate::cycle::Cycle; @@ -437,14 +438,21 @@ impl Workspace { pub fn reset_layout( &self, zone_manager: &mut ZoneManager, - ) { - if let Some(&id) = self.zones.active_element() { - if let Some(default_data) = zone_manager.active_default_data(id) { - if let Some(data) = zone_manager.active_data_mut(id) { - *data = default_data; - } - } - } + ) -> Result<(), StateChangeError> { + let &id = self + .zones + .active_element() + .ok_or(StateChangeError::EarlyStop)?; + + let default_data = zone_manager + .active_default_data(id) + .ok_or(StateChangeError::EarlyStop)?; + + let data = zone_manager + .active_data_mut(id) + .ok_or(StateChangeError::EarlyStop)?; + + Ok(*data = default_data) } pub fn change_gap_size( @@ -452,42 +460,71 @@ impl Workspace { change: Change, delta: u32, zone_manager: &mut ZoneManager, - ) { - if let Some(&id) = self.zones.active_element() { - if let Some(data) = zone_manager.active_data_mut(id) { - data.gap_size = match change { - Change::Inc => std::cmp::min(data.gap_size + delta, MAX_GAP_SIZE), - Change::Dec => std::cmp::max(data.gap_size as i32 - delta as i32, 0) as u32, - }; - } + ) -> Result<(), StateChangeError> { + let &id = self + .zones + .active_element() + .ok_or(StateChangeError::EarlyStop)?; + + let data = zone_manager + .active_data_mut(id) + .ok_or(StateChangeError::EarlyStop)?; + + let new_gap_size = match change { + Change::Inc => std::cmp::min(data.gap_size + delta, MAX_GAP_SIZE), + Change::Dec => std::cmp::max(data.gap_size as i32 - delta as i32, 0) as u32, + }; + + if new_gap_size == data.gap_size { + return Err(StateChangeError::LimitReached); } + + Ok(data.gap_size = new_gap_size) } pub fn reset_gap_size( &self, zone_manager: &mut ZoneManager, - ) { - if let Some(&id) = self.zones.active_element() { - if let Some(default_data) = zone_manager.active_default_data(id) { - if let Some(data) = zone_manager.active_data_mut(id) { - data.gap_size = default_data.gap_size; - } - } - } + ) -> Result<(), StateChangeError> { + let &id = self + .zones + .active_element() + .ok_or(StateChangeError::EarlyStop)?; + + let default_data = zone_manager + .active_default_data(id) + .ok_or(StateChangeError::EarlyStop)?; + + let data = zone_manager + .active_data_mut(id) + .ok_or(StateChangeError::EarlyStop)?; + + Ok(data.gap_size = default_data.gap_size) } pub fn change_main_count( &self, change: Change, zone_manager: &mut ZoneManager, - ) { - if let Some(&id) = self.zones.active_element() { - if let Some(data) = zone_manager.active_data_mut(id) { - data.main_count = match change { - Change::Inc => std::cmp::min(data.main_count + 1, MAX_MAIN_COUNT), - Change::Dec => std::cmp::max(data.main_count as i32 - 1, 0) as u32, - }; - } + ) -> Result<(), StateChangeError> { + let &id = self + .zones + .active_element() + .ok_or(StateChangeError::EarlyStop)?; + + let data = zone_manager + .active_data_mut(id) + .ok_or(StateChangeError::EarlyStop)?; + + let new_main_count = match change { + Change::Inc => std::cmp::min(data.main_count + 1, MAX_MAIN_COUNT), + Change::Dec => std::cmp::max(data.main_count as i32 - 1, 0) as u32, + }; + + if data.main_count == new_main_count { + Err(StateChangeError::LimitReached) + } else { + Ok(data.main_count = new_main_count) } } @@ -496,21 +533,28 @@ impl Workspace { change: Change, delta: f32, zone_manager: &mut ZoneManager, - ) { - if let Some(&id) = self.zones.active_element() { - if let Some(data) = zone_manager.active_data_mut(id) { - match change { - Change::Inc => data.main_factor += delta, - Change::Dec => data.main_factor -= delta, - } + ) -> Result<(), StateChangeError> { + let &id = self + .zones + .active_element() + .ok_or(StateChangeError::EarlyStop)?; + + let data = zone_manager + .active_data_mut(id) + .ok_or(StateChangeError::EarlyStop)?; + + match change { + Change::Inc => data.main_factor += delta, + Change::Dec => data.main_factor -= delta, + } - if data.main_factor < 0.05f32 { - data.main_factor = 0.05f32; - } else if data.main_factor > 0.95f32 { - data.main_factor = 0.95f32; - } - } + if data.main_factor < 0.05f32 { + data.main_factor = 0.05f32; + } else if data.main_factor > 0.95f32 { + data.main_factor = 0.95f32; } + + Ok(()) } pub fn change_margin( @@ -519,40 +563,57 @@ impl Workspace { change: Change, delta: u32, zone_manager: &mut ZoneManager, - ) { - if let Some(&id) = self.zones.active_element() { - if let Some(data) = zone_manager.active_data_mut(id) { - let delta_change = match change { - Change::Inc => delta as i32, - Change::Dec => -(delta as i32), - }; - - let (edge_value, edge_max) = match edge { - Edge::Left => (&mut data.margin.left, MAX_MARGIN.left), - Edge::Right => (&mut data.margin.right, MAX_MARGIN.right), - Edge::Top => (&mut data.margin.top, MAX_MARGIN.top), - Edge::Bottom => (&mut data.margin.bottom, MAX_MARGIN.bottom), - }; - - let edge_changed = *edge_value as i32 + delta_change; - let edge_changed = std::cmp::max(edge_changed, 0); - let edge_changed = std::cmp::min(edge_changed, edge_max as i32); - *edge_value = edge_changed as u32; - } + ) -> Result<(), StateChangeError> { + let &id = self + .zones + .active_element() + .ok_or(StateChangeError::EarlyStop)?; + + let data = zone_manager + .active_data_mut(id) + .ok_or(StateChangeError::EarlyStop)?; + + let delta_change = match change { + Change::Inc => delta as i32, + Change::Dec => -(delta as i32), + }; + + let (edge_value, edge_max) = match edge { + Edge::Left => (&mut data.margin.left, MAX_MARGIN.left), + Edge::Right => (&mut data.margin.right, MAX_MARGIN.right), + Edge::Top => (&mut data.margin.top, MAX_MARGIN.top), + Edge::Bottom => (&mut data.margin.bottom, MAX_MARGIN.bottom), + }; + + let edge_changed = *edge_value as i32 + delta_change; + let edge_changed = std::cmp::max(edge_changed, 0); + let edge_changed = std::cmp::min(edge_changed, edge_max as i32); + + if *edge_value == edge_changed as u32 { + Err(StateChangeError::LimitReached) + } else { + Ok(*edge_value = edge_changed as u32) } } pub fn reset_margin( &self, zone_manager: &mut ZoneManager, - ) { - if let Some(&id) = self.zones.active_element() { - if let Some(default_data) = zone_manager.active_default_data(id) { - if let Some(data) = zone_manager.active_data_mut(id) { - data.margin = default_data.margin; - } - } - } + ) -> Result<(), StateChangeError> { + let &id = self + .zones + .active_element() + .ok_or(StateChangeError::EarlyStop)?; + + let default_data = zone_manager + .active_default_data(id) + .ok_or(StateChangeError::EarlyStop)?; + + let data = zone_manager + .active_data_mut(id) + .ok_or(StateChangeError::EarlyStop)?; + + Ok(data.margin = default_data.margin) } pub fn focused_icon(&self) -> Option<Window> { diff --git a/src/core/zone.rs b/src/core/zone.rs @@ -36,6 +36,11 @@ pub const MAX_MARGIN: Padding = Padding { bottom: 400, }; +const MIN_ZONE_DIM: Dim = Dim { + w: 25, + h: 25, +}; + static INSTANCE_COUNT: atomic::AtomicU32 = atomic::AtomicU32::new(1); fn next_id() -> ZoneId { INSTANCE_COUNT.fetch_add(1, atomic::Ordering::Relaxed) as ZoneId @@ -133,20 +138,6 @@ pub enum LayoutKind { Vert = b'V', } -#[inline] -fn stack_split( - n: usize, - n_main: u32, -) -> (u32, u32) { - let n = n as u32; - - if n <= n_main { - (n, 0) - } else { - (n_main, n - n_main) - } -} - impl LayoutKind { pub fn symbol(&self) -> char { (*self as u8) as char @@ -329,6 +320,20 @@ impl LayoutKind { } } + #[inline] + fn stack_split( + n: usize, + n_main: u32, + ) -> (u32, u32) { + let n = n as u32; + + if n <= n_main { + (n, 0) + } else { + (n_main, n - n_main) + } + } + fn func(&self) -> LayoutFn { match *self { LayoutKind::Float => { @@ -348,7 +353,7 @@ impl LayoutKind { return vec![(Disposition::Changed(*region, NO_DECORATION), true)]; } - let (n_main, n_stack) = stack_split(n, data.main_count); + let (n_main, n_stack) = Self::stack_split(n, data.main_count); let h_stack = if n_stack > 0 { dim.h / n_stack } else { 0 }; let h_main = if n_main > 0 { dim.h / n_main } else { 0 }; @@ -559,6 +564,7 @@ pub struct Layout { } impl Layout { + #[inline] pub fn new() -> Self { let kind = LayoutKind::Stack; let mut data = HashMap::with_capacity(LayoutKind::COUNT); @@ -574,6 +580,7 @@ impl Layout { } } + #[inline] pub fn with_kind(kind: LayoutKind) -> Self { let mut data = HashMap::with_capacity(LayoutKind::COUNT); @@ -588,22 +595,27 @@ impl Layout { } } + #[inline] fn get_config(&self) -> LayoutConfig { self.kind.config() } + #[inline] fn get_data(&self) -> &LayoutData { self.data.get(&self.kind).unwrap() } + #[inline] fn get_data_mut(&mut self) -> &mut LayoutData { self.data.get_mut(&self.kind).unwrap() } + #[inline] fn get_default_data(&self) -> LayoutData { self.kind.default_data() } + #[inline] fn set_kind( &mut self, kind: LayoutKind, @@ -612,6 +624,7 @@ impl Layout { self.kind = kind; } + #[inline] fn adjust_for_margin( region: Region, extents: &Extents, @@ -628,14 +641,31 @@ impl Layout { } } + #[inline] fn adjust_for_gap_size( region: &mut Region, gap_size: u32, + min_dim: &Dim, ) { - region.pos.x += gap_size as i32; - region.pos.y += gap_size as i32; - region.dim.w -= 2 * gap_size; - region.dim.h -= 2 * gap_size; + let dim_gap = 2 * gap_size as i32; + + let new_w = region.dim.w as i32 - dim_gap; + if new_w < min_dim.w as i32 { + region.pos.x += ((region.dim.w as i32 - min_dim.w as i32) as f32 / 2f32) as i32; + region.dim.w = min_dim.w; + } else { + region.dim.w = new_w as u32; + region.pos.x += gap_size as i32; + } + + let new_h = region.dim.h as i32 - dim_gap; + if new_h < min_dim.h as i32 { + region.pos.y += ((region.dim.h as i32 - min_dim.h as i32) as f32 / 2f32) as i32; + region.dim.h = min_dim.h; + } else { + region.dim.h = new_h as u32; + region.pos.y += gap_size as i32; + } } } @@ -653,24 +683,24 @@ impl Apply for Layout { region: Region, active_map: Vec<bool>, ) -> (PlacementMethod, Vec<(Disposition, bool)>) { - let method = self.kind.config().method; + let config = self.kind.config(); let data = self.get_data(); - let region = if method == PlacementMethod::Free { - region - } else { + let region = if config.gap { Self::adjust_for_margin(region, &data.margin) + } else { + region }; ( - method, + config.method, (self.kind.func())(&region, &data, active_map) .into_iter() .map(|(mut disposition, is_visible)| { match disposition { Disposition::Unchanged => {}, Disposition::Changed(ref mut region, _) => { - Self::adjust_for_gap_size(region, data.gap_size); + Self::adjust_for_gap_size(region, data.gap_size, &MIN_ZONE_DIM); }, }