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 e2101e4f7f1472c31d871f482ac4275ebec61d7c
parent 7c99b99a80114faf46f4a93b5c3e6cd1099d8e3f
Author: deurzen <m.deurzen@tum.de>
Date:   Sat, 20 Mar 2021 02:05:56 +0100

layout handling refactoring

Diffstat:
Msrc/core/binding.rs | 2+-
Asrc/core/change.rs | 37+++++++++++++++++++++++++++++++++++++
Msrc/core/client.rs | 735+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Dsrc/core/common.rs | 213-------------------------------------------------------------------------------
Msrc/core/consume.rs | 5+++--
Msrc/core/cycle.rs | 8++++----
Asrc/core/decoration.rs | 124+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/core/defaults.rs | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/core/error.rs | 7+++++++
Asrc/core/identify.rs | 14++++++++++++++
Msrc/core/jump.rs | 2+-
Asrc/core/layout.rs | 955+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/core/macros.rs | 2+-
Msrc/core/main.rs | 68+++++++++++++++++++++++++++++++++++++++++++-------------------------
Msrc/core/model.rs | 227+++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/core/partition.rs | 6+++---
Asrc/core/placement.rs | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/core/stack.rs | 2+-
Msrc/core/util.rs | 22+++++++++++++++-------
Msrc/core/workspace.rs | 142++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/core/zone.rs | 1086+++++++------------------------------------------------------------------------
Dsrc/winsys/common.rs | 1073-------------------------------------------------------------------------------
Msrc/winsys/connection.rs | 40+++++++++++++---------------------------
Msrc/winsys/event.rs | 14+++++++-------
Asrc/winsys/geometry.rs | 801+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/winsys/hints.rs | 201+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/winsys/input.rs | 26++++++++++++++++++++++++--
Asrc/winsys/macros.rs | 12++++++++++++
Msrc/winsys/mod.rs | 7++++++-
Msrc/winsys/screen.rs | 20++++++++++----------
Asrc/winsys/window.rs | 42++++++++++++++++++++++++++++++++++++++++++
Msrc/winsys/xdata/event.rs | 10+++++-----
Msrc/winsys/xdata/input.rs | 7+++----
Msrc/winsys/xdata/xconnection.rs | 718+++++++++++++++++++++++++++++++++++--------------------------------------------
34 files changed, 3762 insertions(+), 2982 deletions(-)

diff --git a/src/core/binding.rs b/src/core/binding.rs @@ -1,9 +1,9 @@ use crate::model::Model; -use winsys::common::Window; use winsys::input::KeyCode; use winsys::input::MouseEventKey; use winsys::input::MouseShortcut; +use winsys::window::Window; use std::collections::HashMap; diff --git a/src/core/change.rs b/src/core/change.rs @@ -0,0 +1,37 @@ +use crate::decoration::Decoration; + +use winsys::geometry::Region; + +use std::ops::Add; +use std::ops::Mul; +use std::ops::Sub; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum Direction { + Forward, + Backward, +} + +impl Direction { + pub fn rev(&self) -> Self { + match self { + Self::Forward => Self::Backward, + Self::Backward => Self::Forward, + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum Change<T> +where + T: Add<Output = T> + Sub<Output = T> + Mul<Output = T>, +{ + Inc(T), + Dec(T), +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum Disposition { + Unchanged(Decoration), + Changed(Region, Decoration), +} diff --git a/src/core/client.rs b/src/core/client.rs @@ -1,17 +1,19 @@ -use crate::common::Decoration; -use crate::common::Ident; -use crate::common::Identify; +use crate::decoration::Decoration; +use crate::identify::Ident; +use crate::identify::Identify; +use crate::placement::PlacementClass; use crate::zone::ZoneId; -use winsys::common::Extents; -use winsys::common::Hex32; -use winsys::common::Pid; -use winsys::common::Pos; -use winsys::common::Region; -use winsys::common::SizeHints; -use winsys::common::Window; -use winsys::common::WindowType; +use winsys::connection::Pid; +use winsys::geometry::Extents; +use winsys::geometry::Pos; +use winsys::geometry::Region; +use winsys::hints::SizeHints; +use winsys::window::Window; +use winsys::window::WindowType; +use std::cell::Ref; +use std::cell::RefCell; use std::time::SystemTime; pub struct Client { @@ -681,6 +683,17 @@ impl PartialEq for Client { } } +pub struct Hex32(pub u32); + +impl std::fmt::Debug for Hex32 { + fn fmt( + &self, + f: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + write!(f, "{:#0x}", &self.0) + } +} + impl std::fmt::Debug for Client { fn fmt( &self, @@ -735,3 +748,703 @@ impl std::fmt::Debug for Client { .finish() } } + +// ----------------------------------------------------------- + +pub struct Client2 { + zone: ZoneId, + window: Window, + frame: Window, + name: RefCell<String>, + class: RefCell<String>, + instance: RefCell<String>, + context: RefCell<usize>, + workspace: RefCell<usize>, + window_type: WindowType, + active_region: RefCell<Region>, + previous_region: RefCell<Region>, + inner_region: RefCell<Region>, + free_region: RefCell<Region>, + tile_region: RefCell<Region>, + decoration: RefCell<Decoration>, + size_hints: RefCell<Option<SizeHints>>, + warp_pos: RefCell<Option<Pos>>, + parent: Option<Window>, + children: RefCell<Vec<Window>>, + leader: Option<Window>, + producer: Option<Window>, + consumers: RefCell<Vec<Window>>, + focused: RefCell<bool>, + mapped: RefCell<bool>, + managed: RefCell<bool>, + in_window: RefCell<bool>, + floating: RefCell<bool>, + fullscreen: RefCell<bool>, + iconified: RefCell<bool>, + disowned: RefCell<bool>, + sticky: RefCell<bool>, + invincible: RefCell<bool>, + urgent: RefCell<bool>, + consuming: RefCell<bool>, + producing: RefCell<bool>, + pid: Option<Pid>, + ppid: Option<Pid>, + last_focused: RefCell<SystemTime>, + managed_since: SystemTime, + expected_unmap_count: RefCell<u8>, +} + +impl<'client> Identify for Client2 { + fn id(&self) -> Ident { + self.window as Ident + } +} + +impl<'client> Client2 { + pub fn new( + zone: ZoneId, + window: Window, + frame: Window, + name: impl Into<String>, + class: impl Into<String>, + instance: impl Into<String>, + window_type: WindowType, + pid: Option<Pid>, + ppid: Option<Pid>, + ) -> Self { + Self { + zone, + window, + frame, + name: RefCell::new(name.into()), + class: RefCell::new(class.into()), + instance: RefCell::new(instance.into()), + context: RefCell::new(0), + workspace: RefCell::new(0), + window_type, + active_region: RefCell::new(Default::default()), + previous_region: RefCell::new(Default::default()), + inner_region: RefCell::new(Default::default()), + free_region: RefCell::new(Default::default()), + tile_region: RefCell::new(Default::default()), + decoration: RefCell::new(Default::default()), + size_hints: RefCell::new(None), + warp_pos: RefCell::new(None), + parent: None, + children: RefCell::new(Vec::new()), + leader: None, + producer: None, + consumers: RefCell::new(Vec::new()), + focused: RefCell::new(false), + mapped: RefCell::new(false), + managed: RefCell::new(true), + in_window: RefCell::new(false), + floating: RefCell::new(false), + fullscreen: RefCell::new(false), + iconified: RefCell::new(false), + disowned: RefCell::new(false), + sticky: RefCell::new(false), + invincible: RefCell::new(false), + urgent: RefCell::new(false), + consuming: RefCell::new(false), + producing: RefCell::new(true), + pid, + ppid, + last_focused: RefCell::new(SystemTime::now()), + managed_since: SystemTime::now(), + expected_unmap_count: RefCell::new(0), + } + } + + #[inline] + pub fn zone(&self) -> ZoneId { + self.zone + } + + #[inline] + pub fn windows(&self) -> (Window, Window) { + (self.window, self.frame) + } + + #[inline] + pub fn window(&self) -> Window { + self.window + } + + #[inline] + pub fn frame(&self) -> Window { + self.frame + } + + #[inline] + pub fn name(&'client self) -> Ref<'client, String> { + self.name.borrow() + } + + #[inline] + pub fn set_name( + &self, + name: impl Into<String>, + ) { + self.name.replace(name.into()); + } + + #[inline] + pub fn class(&'client self) -> Ref<'client, String> { + self.class.borrow() + } + + #[inline] + pub fn set_class( + &self, + class: impl Into<String>, + ) { + self.class.replace(class.into()); + } + + #[inline] + pub fn instance(&'client self) -> Ref<'client, String> { + self.instance.borrow() + } + + #[inline] + pub fn set_instance( + &self, + instance: impl Into<String>, + ) { + self.instance.replace(instance.into()); + } + + #[inline] + pub fn context(&self) -> usize { + self.context.borrow().clone() + } + + #[inline] + pub fn set_context( + &self, + context: usize, + ) { + self.context.replace(context); + } + + #[inline] + pub fn workspace(&self) -> usize { + self.workspace.borrow().clone() + } + + #[inline] + pub fn set_workspace( + &self, + workspace: usize, + ) { + self.workspace.replace(workspace); + } + + #[inline] + pub fn window_type(&self) -> WindowType { + self.window_type + } + + #[inline] + pub fn free_region(&self) -> Region { + self.free_region.borrow().clone() + } + + #[inline] + pub fn tile_region(&self) -> Region { + self.tile_region.borrow().clone() + } + + #[inline] + pub fn active_region(&self) -> Region { + self.active_region.borrow().clone() + } + + #[inline] + pub fn previous_region(&self) -> Region { + self.previous_region.borrow().clone() + } + + #[inline] + pub fn inner_region(&self) -> Region { + self.inner_region.borrow().clone() + } + + #[inline] + fn set_active_region( + &self, + active_region: Region, + ) { + self.previous_region + .replace(self.active_region.borrow().clone()); + self.active_region.replace(active_region); + self.set_inner_region(active_region); + } + + #[inline] + fn set_inner_region( + &self, + active_region: Region, + ) { + self.inner_region.replace( + if let Some(frame) = self.decoration.borrow().clone().frame { + let mut inner_region = active_region - frame.extents; + + inner_region.pos.x = frame.extents.left; + inner_region.pos.y = frame.extents.top; + + inner_region.dim.w = active_region.dim.w - frame.extents.left - frame.extents.right; + inner_region.dim.h = active_region.dim.h - frame.extents.top - frame.extents.bottom; + + inner_region + } else { + let mut inner_region = active_region; + + inner_region.pos.x = 0; + inner_region.pos.y = 0; + + inner_region + }, + ); + } + + #[inline] + pub fn set_region( + &self, + region: PlacementClass<Region>, + ) { + match region { + PlacementClass::Free(region) => { + self.free_region.replace(region); + }, + PlacementClass::Tile(region) => { + self.tile_region.replace(region); + }, + } + } + + #[inline] + pub fn decoration(&self) -> Decoration { + self.decoration.borrow().clone() + } + + #[inline] + pub fn frame_extents(&self) -> Extents { + Extents { + left: 0, + right: 0, + top: 0, + bottom: 0, + } + self.decoration.borrow().clone() + } + + #[inline] + pub fn set_decoration( + &self, + decoration: Decoration, + ) { + self.decoration.replace(decoration); + } + + #[inline] + pub fn size_hints(&self) -> Option<SizeHints> { + self.size_hints.borrow().clone() + } + + #[inline] + pub fn set_size_hints( + &self, + size_hints: Option<SizeHints>, + ) { + self.size_hints.replace(size_hints); + } + + #[inline] + pub fn warp_pos(&self) -> Option<Pos> { + self.warp_pos.borrow().clone() + } + + #[inline] + pub fn set_warp_pos( + &self, + pointer_pos: Pos, + ) { + self.warp_pos.replace(Some(pointer_pos)); + } + + #[inline] + pub fn unset_warp_pos(&self) { + self.warp_pos.replace(None); + } + + #[inline] + pub fn set_parent( + &mut self, + parent: Window, + ) { + self.parent = Some(parent); + } + + #[inline] + pub fn parent(&self) -> Option<Window> { + self.parent + } + + #[inline] + pub fn add_child( + &self, + child: Window, + ) { + self.children.borrow_mut().push(child); + } + + #[inline] + pub fn remove_child( + &self, + child: Window, + ) { + let mut children = self.children.borrow_mut(); + + if let Some(index) = children.iter().rposition(|&c| c == child) { + children.remove(index); + } + } + + #[inline] + pub fn set_leader( + &mut self, + leader: Window, + ) { + self.leader = Some(leader); + } + + #[inline] + pub fn leader(&self) -> Option<Window> { + self.leader + } + + #[inline] + pub fn set_producer( + &mut self, + producer: Window, + ) { + self.producer = Some(producer); + } + + #[inline] + pub fn unset_producer(&mut self) { + self.producer = None; + } + + #[inline] + pub fn producer(&self) -> Option<Window> { + self.producer + } + + #[inline] + pub fn consumer_len(&self) -> usize { + self.consumers.borrow().len() + } + + #[inline] + pub fn add_consumer( + &self, + consumer: Window, + ) { + self.consumers.borrow_mut().push(consumer); + } + + #[inline] + pub fn remove_consumer( + &self, + consumer: Window, + ) { + let mut consumers = self.consumers.borrow_mut(); + + if let Some(index) = consumers.iter().rposition(|&c| c == consumer) { + consumers.remove(index); + } + } + + #[inline] + pub fn is_free(&self) -> bool { + self.floating.borrow().clone() || self.disowned.borrow().clone() || !self.managed.borrow().clone() + } + + #[inline] + pub fn is_focused(&self) -> bool { + self.focused.borrow().clone() + } + + #[inline] + pub fn set_focused( + &self, + focused: bool, + ) { + self.focused.replace(focused); + } + + #[inline] + pub fn is_mapped(&self) -> bool { + self.mapped.borrow().clone() + } + + #[inline] + pub fn set_mapped( + &self, + mapped: bool, + ) { + self.mapped.replace(mapped); + } + + #[inline] + pub fn is_managed(&self) -> bool { + self.managed.borrow().clone() + } + + #[inline] + pub fn set_managed( + &self, + managed: bool, + ) { + self.managed.replace(managed); + } + + #[inline] + pub fn is_in_window(&self) -> bool { + self.in_window.borrow().clone() + } + + #[inline] + pub fn set_in_window( + &self, + in_window: bool, + ) { + self.in_window.replace(in_window); + } + + #[inline] + pub fn is_floating(&self) -> bool { + self.floating.borrow().clone() + } + + #[inline] + pub fn set_floating( + &self, + floating: bool, + ) { + self.floating.replace(floating); + } + + #[inline] + pub fn is_fullscreen(&self) -> bool { + self.fullscreen.borrow().clone() + } + + #[inline] + pub fn set_fullscreen( + &self, + fullscreen: bool, + ) { + self.fullscreen.replace(fullscreen); + } + + #[inline] + pub fn is_iconified(&self) -> bool { + self.iconified.borrow().clone() + } + + #[inline] + pub fn set_iconified( + &self, + iconified: bool, + ) { + self.iconified.replace(iconified); + } + + #[inline] + pub fn is_disowned(&self) -> bool { + self.disowned.borrow().clone() + } + + #[inline] + pub fn set_disowned( + &self, + disowned: bool, + ) { + self.disowned.replace(disowned); + } + + #[inline] + pub fn is_sticky(&self) -> bool { + self.sticky.borrow().clone() + } + + #[inline] + pub fn set_sticky( + &self, + sticky: bool, + ) { + self.sticky.replace(sticky); + } + + #[inline] + pub fn is_invincible(&self) -> bool { + self.invincible.borrow().clone() + } + + #[inline] + pub fn set_invincible( + &self, + invincible: bool, + ) { + self.invincible.replace(invincible); + } + + #[inline] + pub fn is_urgent(&self) -> bool { + self.urgent.borrow().clone() + } + + #[inline] + pub fn set_urgent( + &self, + urgent: bool, + ) { + self.urgent.replace(urgent); + } + + #[inline] + pub fn is_consuming(&self) -> bool { + self.consuming.borrow().clone() + } + + #[inline] + pub fn set_consuming( + &self, + consuming: bool, + ) { + self.consuming.replace(consuming); + } + + #[inline] + pub fn is_producing(&self) -> bool { + self.producing.borrow().clone() + } + + #[inline] + pub fn set_producing( + &self, + producing: bool, + ) { + self.producing.replace(producing); + } + + #[inline] + pub fn pid(&self) -> Option<Pid> { + self.pid + } + + #[inline] + pub fn ppid(&self) -> Option<Pid> { + self.ppid + } + + #[inline] + pub fn last_focused(&self) -> SystemTime { + self.last_focused.borrow().clone() + } + + #[inline] + pub fn managed_since(&self) -> SystemTime { + self.managed_since + } + + #[inline] + pub fn expect_unmap(&self) { + self.expected_unmap_count.replace(self.expected_unmap_count.borrow().clone() + 1); + } + + #[inline] + pub fn is_expecting_unmap(&self) -> bool { + self.expected_unmap_count.borrow().clone() > 0 + } + + #[inline] + pub fn consume_unmap_if_expecting(&self) -> bool { + let expected_unmap_count = self.expected_unmap_count.borrow().clone(); + let expecting = expected_unmap_count > 0; + + if expecting { + self.expected_unmap_count.replace(expected_unmap_count - 1); + } + + expecting + } +} + +impl<'client> PartialEq for Client2 { + fn eq( + &self, + other: &Self, + ) -> bool { + self.window == other.window + } +} + +impl<'client> std::fmt::Debug for Client2 { + fn fmt( + &self, + f: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + f.debug_struct("Client2") + .field("window", &Hex32(self.window)) + .field("frame", &Hex32(self.frame)) + .field("name", &self.name) + .field("class", &self.class) + .field("instance", &self.instance) + .field("context", &self.context) + .field("workspace", &self.workspace) + .field("window_type", &self.window_type) + .field("active_region", &self.active_region) + .field("previous_region", &self.previous_region) + .field("inner_region", &self.inner_region) + .field("free_region", &self.free_region) + .field("tile_region", &self.tile_region) + .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( + "children", + &self + .children + .borrow() + .iter() + .map(|&child| Hex32(child)) + .collect::<Vec<Hex32>>(), + ) + .field("leader", &self.leader) + .field("producer", &self.producer) + .field("consumers", &self.consumers) + .field("focused", &self.focused) + .field("mapped", &self.mapped) + .field("managed", &self.managed) + .field("in_window", &self.in_window) + .field("floating", &self.floating) + .field("fullscreen", &self.fullscreen) + .field("iconified", &self.iconified) + .field("disowned", &self.disowned) + .field("sticky", &self.sticky) + .field("invincible", &self.invincible) + .field("urgent", &self.urgent) + .field("consuming", &self.consuming) + .field("pid", &self.pid) + .field("ppid", &self.ppid) + .field("last_focused", &self.last_focused) + .field("managed_since", &self.managed_since) + .field("expected_unmap_count", &self.expected_unmap_count) + .finish() + } +} diff --git a/src/core/common.rs b/src/core/common.rs @@ -1,213 +0,0 @@ -use winsys::common::Dim; -use winsys::common::Extents; -use winsys::common::Padding; -use winsys::common::Window; - -use std::ops::Add; - -#[macro_export] -macro_rules! WM_NAME ( - () => { "wzrd" }; -); - -pub const MIN_WINDOW_DIM: Dim = Dim { - w: 75, - h: 50, -}; - -pub const NO_DECORATION: Decoration = Decoration { - border: None, - frame: None, -}; - -pub const FREE_DECORATION: Decoration = Decoration { - border: None, - frame: Some(Frame { - extents: Extents { - left: 3, - right: 1, - top: 1, - bottom: 1, - }, - colors: ColorScheme::DEFAULT, - }), -}; - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum StateChangeError { - EarlyStop, - LimitReached, - StateUnchanged, - InvalidCaller, -} - -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, -} - -impl ColorScheme { - const DEFAULT: Self = Self { - regular: 0x333333, - focused: 0xe78a53, - urgent: 0xfbcb97, - rdisowned: 0x999999, - fdisowned: 0xc1c1c1, - rsticky: 0x444444, - fsticky: 0x5f8787, - }; -} - -impl Default for ColorScheme { - fn default() -> Self { - Self::DEFAULT - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct Border { - pub width: u32, - pub colors: ColorScheme, -} - -impl Add<Border> for Padding { - type Output = Self; - - fn add( - self, - _: Border, - ) -> Self::Output { - Self::Output { - left: self.left + 1, - right: self.right + 1, - top: self.top + 1, - bottom: self.bottom + 1, - } - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct Frame { - pub extents: Extents, - pub colors: ColorScheme, -} - -impl Add<Frame> for Padding { - type Output = Self; - - fn add( - self, - frame: Frame, - ) -> Self::Output { - Self::Output { - left: self.left + frame.extents.left, - right: self.right + frame.extents.right, - top: self.top + frame.extents.top, - bottom: self.bottom + frame.extents.bottom, - } - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct Decoration { - pub border: Option<Border>, - pub frame: Option<Frame>, -} - -impl Default for Decoration { - fn default() -> Self { - Self { - border: None, - frame: None, - } - } -} - -impl Decoration { - pub fn extents(&self) -> Extents { - Extents { - left: 0, - right: 0, - top: 0, - bottom: 0, - } + *self - } -} - -impl Add<Decoration> for Padding { - type Output = Self; - - fn add( - mut self, - decoration: Decoration, - ) -> Self::Output { - if let Some(border) = decoration.border { - self = self + border; - } - - if let Some(frame) = decoration.frame { - self = self + frame; - } - - self - } -} - -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 - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum Direction { - Forward, - Backward, -} - -impl Direction { - pub fn rev(&self) -> Self { - match self { - Self::Forward => Self::Backward, - Self::Backward => Self::Forward, - } - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum Change { - Inc, - Dec, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum BorderState { - Urgent, - Focused, - Unfocused, - Disowned, - Sticky, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct BorderSize { - pub left: u32, - pub right: u32, - pub top: u32, - pub bottom: u32, -} diff --git a/src/core/consume.rs b/src/core/consume.rs @@ -1,6 +1,7 @@ use crate::client::Client; -use winsys::common::Pid; -use winsys::common::Window; + +use winsys::connection::Pid; +use winsys::window::Window; use std::collections::HashMap; use std::fs; diff --git a/src/core/cycle.rs b/src/core/cycle.rs @@ -1,7 +1,7 @@ -use crate::common::Direction; -use crate::common::Ident; -use crate::common::Identify; -use crate::common::Index; +use crate::change::Direction; +use crate::identify::Ident; +use crate::identify::Identify; +use crate::identify::Index; use crate::util::BuildIdHasher; use crate::util::Util; diff --git a/src/core/decoration.rs b/src/core/decoration.rs @@ -0,0 +1,124 @@ +use winsys::geometry::Extents; +use winsys::geometry::Padding; + +use std::ops::Add; + +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, +} + +impl ColorScheme { + pub const DEFAULT: Self = Self { + regular: 0x333333, + focused: 0xe78a53, + urgent: 0xfbcb97, + rdisowned: 0x999999, + fdisowned: 0xc1c1c1, + rsticky: 0x444444, + fsticky: 0x5f8787, + }; +} + +impl Default for ColorScheme { + fn default() -> Self { + Self::DEFAULT + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct Border { + pub width: u32, + pub colors: ColorScheme, +} + +impl Add<Border> for Padding { + type Output = Self; + + fn add( + self, + _: Border, + ) -> Self::Output { + Self::Output { + left: self.left + 1, + right: self.right + 1, + top: self.top + 1, + bottom: self.bottom + 1, + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct Frame { + pub extents: Extents, + pub colors: ColorScheme, +} + +impl Add<Frame> for Padding { + type Output = Self; + + fn add( + self, + frame: Frame, + ) -> Self::Output { + Self::Output { + left: self.left + frame.extents.left, + right: self.right + frame.extents.right, + top: self.top + frame.extents.top, + bottom: self.bottom + frame.extents.bottom, + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct Decoration { + pub border: Option<Border>, + pub frame: Option<Frame>, +} + +impl Default for Decoration { + fn default() -> Self { + Self { + border: None, + frame: None, + } + } +} + +impl Decoration { + pub fn extents(&self) -> Extents { + Extents { + left: 0, + right: 0, + top: 0, + bottom: 0, + } + *self + } +} + +impl Add<Decoration> for Padding { + type Output = Self; + + fn add( + mut self, + decoration: Decoration, + ) -> Self::Output { + if let Some(border) = decoration.border { + self = self + border; + } + + if let Some(frame) = decoration.frame { + self = self + frame; + } + + self + } +} diff --git a/src/core/defaults.rs b/src/core/defaults.rs @@ -0,0 +1,65 @@ +use crate::client::Client; +use crate::decoration::ColorScheme; +use crate::decoration::Decoration; +use crate::decoration::Frame; +use crate::layout::Layout; +use crate::zone::Zone; + +use winsys::geometry::Dim; +use winsys::geometry::Extents; +use winsys::geometry::Padding; + +#[macro_export] +macro_rules! WM_NAME ( + () => { "wzrd" }; +); + +impl Client { + pub const MIN_CLIENT_DIM: Dim = Dim { + w: 75, + h: 50, + }; + + pub const PREFERRED_CLIENT_DIM: Dim = Dim { + w: 480, + h: 260, + }; +} + +impl Decoration { + pub const NO_DECORATION: Self = Self { + border: None, + frame: None, + }; + + pub const FREE_DECORATION: Self = Self { + border: None, + frame: Some(Frame { + extents: Extents { + left: 3, + right: 1, + top: 1, + bottom: 1, + }, + colors: ColorScheme::DEFAULT, + }), + }; +} + +impl Layout { + pub const MAX_MAIN_COUNT: u32 = 15; + pub const MAX_GAP_SIZE: u32 = 300; + pub const MAX_MARGIN: Padding = Padding { + left: 700, + right: 700, + top: 400, + bottom: 400, + }; +} + +impl Zone { + pub const MIN_ZONE_DIM: Dim = Dim { + w: 25, + h: 25, + }; +} diff --git a/src/core/error.rs b/src/core/error.rs @@ -0,0 +1,7 @@ +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum StateChangeError { + EarlyStop, + LimitReached, + StateUnchanged, + InvalidCaller, +} diff --git a/src/core/identify.rs b/src/core/identify.rs @@ -0,0 +1,14 @@ +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/jump.rs b/src/core/jump.rs @@ -1,5 +1,5 @@ use crate::client::Client; -use crate::common::Index; +use crate::identify::Index; use crate::workspace::ClientSelector; #[derive(Debug, Copy, Clone, PartialEq, Eq)] diff --git a/src/core/layout.rs b/src/core/layout.rs @@ -0,0 +1,955 @@ +use crate::change::Disposition; +use crate::decoration::Decoration; +use crate::decoration::Frame; +use crate::error::StateChangeError; +use crate::identify::Ident; +use crate::identify::Identify; +use crate::placement::PlacementMethod; +use crate::zone::Zone; + +use winsys::geometry::Dim; +use winsys::geometry::Extents; +use winsys::geometry::Padding; +use winsys::geometry::Pos; +use winsys::geometry::Region; + +use strum::EnumCount; +use strum::IntoEnumIterator; +use strum_macros::EnumIter; +use strum_macros::ToString; + +use std::collections::HashMap; +use std::string::ToString; +use std::vec::Vec; + +type LayoutFn = fn(&Region, &LayoutData, Vec<bool>) -> Vec<(Disposition, bool)>; + +#[non_exhaustive] +#[derive(Debug, PartialEq, Clone, Copy)] +pub struct LayoutConfig { + pub method: PlacementMethod, + pub decoration: Decoration, + pub root_only: bool, + pub margin: bool, + pub gap: bool, + pub persistent: bool, + pub single: bool, + pub wraps: bool, +} + +impl Default for LayoutConfig { + fn default() -> Self { + Self { + method: PlacementMethod::Free, + decoration: Default::default(), + root_only: true, + margin: false, + gap: false, + persistent: false, + single: false, + wraps: true, + } + } +} + +#[non_exhaustive] +#[derive(Debug, PartialEq, Clone, Copy)] +pub struct LayoutData { + /// Generic layout data + pub margin: Padding, + pub gap_size: u32, + + /// Tiled layout data + pub main_count: u32, + pub main_factor: f32, +} + +impl Default for LayoutData { + fn default() -> Self { + Self { + margin: Default::default(), + gap_size: 0u32, + + main_count: 1u32, + main_factor: 0.50f32, + } + } +} + +#[non_exhaustive] +#[repr(u8)] +#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, EnumIter, EnumCount, ToString)] +pub enum LayoutKind { + /// Free layouts + Float = b'f', + BLFloat = b'F', + SingleFloat = b'z', + BLSingleFloat = b'Z', + + /// Tiled layouts + // Overlapping + Center = b';', + Monocle = b'%', + // Non-overlapping + Paper = b'p', + SPaper = b'P', + Stack = b's', + SStack = b'S', + BStack = b'b', + SBStack = b'B', + Horz = b'h', + SHorz = b'H', + Vert = b'v', + SVert = b'V', +} + +impl LayoutKind { + pub fn symbol(&self) -> char { + (*self as u8) as char + } + + pub fn name(&self) -> String { + self.to_string() + } + + pub fn config(&self) -> LayoutConfig { + match *self { + LayoutKind::Float => LayoutConfig { + method: PlacementMethod::Free, + decoration: Decoration::FREE_DECORATION, + root_only: true, + margin: false, + gap: false, + persistent: false, + single: false, + wraps: true, + }, + LayoutKind::BLFloat => LayoutConfig { + method: PlacementMethod::Free, + decoration: Decoration::NO_DECORATION, + root_only: true, + margin: false, + gap: false, + persistent: false, + single: false, + wraps: true, + }, + LayoutKind::SingleFloat => LayoutConfig { + method: PlacementMethod::Free, + decoration: Decoration::FREE_DECORATION, + root_only: true, + margin: false, + gap: false, + persistent: true, + single: true, + wraps: true, + }, + LayoutKind::BLSingleFloat => LayoutConfig { + method: PlacementMethod::Free, + decoration: Decoration::NO_DECORATION, + root_only: true, + margin: false, + gap: false, + persistent: true, + single: true, + wraps: true, + }, + LayoutKind::Center => LayoutConfig { + method: PlacementMethod::Tile, + decoration: Decoration::NO_DECORATION, + root_only: false, + margin: true, + gap: true, + persistent: false, + single: false, + wraps: true, + }, + LayoutKind::Monocle => LayoutConfig { + method: PlacementMethod::Tile, + decoration: Decoration::NO_DECORATION, + root_only: false, + margin: true, + gap: true, + persistent: false, + single: false, + wraps: true, + }, + LayoutKind::Paper => LayoutConfig { + method: PlacementMethod::Tile, + decoration: Decoration { + frame: Some(Frame { + extents: Extents { + left: 1, + right: 1, + top: 0, + bottom: 0, + }, + colors: Default::default(), + }), + border: None, + }, + root_only: false, + margin: true, + gap: true, + persistent: true, + single: false, + wraps: false, + }, + LayoutKind::SPaper => LayoutConfig { + method: PlacementMethod::Tile, + decoration: Decoration { + frame: Some(Frame { + extents: Extents { + left: 1, + right: 1, + top: 0, + bottom: 0, + }, + colors: Default::default(), + }), + border: None, + }, + root_only: false, + margin: true, + gap: false, + persistent: true, + single: false, + wraps: false, + }, + LayoutKind::Stack => LayoutConfig { + method: PlacementMethod::Tile, + decoration: Decoration { + frame: Some(Frame { + extents: Extents { + left: 0, + right: 0, + top: 3, + bottom: 0, + }, + colors: Default::default(), + }), + border: None, + }, + root_only: false, + margin: true, + gap: true, + persistent: false, + single: false, + wraps: true, + }, + LayoutKind::SStack => LayoutConfig { + method: PlacementMethod::Tile, + decoration: Decoration { + frame: Some(Frame { + extents: Extents { + left: 0, + right: 0, + top: 3, + bottom: 0, + }, + colors: Default::default(), + }), + border: None, + }, + root_only: false, + margin: true, + gap: false, + persistent: false, + single: false, + wraps: true, + }, + LayoutKind::BStack => LayoutConfig { + method: PlacementMethod::Tile, + decoration: Decoration { + frame: Some(Frame { + extents: Extents { + left: 0, + right: 0, + top: 3, + bottom: 0, + }, + colors: Default::default(), + }), + border: None, + }, + root_only: false, + margin: true, + gap: true, + persistent: false, + single: false, + wraps: true, + }, + LayoutKind::SBStack => LayoutConfig { + method: PlacementMethod::Tile, + decoration: Decoration { + frame: Some(Frame { + extents: Extents { + left: 0, + right: 0, + top: 3, + bottom: 0, + }, + colors: Default::default(), + }), + border: None, + }, + root_only: false, + margin: true, + gap: false, + persistent: false, + single: false, + wraps: true, + }, + LayoutKind::Horz => LayoutConfig { + method: PlacementMethod::Tile, + decoration: Decoration { + frame: Some(Frame { + extents: Extents { + left: 0, + right: 0, + top: 3, + bottom: 0, + }, + colors: Default::default(), + }), + border: None, + }, + root_only: false, + margin: true, + gap: true, + persistent: false, + single: false, + wraps: true, + }, + LayoutKind::SHorz => LayoutConfig { + method: PlacementMethod::Tile, + decoration: Decoration { + frame: Some(Frame { + extents: Extents { + left: 0, + right: 0, + top: 3, + bottom: 0, + }, + colors: Default::default(), + }), + border: None, + }, + root_only: false, + margin: true, + gap: false, + persistent: false, + single: false, + wraps: true, + }, + LayoutKind::Vert => LayoutConfig { + method: PlacementMethod::Tile, + decoration: Decoration { + frame: Some(Frame { + extents: Extents { + left: 0, + right: 0, + top: 3, + bottom: 0, + }, + colors: Default::default(), + }), + border: None, + }, + root_only: false, + margin: true, + gap: true, + persistent: false, + single: false, + wraps: true, + }, + LayoutKind::SVert => LayoutConfig { + method: PlacementMethod::Tile, + decoration: Decoration { + frame: Some(Frame { + extents: Extents { + left: 0, + right: 0, + top: 3, + bottom: 0, + }, + colors: Default::default(), + }), + border: None, + }, + root_only: false, + margin: true, + gap: false, + persistent: false, + single: false, + wraps: true, + }, + + #[allow(unreachable_patterns)] + _ => unimplemented!("{:?} does not have an associated configuration", self), + } + } + + fn default_data(&self) -> LayoutData { + match *self { + LayoutKind::Float => Default::default(), + LayoutKind::BLFloat => Default::default(), + LayoutKind::SingleFloat => Default::default(), + LayoutKind::BLSingleFloat => Default::default(), + LayoutKind::Center => LayoutData { + main_count: 5u32, + main_factor: 0.40f32, + ..Default::default() + }, + LayoutKind::Monocle => Default::default(), + LayoutKind::Paper => Default::default(), + LayoutKind::SPaper => Default::default(), + LayoutKind::Stack => LayoutData { + main_count: 1u32, + main_factor: 0.50f32, + ..Default::default() + }, + LayoutKind::SStack => LayoutData { + main_count: 1u32, + main_factor: 0.50f32, + ..Default::default() + }, + LayoutKind::BStack => LayoutData { + main_count: 1u32, + main_factor: 0.50f32, + ..Default::default() + }, + LayoutKind::SBStack => LayoutData { + main_count: 1u32, + main_factor: 0.50f32, + ..Default::default() + }, + LayoutKind::Horz => Default::default(), + LayoutKind::SHorz => Default::default(), + LayoutKind::Vert => Default::default(), + LayoutKind::SVert => Default::default(), + + #[allow(unreachable_patterns)] + _ => unimplemented!("{:?} does not have associated default data", self), + } + } + + #[inline] + fn stack_split( + n: usize, + n_main: u32, + ) -> (i32, i32) { + let n_main = n_main as i32; + let n = n as i32; + + if n <= n_main { + (n, 0i32) + } else { + (n_main, n - n_main) + } + } + + fn func(&self) -> LayoutFn { + match *self { + LayoutKind::Float => |_, _, active_map| { + let config = &LayoutKind::Float.config(); + vec![(Disposition::Unchanged(config.decoration), true); active_map.len()] + }, + LayoutKind::BLFloat => |_, _, active_map| { + let config = &LayoutKind::BLFloat.config(); + vec![(Disposition::Unchanged(config.decoration), true); active_map.len()] + }, + LayoutKind::SingleFloat => |_, _, active_map| { + let config = &LayoutKind::SingleFloat.config(); + active_map + .into_iter() + .map(|b| (Disposition::Unchanged(config.decoration), b)) + .collect() + }, + LayoutKind::BLSingleFloat => |_, _, active_map| { + let config = &LayoutKind::BLSingleFloat.config(); + active_map + .into_iter() + .map(|b| (Disposition::Unchanged(config.decoration), b)) + .collect() + }, + LayoutKind::Center => |region, data, active_map| { + let config = &LayoutKind::Center.config(); + let (pos, dim) = region.values(); + + let h_comp = Layout::MAX_MAIN_COUNT + 1; + let w_ratio: f32 = data.main_factor / 0.95; + let h_ratio: f32 = (h_comp - data.main_count) as f32 / h_comp as f32; + + active_map + .into_iter() + .map(|_| { + ( + Disposition::Changed( + Region { + pos, + dim, + } + .from_absolute_inner_center(&Dim { + w: (dim.w as f32 * w_ratio) as i32, + h: (dim.h as f32 * h_ratio) as i32, + }), + config.decoration, + ), + true, + ) + }) + .collect() + }, + LayoutKind::Monocle => |region, _, active_map| { + let config = &LayoutKind::Monocle.config(); + let (pos, dim) = region.values(); + + active_map + .into_iter() + .map(|_| { + ( + Disposition::Changed( + Region { + pos, + dim, + }, + config.decoration, + ), + true, + ) + }) + .collect() + }, + LayoutKind::Paper => |region, data, active_map| { + const MIN_W_RATIO: f32 = 0.5; + + let config = &LayoutKind::Paper.config(); + let (pos, dim) = region.values(); + let n = active_map.len(); + + if n == 1 { + return vec![( + Disposition::Changed(*region, Decoration::NO_DECORATION), + true, + )]; + } + + let cw = (dim.w as f32 + * if data.main_factor > MIN_W_RATIO { + data.main_factor + } else { + MIN_W_RATIO + }) as i32; + + let w = ((dim.w - cw) as usize / (n - 1)) as i32; + let mut after_active = false; + + active_map + .into_iter() + .enumerate() + .map(|(i, active)| { + let i = i as i32; + + ( + Disposition::Changed( + if active { + after_active = true; + Region::new(pos.x + i * w, pos.y, cw, dim.h) + } else { + let mut x = pos.x + i * w; + + if after_active { + x += cw - w; + } + + Region::new(x, pos.y, w, dim.h) + }, + config.decoration, + ), + true, + ) + }) + .collect() + }, + LayoutKind::SPaper => |region, data, active_map| { + let mut region = region.clone(); + Layout::adjust_for_gap_size(&mut region, data.gap_size, &Zone::MIN_ZONE_DIM); + + (Self::Paper.func())(&region, data, active_map) + }, + LayoutKind::Stack => |region, data, active_map| { + let (pos, dim) = region.values(); + let n = active_map.len(); + + if n == 1 { + return vec![( + Disposition::Changed(*region, Decoration::NO_DECORATION), + true, + )]; + } + + 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 }; + + let div = if data.main_count > 0 { + (dim.w as f32 * data.main_factor) as i32 + } else { + 0 + }; + + let config = &LayoutKind::Stack.config(); + let main_count = data.main_count as i32; + + active_map + .into_iter() + .enumerate() + .map(|(i, _)| { + let i = i as i32; + + ( + Disposition::Changed( + if i < main_count { + Region::new( + pos.x, + pos.y + (i * h_main), + if n_stack == 0 { dim.w } else { div }, + h_main, + ) + } else { + Region::new( + pos.x + div, + pos.y + (i - main_count) * h_stack, + dim.w - div, + h_stack, + ) + }, + config.decoration, + ), + true, + ) + }) + .collect() + }, + LayoutKind::SStack => |region, data, active_map| { + let mut region = region.clone(); + Layout::adjust_for_gap_size(&mut region, data.gap_size, &Zone::MIN_ZONE_DIM); + + (Self::Stack.func())(&region, data, active_map) + }, + LayoutKind::BStack => |region, data, active_map| { + let (pos, dim) = region.values(); + let n = active_map.len(); + + if n == 1 { + return vec![( + Disposition::Changed(*region, Decoration::NO_DECORATION), + true, + )]; + } + + let (n_main, n_stack) = Self::stack_split(n, data.main_count); + + let div = if data.main_count > 0 { + (dim.w as f32 * data.main_factor) as i32 + } else { + 0 + }; + + let h_main = if n_main > 0 { + (if n_stack > 0 { div } else { dim.h }) / n_main + } else { + 0 + }; + + let w_stack = if n_stack > 0 { dim.w / n_stack } else { 0 }; + + let config = &LayoutKind::Stack.config(); + let main_count = data.main_count as i32; + + active_map + .into_iter() + .enumerate() + .map(|(i, _)| { + let i = i as i32; + + ( + Disposition::Changed( + if i < main_count { + Region::new(pos.x, pos.y + (i * h_main), dim.w, h_main) + } else { + Region::new( + pos.x + ((i - main_count) * w_stack), + pos.y + div, + w_stack, + dim.h - div, + ) + }, + config.decoration, + ), + true, + ) + }) + .collect() + }, + LayoutKind::SBStack => |region, data, active_map| { + let mut region = region.clone(); + Layout::adjust_for_gap_size(&mut region, data.gap_size, &Zone::MIN_ZONE_DIM); + + (Self::BStack.func())(&region, data, active_map) + }, + LayoutKind::Horz => |_region, _data, _active_map| todo!(), + LayoutKind::SHorz => |_region, _data, _active_map| todo!(), + LayoutKind::Vert => |_region, _data, _active_map| todo!(), + LayoutKind::SVert => |_region, _data, _active_map| todo!(), + + #[allow(unreachable_patterns)] + _ => unimplemented!("{:?} does not have an associated function", self), + } + } +} + +pub struct Layout { + kind: LayoutKind, + prev_kind: LayoutKind, + data: HashMap<LayoutKind, LayoutData>, +} + +impl Layout { + #[inline] + pub fn new() -> Self { + let kind = LayoutKind::Stack; + let mut data = HashMap::with_capacity(LayoutKind::COUNT); + + for kind in LayoutKind::iter() { + data.insert(kind, kind.default_data()); + } + + Self { + kind, + prev_kind: kind, + data, + } + } + + #[inline] + pub fn with_kind(kind: LayoutKind) -> Self { + let mut data = HashMap::with_capacity(LayoutKind::COUNT); + + for kind in LayoutKind::iter() { + data.insert(kind, kind.default_data()); + } + + Self { + kind, + prev_kind: kind, + data, + } + } + + #[inline] + pub fn kind(&self) -> LayoutKind { + self.kind + } + + #[inline] + pub fn prev_kind(&self) -> LayoutKind { + self.prev_kind + } + + #[inline] + pub fn config(&self) -> LayoutConfig { + self.kind.config() + } + + #[inline] + pub fn prev_data(&self) -> &LayoutData { + self.data.get(&self.prev_kind).unwrap() + } + + #[inline] + pub fn data(&self) -> &LayoutData { + self.data.get(&self.kind).unwrap() + } + + #[inline] + pub fn data_mut(&mut self) -> &mut LayoutData { + self.data.get_mut(&self.kind).unwrap() + } + + #[inline] + pub fn default_data(&self) -> LayoutData { + self.kind.default_data() + } + + #[inline] + pub fn set_kind( + &mut self, + kind: LayoutKind, + ) -> Result<LayoutKind, StateChangeError> { + if kind == self.kind { + return Err(StateChangeError::EarlyStop); + } + + self.prev_kind = self.kind; + self.kind = kind; + + Ok(self.prev_kind) + } + + #[inline] + pub fn adjust_for_margin( + region: Region, + extents: &Extents, + ) -> Region { + Region { + pos: Pos { + x: region.pos.x + extents.left as i32, + y: region.pos.y + extents.top as i32, + }, + dim: Dim { + w: region.dim.w - extents.left - extents.right, + h: region.dim.h - extents.top - extents.bottom, + }, + } + } + + #[inline] + pub fn adjust_for_gap_size( + region: &mut Region, + gap_size: u32, + min_dim: &Dim, + ) { + let gap_size = gap_size as i32; + let dim_gap = 2 * gap_size; + + let new_w = region.dim.w - dim_gap; + if new_w < min_dim.w { + region.pos.x += ((region.dim.w - min_dim.w) as f32 / 2f32) as i32; + region.dim.w = min_dim.w; + } else { + region.dim.w = new_w; + region.pos.x += gap_size; + } + + let new_h = region.dim.h - dim_gap; + if new_h < min_dim.h { + region.pos.y += ((region.dim.h - min_dim.h) as f32 / 2f32) as i32; + region.dim.h = min_dim.h; + } else { + region.dim.h = new_h; + region.pos.y += gap_size; + } + } + + #[inline] + pub fn adjust_for_border( + region: &mut Region, + border_width: u32, + min_dim: &Dim, + ) { + let border_padding = 2 * border_width as i32; + + let new_w = region.dim.w - border_padding; + region.dim.w = std::cmp::max(min_dim.w, new_w); + + let new_h = region.dim.h - border_padding; + region.dim.h = std::cmp::max(min_dim.h, new_h); + } +} + +impl Default for Layout { + fn default() -> Self { + Self { + kind: LayoutKind::Stack, + prev_kind: LayoutKind::Stack, + data: HashMap::new(), + } + } +} + +pub trait Apply { + fn apply( + &self, + region: Region, + active_map: Vec<bool>, + ) -> (PlacementMethod, Vec<(Disposition, bool)>); +} + +impl Apply for Layout { + #[inline] + fn apply( + &self, + region: Region, + active_map: Vec<bool>, + ) -> (PlacementMethod, Vec<(Disposition, bool)>) { + let config = self.kind.config(); + let data = self.data(); + + let region = if config.margin { + Self::adjust_for_margin(region, &data.margin) + } else { + region + }; + + ( + 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, decoration) => { + if let Some(border) = decoration.border { + Self::adjust_for_gap_size( + region, + border.width, + &Zone::MIN_ZONE_DIM, + ); + } + + if config.gap { + Self::adjust_for_gap_size( + region, + data.gap_size, + &Zone::MIN_ZONE_DIM, + ); + } + }, + } + + (disposition, is_visible) + }) + .collect(), + ) + } +} + +impl std::cmp::PartialEq<Self> for Layout { + fn eq( + &self, + other: &Self, + ) -> bool { + self.kind == other.kind && self.data.get(&self.kind) == other.data.get(&other.kind) + } +} + +impl Identify for Layout { + fn id(&self) -> Ident { + self.kind as Ident + } +} + +impl std::fmt::Debug for Layout { + fn fmt( + &self, + f: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + f.debug_struct("Layout") + .field("kind", &self.kind) + .field("prev_kind", &self.prev_kind) + .field("data", &self.data.get(&self.kind)) + .finish() + } +} diff --git a/src/core/macros.rs b/src/core/macros.rs @@ -47,7 +47,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::common::Window>| { + Box::new(|$model: &mut $crate::model::Model, $window: Option<winsys::window::Window>| { $body }) as $crate::binding::MouseEvents }; diff --git a/src/core/main.rs b/src/core/main.rs @@ -1,5 +1,6 @@ #![deny(clippy::all)] #![allow(dead_code)] +#![recursion_limit = "256"] #[macro_use] extern crate log; @@ -9,7 +10,7 @@ use simplelog::LevelFilter; #[allow(unused_imports)] use simplelog::SimpleLogger; -use winsys::common::Edge; +use winsys::geometry::Edge; use winsys::xdata::xconnection::XConnection; pub use winsys::Result; @@ -17,15 +18,21 @@ pub use winsys::Result; mod macros; #[macro_use] -mod common; +mod defaults; mod binding; +mod change; mod client; mod consume; mod cycle; +mod decoration; +mod error; +mod identify; mod jump; +mod layout; mod model; mod partition; +mod placement; mod rule; mod stack; mod util; @@ -34,13 +41,13 @@ mod zone; use binding::KeyBindings; use binding::MouseBindings; -use common::Change; -use common::Direction; +use change::Change; +use change::Direction; use jump::JumpCriterium; use jump::MatchMethod; +use layout::LayoutKind; use model::Model; use workspace::ClientSelector; -use zone::LayoutKind; pub fn main() -> Result<()> { #[cfg(debug_assertions)] @@ -48,9 +55,13 @@ pub fn main() -> Result<()> { let (conn, screen_num) = x11rb::connect(None)?; let (mouse_bindings, key_bindings) = init_bindings(); - let mut xconn = XConnection::new(&conn, screen_num)?; - Model::new(&mut xconn, &key_bindings, &mouse_bindings).run(key_bindings, mouse_bindings); + Model::new( + &mut XConnection::new(&conn, screen_num)?, + &key_bindings, + &mouse_bindings, + ) + .run(key_bindings, mouse_bindings); Ok(()) } @@ -193,34 +204,41 @@ fn init_bindings() -> (MouseBindings, KeyBindings) { "1-C-k" => do_internal!(cycle_zones, Direction::Backward), // active workspace layout setters - "1-m" => do_internal!(set_layout, LayoutKind::Monocle), - "1-t" => do_internal!(set_layout, LayoutKind::Stack), - "1-g" => do_internal!(set_layout, LayoutKind::Center), "1-S-f" => do_internal!(set_layout, LayoutKind::Float), "1-S-l" => do_internal!(set_layout, LayoutKind::BLFloat), "1-z" => do_internal!(set_layout, LayoutKind::SingleFloat), "1-S-z" => do_internal!(set_layout, LayoutKind::BLSingleFloat), - "1-C-S-f" => do_internal!(apply_float_retain_region), + "1-m" => do_internal!(set_layout, LayoutKind::Monocle), + "1-g" => do_internal!(set_layout, LayoutKind::Center), + "1-t" => do_internal!(set_layout, LayoutKind::Stack), "1-S-t" => do_internal!(set_layout, LayoutKind::SStack), "1-C-S-p" => do_internal!(set_layout, LayoutKind::Paper), + "1-2-C-S-p" => do_internal!(set_layout, LayoutKind::SPaper), + "1-C-S-b" => do_internal!(set_layout, LayoutKind::BStack), + "1-2-C-S-b" => do_internal!(set_layout, LayoutKind::SBStack), + "1-S-y" => do_internal!(set_layout, LayoutKind::Horz), + "1-C-y" => do_internal!(set_layout, LayoutKind::SHorz), + "1-S-v" => do_internal!(set_layout, LayoutKind::Vert), + "1-C-v" => do_internal!(set_layout, LayoutKind::SVert), + "1-C-S-f" => do_internal!(apply_float_retain_region), "1-space" => do_internal!(toggle_layout), // active workspace layout-data modifiers - "1-plus" => do_internal!(change_gap_size, Change::Inc), - "1-minus" => do_internal!(change_gap_size, Change::Dec), + "1-plus" => do_internal!(change_gap_size, Change::Inc(5u32)), + "1-minus" => do_internal!(change_gap_size, Change::Dec(5u32)), "1-S-equal" => do_internal!(reset_gap_size), - "1-i" => do_internal!(change_main_count, Change::Inc), - "1-d" => do_internal!(change_main_count, Change::Dec), - "1-l" => do_internal!(change_main_factor, Change::Inc), - "1-h" => do_internal!(change_main_factor, Change::Dec), - "1-S-Left" => do_internal!(change_margin, Edge::Left, Change::Inc), - "1-C-S-Left" => do_internal!(change_margin, Edge::Left, Change::Dec), - "1-S-Up" => do_internal!(change_margin, Edge::Top, Change::Inc), - "1-C-S-Up" => do_internal!(change_margin, Edge::Top, Change::Dec), - "1-S-Down" => do_internal!(change_margin, Edge::Bottom, Change::Inc), - "1-C-S-Down" => do_internal!(change_margin, Edge::Bottom, Change::Dec), - "1-S-Right" => do_internal!(change_margin, Edge::Right, Change::Inc), - "1-C-S-Right" => do_internal!(change_margin, Edge::Right, Change::Dec), + "1-i" => do_internal!(change_main_count, Change::Inc(1u32)), + "1-d" => do_internal!(change_main_count, Change::Dec(1u32)), + "1-l" => do_internal!(change_main_factor, Change::Inc(0.05f32)), + "1-h" => do_internal!(change_main_factor, Change::Dec(0.05f32)), + "1-S-Left" => do_internal!(change_margin, Edge::Left, Change::Inc(5i32)), + "1-C-S-Left" => do_internal!(change_margin, Edge::Left, Change::Dec(5i32)), + "1-S-Up" => do_internal!(change_margin, Edge::Top, Change::Inc(5i32)), + "1-C-S-Up" => do_internal!(change_margin, Edge::Top, Change::Dec(5i32)), + "1-S-Down" => do_internal!(change_margin, Edge::Bottom, Change::Inc(5i32)), + "1-C-S-Down" => do_internal!(change_margin, Edge::Bottom, Change::Dec(5i32)), + "1-S-Right" => do_internal!(change_margin, Edge::Right, Change::Inc(5i32)), + "1-C-S-Right" => do_internal!(change_margin, Edge::Right, Change::Dec(5i32)), "1-C-S-equal" => do_internal!(reset_margin), "1-2-C-S-l" => do_internal!(copy_prev_layout_data), "1-2-C-S-equal" => do_internal!(reset_layout_data), diff --git a/src/core/model.rs b/src/core/model.rs @@ -1,61 +1,59 @@ use crate::binding::KeyBindings; use crate::binding::MouseBindings; +use crate::change::Change; +use crate::change::Direction; 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; use crate::consume::get_spawner_pid; use crate::cycle::Cycle; use crate::cycle::InsertPos; use crate::cycle::Selector; +use crate::decoration::Decoration; +use crate::error::StateChangeError; +use crate::identify::Index; use crate::jump::JumpCriterium; use crate::jump::MatchMethod; +use crate::layout::Layout; +use crate::layout::LayoutKind; use crate::partition::Partition; +use crate::placement::Placement; +use crate::placement::PlacementMethod; +use crate::placement::PlacementRegion; +use crate::placement::PlacementTarget; use crate::rule::Rules; use crate::stack::StackLayer; use crate::stack::StackManager; use crate::workspace::Buffer; use crate::workspace::BufferKind; use crate::workspace::Workspace; -use crate::zone::Layout; -use crate::zone::LayoutKind; -use crate::zone::Placement; -use crate::zone::PlacementKind; -use crate::zone::PlacementMethod; -use crate::zone::PlacementRegion; use crate::zone::ZoneContent; use crate::zone::ZoneManager; #[allow(unused_imports)] use crate::util::Util; -use winsys::common::Corner; -use winsys::common::Dim; -use winsys::common::Edge; -use winsys::common::Grip; -use winsys::common::Hints; -use winsys::common::IcccmWindowState; -use winsys::common::Pid; -use winsys::common::Pos; -use winsys::common::Region; -use winsys::common::Window; -use winsys::common::WindowState; -use winsys::common::WindowType; use winsys::connection::Connection; +use winsys::connection::Pid; use winsys::event::Event; use winsys::event::PropertyKind; use winsys::event::StackMode; use winsys::event::ToggleAction; +use winsys::geometry::Corner; +use winsys::geometry::Dim; +use winsys::geometry::Edge; +use winsys::geometry::Pos; +use winsys::geometry::Region; +use winsys::hints::Hints; use winsys::input::EventTarget; +use winsys::input::Grip; use winsys::input::KeyCode; use winsys::input::MouseEvent; use winsys::input::MouseEventKey; use winsys::input::MouseEventKind; use winsys::screen::Screen; +use winsys::window::IcccmWindowState; +use winsys::window::Window; +use winsys::window::WindowState; +use winsys::window::WindowType; use std::collections::HashMap; @@ -345,26 +343,26 @@ impl<'a> Model<'a> { for placement in show { match placement.kind { - PlacementKind::Client(window) => { + PlacementTarget::Client(window) => { let frame = self.frame(window).unwrap(); self.update_client_placement(&placement); self.place_client(window, placement.method); self.map_client(frame); }, - PlacementKind::Tab(_) => {}, - PlacementKind::Layout => {}, + PlacementTarget::Tab(_) => {}, + PlacementTarget::Layout => {}, }; } for placement in hide { match placement.kind { - PlacementKind::Client(window) => { + PlacementTarget::Client(window) => { let frame = self.frame(window).unwrap(); self.unmap_client(frame); }, - PlacementKind::Tab(_) => {}, - PlacementKind::Layout => {}, + PlacementTarget::Tab(_) => {}, + PlacementTarget::Layout => {}, }; } @@ -679,16 +677,16 @@ impl<'a> Model<'a> { let hints = self.conn.get_icccm_window_hints(window); let (_, size_hints) = self.conn - .get_icccm_window_size_hints(window, Some(MIN_WINDOW_DIM), &None); + .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(&FREE_DECORATION.extents()) + .with_extents(&Decoration::FREE_DECORATION.extents()) } else { geometry - .with_minimum_dim(&MIN_WINDOW_DIM) - .with_extents(&FREE_DECORATION.extents()) + .with_minimum_dim(&Client::MIN_CLIENT_DIM) + .with_extents(&Decoration::FREE_DECORATION.extents()) }; let parent = self.conn.get_icccm_window_transient_for(window); @@ -771,7 +769,7 @@ impl<'a> Model<'a> { client.set_context(context); client.set_workspace(workspace); - let extents = FREE_DECORATION.extents(); + let extents = Decoration::FREE_DECORATION.extents(); self.conn.reparent_window(window, frame, Pos { x: extents.left as i32, y: extents.top as i32, @@ -782,7 +780,7 @@ impl<'a> Model<'a> { if let Some(current_workspace) = self.workspaces.get(workspace) { let parent_zone = current_workspace - .active_zone() + .active_spawn_zone() .map(|id| self.zone_manager.nearest_cycle(id)); let id = self @@ -947,10 +945,12 @@ impl<'a> Model<'a> { let workspace_index = self.active_workspace(); let workspace = self.workspace(workspace_index); - let cycle = workspace.active_zone().unwrap(); + let cycle = workspace.active_focus_zone().unwrap(); let cycle = self.zone_manager.nearest_cycle(cycle); - let id = self.zone_manager.new_zone(Some(cycle), - ZoneContent::Layout(Layout::new(), Cycle::new(Vec::new(), true))); + let id = self.zone_manager.new_zone( + Some(cycle), + ZoneContent::Layout(Layout::new(), Cycle::new(Vec::new(), true)), + ); let workspace = self.workspace_mut(workspace_index); workspace.add_zone(id, &InsertPos::Back); @@ -961,10 +961,11 @@ impl<'a> Model<'a> { let workspace_index = self.active_workspace(); let workspace = self.workspace(workspace_index); - let cycle = workspace.active_zone().unwrap(); + let cycle = workspace.active_focus_zone().unwrap(); let cycle = self.zone_manager.nearest_cycle(cycle); - let id = self.zone_manager.new_zone(Some(cycle), - ZoneContent::Tab(Cycle::new(Vec::new(), true))); + let id = self + .zone_manager + .new_zone(Some(cycle), ZoneContent::Tab(Cycle::new(Vec::new(), true))); let workspace = self.workspace_mut(workspace_index); workspace.add_zone(id, &InsertPos::Back); @@ -975,7 +976,7 @@ impl<'a> Model<'a> { let workspace_index = self.active_workspace(); let workspace = self.workspace(workspace_index); - let cycle = workspace.active_zone().unwrap(); + let cycle = workspace.active_spawn_zone().unwrap(); let cycle = self.zone_manager.nearest_cycle(cycle); if cycle == workspace.root_zone() { @@ -1125,7 +1126,7 @@ impl<'a> Model<'a> { placement: &Placement, ) { match placement.kind { - PlacementKind::Client(window) => { + PlacementTarget::Client(window) => { let client = self.client_mut(window).unwrap(); let region = match placement.region { PlacementRegion::FreeRegion => *client.free_region(), @@ -1651,12 +1652,12 @@ impl<'a> Model<'a> { pub fn change_gap_size( &mut self, - change: Change, + change: Change<u32>, ) -> 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, &mut self.zone_manager)?; } self.apply_layout(workspace_index, true); @@ -1698,7 +1699,7 @@ impl<'a> Model<'a> { pub fn change_main_count( &mut self, - change: Change, + change: Change<u32>, ) -> Result<(), StateChangeError> { let workspace_index = self.active_workspace(); @@ -1712,12 +1713,12 @@ impl<'a> Model<'a> { pub fn change_main_factor( &mut self, - change: Change, + change: Change<f32>, ) -> 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, &mut self.zone_manager)?; } self.apply_layout(workspace_index, true); @@ -1727,12 +1728,12 @@ impl<'a> Model<'a> { pub fn change_margin( &mut self, edge: Edge, - change: Change, + change: Change<i32>, ) -> 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, &mut self.zone_manager)?; } self.apply_layout(workspace_index, true); @@ -1757,7 +1758,7 @@ impl<'a> Model<'a> { let workspace_index = self.active_workspace(); let workspace = self.workspace_mut(workspace_index); - if let Some(id) = workspace.active_zone() { + if let Some(id) = workspace.active_focus_zone() { info!( "activating layout {:?} on workspace {}", kind, workspace_index @@ -1774,7 +1775,7 @@ impl<'a> Model<'a> { let workspace_index = self.active_workspace(); let workspace = self.workspace_mut(workspace_index); - if let Some(id) = workspace.active_zone() { + if let Some(id) = workspace.active_focus_zone() { let prev_kind = self.zone_manager.set_prev_kind(id); info!( @@ -1924,15 +1925,15 @@ impl<'a> Model<'a> { } self.zone_manager.activate_zone(id); + let cycle = self.zone_manager.nearest_cycle(id); - self.workspaces - .get_mut(client_workspace_index) - .and_then(|ws| ws.focus_client(window)); + self.workspaces.get_mut(client_workspace_index).map(|ws| { + ws.activate_zone(cycle); + ws.focus_client(window); + }); - if let Some(config) = self.zone_manager.cycle_config(id) { - if config.persistent { - self.apply_layout(client_workspace_index, false); - } + if self.zone_manager.is_within_persisent(id) { + self.apply_layout(client_workspace_index, false); } if self.conn.get_focused_window() != window { @@ -2390,7 +2391,7 @@ impl<'a> Model<'a> { let placement = Placement { method: PlacementMethod::Free, - kind: PlacementKind::Client(window), + kind: PlacementTarget::Client(window), zone: client.zone(), region: PlacementRegion::NewRegion(region), decoration: *client.decoration(), @@ -2437,7 +2438,7 @@ impl<'a> Model<'a> { let placement = Placement { method: PlacementMethod::Free, - kind: PlacementKind::Client(window), + kind: PlacementTarget::Client(window), zone: client.zone(), region: PlacementRegion::NewRegion(region), decoration: *client.decoration(), @@ -2470,10 +2471,10 @@ impl<'a> Model<'a> { let mut region = region.without_extents(&frame_extents); - if (width_inc.is_negative() && -width_inc >= region.dim.w as i32) - || (height_inc.is_negative() && -height_inc >= region.dim.h as i32) - || (region.dim.w as i32 + width_inc <= MIN_WINDOW_DIM.w as i32) - || (region.dim.h as i32 + height_inc <= MIN_WINDOW_DIM.h as i32) + 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; } @@ -2485,12 +2486,12 @@ impl<'a> Model<'a> { step.abs() ); - region.dim.w = (region.dim.w as i32 + width_inc) as u32; - region.dim.h = (region.dim.h as i32 + height_inc) as u32; + region.dim.w = region.dim.w + width_inc; + region.dim.h = region.dim.h + height_inc; let mut region = region.with_extents(&frame_extents); - let dx = region.dim.w as i32 - original_region.dim.w as i32; - let dy = region.dim.h as i32 - original_region.dim.h as i32; + 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; @@ -2500,7 +2501,7 @@ impl<'a> Model<'a> { let placement = Placement { method: PlacementMethod::Free, - kind: PlacementKind::Client(window), + kind: PlacementTarget::Client(window), zone: client.zone(), region: PlacementRegion::NewRegion(region), decoration: *client.decoration(), @@ -2541,51 +2542,51 @@ impl<'a> Model<'a> { match edge { Edge::Left => { - if step.is_negative() && -step >= region.dim.w as i32 { + if step.is_negative() && -step >= region.dim.w { return; } - if region.dim.w as i32 + step <= MIN_WINDOW_DIM.w as i32 { - region.pos.x -= MIN_WINDOW_DIM.w as i32 - region.dim.w as i32; - region.dim.w = MIN_WINDOW_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 = (region.dim.w as i32 + step) as u32; + region.dim.w = region.dim.w + step; } }, Edge::Right => { - if step.is_negative() && -step >= region.dim.w as i32 { + if step.is_negative() && -step >= region.dim.w { return; } - if region.dim.w as i32 + step <= MIN_WINDOW_DIM.w as i32 { - region.dim.w = MIN_WINDOW_DIM.w; + 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 as i32 + step) as u32; + region.dim.w = region.dim.w + step; } }, Edge::Top => { - if step.is_negative() && -step >= region.dim.h as i32 { + if step.is_negative() && -step >= region.dim.h { return; } - if region.dim.h as i32 + step <= MIN_WINDOW_DIM.h as i32 { - region.pos.y -= MIN_WINDOW_DIM.h as i32 - region.dim.h as i32; - region.dim.h = MIN_WINDOW_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 = (region.dim.h as i32 + step) as u32; + region.dim.h = region.dim.h + step; } }, Edge::Bottom => { - if step.is_negative() && -step >= region.dim.h as i32 { + if step.is_negative() && -step >= region.dim.h { return; } - if region.dim.h as i32 + step <= MIN_WINDOW_DIM.h as i32 { - region.dim.h = MIN_WINDOW_DIM.h; + 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 as i32 + step) as u32; + region.dim.h = region.dim.h + step; } }, } @@ -2594,7 +2595,7 @@ impl<'a> Model<'a> { let region = region.with_extents(&frame_extents); let placement = Placement { method: PlacementMethod::Free, - kind: PlacementKind::Client(window), + kind: PlacementTarget::Client(window), zone: client.zone(), region: PlacementRegion::NewRegion(region), decoration: *client.decoration(), @@ -2650,7 +2651,7 @@ impl<'a> Model<'a> { let placement = Placement { method: PlacementMethod::Free, - kind: PlacementKind::Client(window), + kind: PlacementTarget::Client(window), zone: client.zone(), region: PlacementRegion::NewRegion(region), decoration: *client.decoration(), @@ -2714,19 +2715,19 @@ impl<'a> Model<'a> { let delta = grip_pos.dist(current_pos); let dest_w = if left_grip { - window_region.dim.w as i32 - delta.dx + window_region.dim.w - delta.dx } else { - window_region.dim.w as i32 + delta.dx + window_region.dim.w + delta.dx }; let dest_h = if top_grip { - window_region.dim.h as i32 - delta.dy + window_region.dim.h - delta.dy } else { - window_region.dim.h as i32 + delta.dy + window_region.dim.h + delta.dy }; - dim.w = std::cmp::max(0, dest_w) as u32; - dim.h = std::cmp::max(0, dest_h) as u32; + dim.w = std::cmp::max(0, dest_w); + dim.h = std::cmp::max(0, dest_h); if let Some(size_hints) = client.size_hints() { size_hints.apply(&mut dim); @@ -2739,13 +2740,11 @@ impl<'a> Model<'a> { .with_extents(&decoration.extents()); if top_grip { - region.pos.y = - window_region.pos.y + (window_region.dim.h as i32 - region.dim.h as i32); + 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 as i32 - region.dim.w as i32); + region.pos.x = window_region.pos.x + (window_region.dim.w - region.dim.w); } if region == previous_region { @@ -2755,7 +2754,7 @@ impl<'a> Model<'a> { let window = client.window(); let placement = Placement { method: PlacementMethod::Free, - kind: PlacementKind::Client(window), + kind: PlacementTarget::Client(window), zone: client.zone(), region: PlacementRegion::NewRegion(region), decoration: *decoration, @@ -2935,21 +2934,17 @@ impl<'a> Model<'a> { }, ((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 as i32 - h as i32 => - { + ((_, y), (_, h)) if y == screen.full_region().dim.h - h => { Some((Edge::Bottom, h)) }, - ((x, _), (w, _)) - if x == screen.full_region().dim.w as i32 - w as i32 => - { + ((x, _), (w, _)) if x == screen.full_region().dim.w - w => { Some((Edge::Right, w)) }, _ => None, }; if let Some((edge, width)) = strut { - screen.add_strut(edge, window, width); + screen.add_strut(edge, window, width as u32); if !screen.showing_struts() { self.conn.unmap_window(window); @@ -3280,7 +3275,7 @@ impl<'a> Model<'a> { } else { region .without_extents(&frame_extents) - .with_minimum_dim(&MIN_WINDOW_DIM) + .with_minimum_dim(&Client::MIN_CLIENT_DIM) .with_extents(&frame_extents) } }); @@ -3288,7 +3283,7 @@ impl<'a> Model<'a> { if let Some(region) = region { let placement = Placement { method: PlacementMethod::Free, - kind: PlacementKind::Client(window), + kind: PlacementTarget::Client(window), zone: client.zone(), region: PlacementRegion::NewRegion(region), decoration: *client.decoration(), @@ -3407,14 +3402,14 @@ impl<'a> Model<'a> { let mut geometry = geometry.unwrap(); let (_, size_hints) = self.conn.get_icccm_window_size_hints( window, - Some(MIN_WINDOW_DIM), + 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(&MIN_WINDOW_DIM) + geometry.with_minimum_dim(&Client::MIN_CLIENT_DIM) }; geometry.pos = client.free_region().pos; @@ -3458,9 +3453,9 @@ impl<'a> Model<'a> { client.frame_extents() } else { if self.conn.must_manage_window(window) { - FREE_DECORATION.extents() + Decoration::FREE_DECORATION.extents() } else { - NO_DECORATION.extents() + Decoration::NO_DECORATION.extents() } }, ); diff --git a/src/core/partition.rs b/src/core/partition.rs @@ -1,6 +1,6 @@ -use crate::common::Ident; -use crate::common::Identify; -use crate::common::Index; +use crate::identify::Ident; +use crate::identify::Identify; +use crate::identify::Index; use winsys::screen::Screen; diff --git a/src/core/placement.rs b/src/core/placement.rs @@ -0,0 +1,51 @@ +use crate::decoration::Decoration; +use crate::zone::ZoneContent; +use crate::zone::ZoneId; + +use winsys::geometry::Region; +use winsys::window::Window; + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum PlacementMethod { + Free, + Tile, +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum PlacementClass<T> { + Free(T), + Tile(T), +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum PlacementTarget { + Client(Window), + Tab(usize), + Layout, +} + +impl PlacementTarget { + pub fn from_zone_content(content: &ZoneContent) -> Self { + match content { + ZoneContent::Client(window) => PlacementTarget::Client(*window), + ZoneContent::Tab(zones) => PlacementTarget::Tab(zones.len()), + ZoneContent::Layout(..) => PlacementTarget::Layout, + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum PlacementRegion { + NoRegion, + FreeRegion, + NewRegion(Region), +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct Placement { + pub method: PlacementMethod, + pub kind: PlacementTarget, + pub zone: ZoneId, + pub region: PlacementRegion, + pub decoration: Decoration, +} diff --git a/src/core/stack.rs b/src/core/stack.rs @@ -1,4 +1,4 @@ -use winsys::common::Window; +use winsys::window::Window; use std::collections::HashMap; use std::vec::Vec; diff --git a/src/core/util.rs b/src/core/util.rs @@ -1,5 +1,5 @@ -use crate::common::Change; -use crate::common::Index; +use crate::change::Change; +use crate::identify::Index; use winsys::input::Button; use winsys::input::CodeMap; @@ -12,6 +12,8 @@ use std::hash::BuildHasherDefault; use std::hash::Hasher; use std::ops::Add; use std::ops::AddAssign; +use std::ops::Mul; +use std::ops::MulAssign; use std::ops::Sub; use std::ops::SubAssign; use std::process::Command; @@ -60,14 +62,20 @@ impl Util { min: T, max: T, mut base: T, - change: Change, - delta: T, + change: Change<T>, ) -> T where - T: Ord + Add<Output = T> + AddAssign + Sub<Output = T> + SubAssign + Copy, + T: Ord + + Add<Output = T> + + AddAssign + + Mul<Output = T> + + MulAssign + + Sub<Output = T> + + SubAssign + + Copy, { match change { - Change::Inc => { + Change::Inc(delta) => { base += delta; if base > max { max @@ -75,7 +83,7 @@ impl Util { base } }, - Change::Dec => { + Change::Dec(delta) => { if base >= min + delta { base - delta } else { diff --git a/src/core/workspace.rs b/src/core/workspace.rs @@ -1,30 +1,27 @@ +use crate::change::Change; +use crate::change::Direction; use crate::client::Client; -use crate::common::Change; -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; use crate::cycle::InsertPos; use crate::cycle::Selector; -use crate::zone::Placement; -use crate::zone::PlacementKind; -use crate::zone::PlacementMethod; -use crate::zone::PlacementRegion; +use crate::decoration::Decoration; +use crate::error::StateChangeError; +use crate::identify::Ident; +use crate::identify::Identify; +use crate::identify::Index; +use crate::layout::Layout; +use crate::placement::Placement; +use crate::placement::PlacementMethod; +use crate::placement::PlacementRegion; +use crate::placement::PlacementTarget; use crate::zone::ZoneId; use crate::zone::ZoneManager; -use crate::zone::MAX_GAP_SIZE; -use crate::zone::MAX_MAIN_COUNT; -use crate::zone::MAX_MARGIN; -use winsys::common::Edge; -use winsys::common::Grip; -use winsys::common::Pos; -use winsys::common::Region; -use winsys::common::Window; +use winsys::geometry::Edge; +use winsys::geometry::Pos; +use winsys::geometry::Region; +use winsys::input::Grip; +use winsys::window::Window; use std::collections::HashMap; use std::collections::VecDeque; @@ -142,7 +139,8 @@ pub struct Workspace { number: Ident, name: String, root_zone: ZoneId, - zones: Cycle<ZoneId>, + focus_zones: Cycle<ZoneId>, + spawn_zones: Cycle<ZoneId>, clients: Cycle<Window>, icons: Cycle<Window>, } @@ -157,7 +155,8 @@ impl Workspace { number, name: name.into(), root_zone, - zones: Cycle::new(vec![root_zone], true), + focus_zones: Cycle::new(vec![root_zone], true), + spawn_zones: Cycle::new(vec![root_zone], true), clients: Cycle::new(Vec::new(), true), icons: Cycle::new(Vec::new(), true), } @@ -213,8 +212,12 @@ impl Workspace { self.clients.stack_after_focus() } - pub fn active_zone(&self) -> Option<ZoneId> { - self.zones.active_element().copied() + pub fn active_focus_zone(&self) -> Option<ZoneId> { + self.focus_zones.active_element().copied() + } + + pub fn active_spawn_zone(&self) -> Option<ZoneId> { + self.spawn_zones.active_element().copied() } pub fn focused_client(&self) -> Option<Window> { @@ -229,7 +232,7 @@ impl Workspace { let sel = match sel { ClientSelector::AtActive => Selector::AtActive, ClientSelector::AtMaster => { - if let Some(&id) = self.zones.active_element() { + if let Some(&id) = self.focus_zones.active_element() { let cycle = zone_manager.nearest_cycle(id); let cycle = zone_manager.zone(cycle); @@ -262,7 +265,8 @@ impl Workspace { id: ZoneId, insert: &InsertPos, ) { - self.zones.insert_at(insert, id); + self.focus_zones.insert_at(insert, id); + self.spawn_zones.insert_at(insert, id); } pub fn add_client( @@ -288,12 +292,12 @@ impl Workspace { &mut self, id: ZoneId, ) -> Option<ZoneId> { - let prev_active = match self.zones.active_element() { + let prev_active = match self.focus_zones.active_element() { Some(z) => *z, None => return None, }; - self.zones.activate_for(&Selector::AtIdent(id)); + self.focus_zones.activate_for(&Selector::AtIdent(id)); Some(prev_active) } @@ -313,8 +317,9 @@ impl Workspace { pub fn remove_zone( &mut self, id: ZoneId, - ) -> Option<Window> { - self.zones.remove_for(&Selector::AtIdent(id)) + ) { + self.focus_zones.remove_for(&Selector::AtIdent(id)); + self.spawn_zones.remove_for(&Selector::AtIdent(id)); } pub fn remove_client( @@ -360,25 +365,25 @@ impl Workspace { ( PlacementMethod::Tile, PlacementRegion::NewRegion(screen_region), - NO_DECORATION, + Decoration::NO_DECORATION, ) } else if client.is_iconified() { ( PlacementMethod::Tile, PlacementRegion::NoRegion, - NO_DECORATION, + Decoration::NO_DECORATION, ) - }else { + } else { ( PlacementMethod::Free, PlacementRegion::FreeRegion, - FREE_DECORATION, + Decoration::FREE_DECORATION, ) }; Placement { method, - kind: PlacementKind::Client(client.window()), + kind: PlacementTarget::Client(client.window()), zone: client.zone(), region, decoration, @@ -395,19 +400,19 @@ impl Workspace { dir: Direction, zone_manager: &ZoneManager, ) -> Option<(ZoneId, ZoneId)> { - if self.zones.len() < 2 { + if self.spawn_zones.len() < 2 { return None; } - let prev_active = *self.zones.active_element()?; - let mut now_active = *self.zones.cycle_active(dir)?; + let prev_active = *self.spawn_zones.active_element()?; + let mut now_active = *self.spawn_zones.cycle_active(dir)?; loop { if zone_manager.is_cycle(now_active) { return Some((prev_active, now_active)); } - now_active = *self.zones.cycle_active(dir)?; + now_active = *self.spawn_zones.cycle_active(dir)?; } } @@ -473,7 +478,7 @@ impl Workspace { zone_manager: &mut ZoneManager, ) -> Result<(), StateChangeError> { let &id = self - .zones + .focus_zones .active_element() .ok_or(StateChangeError::EarlyStop)?; @@ -493,7 +498,7 @@ impl Workspace { zone_manager: &mut ZoneManager, ) -> Result<(), StateChangeError> { let &id = self - .zones + .focus_zones .active_element() .ok_or(StateChangeError::EarlyStop)?; @@ -510,12 +515,11 @@ impl Workspace { pub fn change_gap_size( &self, - change: Change, - delta: u32, + change: Change<u32>, zone_manager: &mut ZoneManager, ) -> Result<(), StateChangeError> { let &id = self - .zones + .focus_zones .active_element() .ok_or(StateChangeError::EarlyStop)?; @@ -524,8 +528,8 @@ impl Workspace { .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, + Change::Inc(delta) => std::cmp::min(data.gap_size + delta, Layout::MAX_GAP_SIZE), + Change::Dec(delta) => std::cmp::max(data.gap_size as i32 - delta as i32, 0) as u32, }; if new_gap_size == data.gap_size { @@ -540,7 +544,7 @@ impl Workspace { zone_manager: &mut ZoneManager, ) -> Result<(), StateChangeError> { let &id = self - .zones + .focus_zones .active_element() .ok_or(StateChangeError::EarlyStop)?; @@ -557,11 +561,11 @@ impl Workspace { pub fn change_main_count( &self, - change: Change, + change: Change<u32>, zone_manager: &mut ZoneManager, ) -> Result<(), StateChangeError> { let &id = self - .zones + .focus_zones .active_element() .ok_or(StateChangeError::EarlyStop)?; @@ -570,8 +574,8 @@ impl Workspace { .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, + Change::Inc(delta) => std::cmp::min(data.main_count + delta, Layout::MAX_MAIN_COUNT), + Change::Dec(delta) => std::cmp::max(data.main_count - delta, 0), }; if data.main_count == new_main_count { @@ -583,12 +587,11 @@ impl Workspace { pub fn change_main_factor( &self, - change: Change, - delta: f32, + change: Change<f32>, zone_manager: &mut ZoneManager, ) -> Result<(), StateChangeError> { let &id = self - .zones + .focus_zones .active_element() .ok_or(StateChangeError::EarlyStop)?; @@ -597,8 +600,8 @@ impl Workspace { .ok_or(StateChangeError::EarlyStop)?; match change { - Change::Inc => data.main_factor += delta, - Change::Dec => data.main_factor -= delta, + Change::Inc(delta) => data.main_factor += delta, + Change::Dec(delta) => data.main_factor -= delta, } if data.main_factor < 0.05f32 { @@ -613,12 +616,11 @@ impl Workspace { pub fn change_margin( &self, edge: Edge, - change: Change, - delta: u32, + change: Change<i32>, zone_manager: &mut ZoneManager, ) -> Result<(), StateChangeError> { let &id = self - .zones + .focus_zones .active_element() .ok_or(StateChangeError::EarlyStop)?; @@ -627,25 +629,25 @@ impl Workspace { .ok_or(StateChangeError::EarlyStop)?; let delta_change = match change { - Change::Inc => delta as i32, - Change::Dec => -(delta as i32), + Change::Inc(delta) => delta, + Change::Dec(delta) => -delta, }; 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), + Edge::Left => (&mut data.margin.left, Layout::MAX_MARGIN.left), + Edge::Right => (&mut data.margin.right, Layout::MAX_MARGIN.right), + Edge::Top => (&mut data.margin.top, Layout::MAX_MARGIN.top), + Edge::Bottom => (&mut data.margin.bottom, Layout::MAX_MARGIN.bottom), }; - let edge_changed = *edge_value as i32 + delta_change; + let edge_changed = *edge_value + delta_change as i32; let edge_changed = std::cmp::max(edge_changed, 0); - let edge_changed = std::cmp::min(edge_changed, edge_max as i32); + let edge_changed = std::cmp::min(edge_changed, edge_max); - if *edge_value == edge_changed as u32 { + if *edge_value == edge_changed { Err(StateChangeError::LimitReached) } else { - Ok(*edge_value = edge_changed as u32) + Ok(*edge_value = edge_changed) } } @@ -654,7 +656,7 @@ impl Workspace { zone_manager: &mut ZoneManager, ) -> Result<(), StateChangeError> { let &id = self - .zones + .focus_zones .active_element() .ok_or(StateChangeError::EarlyStop)?; diff --git a/src/core/zone.rs b/src/core/zone.rs @@ -1,930 +1,35 @@ -use crate::common::Border; -use crate::common::Decoration; -use crate::common::Frame; -use crate::common::Ident; -use crate::common::Identify; -use crate::common::StateChangeError; -use crate::common::FREE_DECORATION; -use crate::common::NO_DECORATION; +use crate::change::Disposition; use crate::cycle::Cycle; use crate::cycle::InsertPos; use crate::cycle::Selector; - -use winsys::common::Dim; -use winsys::common::Extents; -use winsys::common::Padding; -use winsys::common::Pos; -use winsys::common::Region; -use winsys::common::Window; - -use strum::EnumCount; -use strum::IntoEnumIterator; -use strum_macros::EnumIter; -use strum_macros::ToString; +use crate::decoration::Border; +use crate::decoration::Decoration; +use crate::error::StateChangeError; +use crate::identify::Ident; +use crate::identify::Identify; +use crate::layout::Apply; +use crate::layout::Layout; +use crate::layout::LayoutConfig; +use crate::layout::LayoutData; +use crate::layout::LayoutKind; +use crate::placement::Placement; +use crate::placement::PlacementMethod; +use crate::placement::PlacementRegion; +use crate::placement::PlacementTarget; + +use winsys::geometry::Region; +use winsys::window::Window; use std::collections::HashMap; -use std::string::ToString; use std::sync::atomic; use std::vec::Vec; pub type ZoneId = u32; -pub const MAX_MAIN_COUNT: u32 = 15; -pub const MAX_GAP_SIZE: u32 = 300; -pub const MAX_MARGIN: Padding = Padding { - left: 700, - right: 700, - top: 400, - 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 } - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -enum Disposition { - Unchanged(Decoration), - Changed(Region, Decoration), -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum PlacementMethod { - /// Does not inhibit free placement of clients - Free, - - /// Arranges clients along a predefined layout - Tile, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum PlacementKind { - Client(Window), - Tab(usize), - Layout, -} - -impl PlacementKind { - pub fn from_zone_content(content: &ZoneContent) -> Self { - match content { - ZoneContent::Client(window) => PlacementKind::Client(*window), - ZoneContent::Tab(zones) => PlacementKind::Tab(zones.len()), - ZoneContent::Layout(..) => PlacementKind::Layout, - } - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum PlacementRegion { - NoRegion, - FreeRegion, - NewRegion(Region), -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct Placement { - pub method: PlacementMethod, - pub kind: PlacementKind, - pub zone: ZoneId, - pub region: PlacementRegion, - pub decoration: Decoration, -} - -type LayoutFn = fn(&Region, &LayoutData, Vec<bool>) -> Vec<(Disposition, bool)>; - -#[non_exhaustive] -#[derive(Debug, PartialEq, Clone, Copy)] -pub struct LayoutConfig { - pub method: PlacementMethod, - pub decoration: Decoration, - pub root_only: bool, - pub margin: bool, - pub gap: bool, - pub persistent: bool, - pub single: bool, - pub wraps: bool, -} - -impl Default for LayoutConfig { - fn default() -> Self { - Self { - method: PlacementMethod::Free, - decoration: Default::default(), - root_only: true, - margin: false, - gap: false, - persistent: false, - single: false, - wraps: true, - } - } -} - -#[non_exhaustive] -#[derive(Debug, PartialEq, Clone, Copy)] -pub struct LayoutData { - /// Generic layout data - pub margin: Padding, - pub gap_size: u32, - - /// Tiled layout data - pub main_count: u32, - pub main_factor: f32, -} - -impl Default for LayoutData { - fn default() -> Self { - Self { - margin: Default::default(), - gap_size: 0u32, - - main_count: 1u32, - main_factor: 0.50f32, - } - } -} - -#[non_exhaustive] -#[repr(u8)] -#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, EnumIter, EnumCount, ToString)] -pub enum LayoutKind { - /// Free layouts - Float = b'f', - BLFloat = b'F', - SingleFloat = b'z', - BLSingleFloat = b'Z', - - /// Tiled layouts - // Overlapping - Center = b';', - Monocle = b'%', - // Non-overlapping - Paper = b'p', - SPaper = b'P', - Stack = b's', - SStack = b'S', - BStack = b'b', - BSStack = b'B', - Horz = b'h', - SHorz = b'H', - Vert = b'v', - SVert = b'V', -} - -impl LayoutKind { - pub fn symbol(&self) -> char { - (*self as u8) as char - } - - pub fn name(&self) -> String { - self.to_string() - } - - fn config(&self) -> LayoutConfig { - match *self { - LayoutKind::Float => LayoutConfig { - method: PlacementMethod::Free, - decoration: FREE_DECORATION, - root_only: true, - margin: false, - gap: false, - persistent: false, - single: false, - wraps: true, - }, - LayoutKind::BLFloat => LayoutConfig { - method: PlacementMethod::Free, - decoration: NO_DECORATION, - root_only: true, - margin: false, - gap: false, - persistent: false, - single: false, - wraps: true, - }, - LayoutKind::SingleFloat => LayoutConfig { - method: PlacementMethod::Free, - decoration: FREE_DECORATION, - root_only: true, - margin: false, - gap: false, - persistent: true, - single: true, - wraps: true, - }, - LayoutKind::BLSingleFloat => LayoutConfig { - method: PlacementMethod::Free, - decoration: NO_DECORATION, - root_only: true, - margin: false, - gap: false, - persistent: true, - single: true, - wraps: true, - }, - LayoutKind::Center => LayoutConfig { - method: PlacementMethod::Tile, - decoration: NO_DECORATION, - root_only: false, - margin: true, - gap: true, - persistent: false, - single: false, - wraps: true, - }, - LayoutKind::Monocle => LayoutConfig { - method: PlacementMethod::Tile, - decoration: NO_DECORATION, - root_only: false, - margin: true, - gap: true, - persistent: false, - single: false, - wraps: true, - }, - LayoutKind::Paper => LayoutConfig { - method: PlacementMethod::Tile, - decoration: Decoration { - frame: Some(Frame { - extents: Extents { - left: 1, - right: 1, - top: 0, - bottom: 0, - }, - colors: Default::default(), - }), - border: None, - }, - root_only: false, - margin: true, - gap: true, - persistent: true, - single: false, - wraps: false, - }, - LayoutKind::SPaper => LayoutConfig { - method: PlacementMethod::Tile, - decoration: Decoration { - frame: Some(Frame { - extents: Extents { - left: 1, - right: 1, - top: 0, - bottom: 0, - }, - colors: Default::default(), - }), - border: None, - }, - root_only: false, - margin: true, - gap: false, - persistent: true, - single: false, - wraps: false, - }, - LayoutKind::Stack => LayoutConfig { - method: PlacementMethod::Tile, - decoration: Decoration { - frame: Some(Frame { - extents: Extents { - left: 0, - right: 0, - top: 3, - bottom: 0, - }, - colors: Default::default(), - }), - border: None, - }, - root_only: false, - margin: true, - gap: true, - persistent: false, - single: false, - wraps: true, - }, - LayoutKind::SStack => LayoutConfig { - method: PlacementMethod::Tile, - decoration: Decoration { - frame: Some(Frame { - extents: Extents { - left: 0, - right: 0, - top: 3, - bottom: 0, - }, - colors: Default::default(), - }), - border: None, - }, - root_only: false, - margin: true, - gap: false, - persistent: false, - single: false, - wraps: true, - }, - LayoutKind::BStack => LayoutConfig { - method: PlacementMethod::Tile, - decoration: Decoration { - frame: Some(Frame { - extents: Extents { - left: 0, - right: 0, - top: 3, - bottom: 0, - }, - colors: Default::default(), - }), - border: None, - }, - root_only: false, - margin: true, - gap: true, - persistent: false, - single: false, - wraps: true, - }, - LayoutKind::BSStack => LayoutConfig { - method: PlacementMethod::Tile, - decoration: Decoration { - frame: Some(Frame { - extents: Extents { - left: 0, - right: 0, - top: 3, - bottom: 0, - }, - colors: Default::default(), - }), - border: None, - }, - root_only: false, - margin: true, - gap: false, - persistent: false, - single: false, - wraps: true, - }, - LayoutKind::Horz => LayoutConfig { - method: PlacementMethod::Tile, - decoration: Decoration { - frame: Some(Frame { - extents: Extents { - left: 0, - right: 0, - top: 3, - bottom: 0, - }, - colors: Default::default(), - }), - border: None, - }, - root_only: false, - margin: true, - gap: true, - persistent: false, - single: false, - wraps: true, - }, - LayoutKind::SHorz => LayoutConfig { - method: PlacementMethod::Tile, - decoration: Decoration { - frame: Some(Frame { - extents: Extents { - left: 0, - right: 0, - top: 3, - bottom: 0, - }, - colors: Default::default(), - }), - border: None, - }, - root_only: false, - margin: true, - gap: false, - persistent: false, - single: false, - wraps: true, - }, - LayoutKind::Vert => LayoutConfig { - method: PlacementMethod::Tile, - decoration: Decoration { - frame: Some(Frame { - extents: Extents { - left: 0, - right: 0, - top: 3, - bottom: 0, - }, - colors: Default::default(), - }), - border: None, - }, - root_only: false, - margin: true, - gap: true, - persistent: false, - single: false, - wraps: true, - }, - LayoutKind::SVert => LayoutConfig { - method: PlacementMethod::Tile, - decoration: Decoration { - frame: Some(Frame { - extents: Extents { - left: 0, - right: 0, - top: 3, - bottom: 0, - }, - colors: Default::default(), - }), - border: None, - }, - root_only: false, - margin: true, - gap: false, - persistent: false, - single: false, - wraps: true, - }, - - #[allow(unreachable_patterns)] - _ => unimplemented!("{:?} does not have an associated configuration", self), - } - } - - fn default_data(&self) -> LayoutData { - match *self { - LayoutKind::Float => Default::default(), - LayoutKind::BLFloat => Default::default(), - LayoutKind::SingleFloat => Default::default(), - LayoutKind::BLSingleFloat => Default::default(), - LayoutKind::Center => LayoutData { - main_count: 5u32, - main_factor: 0.40f32, - ..Default::default() - }, - LayoutKind::Monocle => Default::default(), - LayoutKind::Paper => Default::default(), - LayoutKind::SPaper => Default::default(), - LayoutKind::Stack => LayoutData { - main_count: 1u32, - main_factor: 0.50f32, - ..Default::default() - }, - LayoutKind::SStack => LayoutData { - main_count: 1u32, - main_factor: 0.50f32, - ..Default::default() - }, - LayoutKind::BStack => LayoutData { - main_count: 1u32, - main_factor: 0.50f32, - ..Default::default() - }, - LayoutKind::BSStack => LayoutData { - main_count: 1u32, - main_factor: 0.50f32, - ..Default::default() - }, - LayoutKind::Horz => Default::default(), - LayoutKind::SHorz => Default::default(), - LayoutKind::Vert => Default::default(), - LayoutKind::SVert => Default::default(), - - #[allow(unreachable_patterns)] - _ => unimplemented!("{:?} does not have associated default data", self), - } - } - - #[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 => |_, _, active_map| { - let config = &LayoutKind::Float.config(); - vec![(Disposition::Unchanged(config.decoration), true); active_map.len()] - }, - LayoutKind::BLFloat => |_, _, active_map| { - let config = &LayoutKind::BLFloat.config(); - vec![(Disposition::Unchanged(config.decoration), true); active_map.len()] - }, - LayoutKind::SingleFloat => |_, _, active_map| { - let config = &LayoutKind::SingleFloat.config(); - active_map - .into_iter() - .map(|b| (Disposition::Unchanged(config.decoration), b)) - .collect() - }, - LayoutKind::BLSingleFloat => |_, _, active_map| { - let config = &LayoutKind::BLSingleFloat.config(); - active_map - .into_iter() - .map(|b| (Disposition::Unchanged(config.decoration), b)) - .collect() - }, - LayoutKind::Center => |region, data, active_map| { - let config = &LayoutKind::Center.config(); - let (pos, dim) = region.values(); - - let h_comp = MAX_MAIN_COUNT + 1; - let w_ratio: f32 = data.main_factor / 0.95; - let h_ratio: f32 = (h_comp - data.main_count) as f32 / h_comp as f32; - - active_map - .into_iter() - .map(|_| { - ( - Disposition::Changed( - Region { - pos, - dim, - } - .from_absolute_inner_center(&Dim { - w: (dim.w as f32 * w_ratio) as u32, - h: (dim.h as f32 * h_ratio) as u32, - }), - config.decoration, - ), - true, - ) - }) - .collect() - }, - LayoutKind::Monocle => |region, _, active_map| { - let config = &LayoutKind::Monocle.config(); - let (pos, dim) = region.values(); - - active_map - .into_iter() - .map(|_| { - ( - Disposition::Changed( - Region { - pos, - dim, - }, - config.decoration, - ), - true, - ) - }) - .collect() - }, - LayoutKind::Paper => |region, data, active_map| { - const MIN_W_RATIO: f32 = 0.5; - - let config = &LayoutKind::Paper.config(); - let (pos, dim) = region.values(); - let n = active_map.len(); - - if n == 1 { - return vec![(Disposition::Changed(*region, NO_DECORATION), true)]; - } - - let cw = (dim.w as f32 - * if data.main_factor > MIN_W_RATIO { - data.main_factor - } else { - MIN_W_RATIO - }) as u32; - - let w = ((dim.w - cw) as usize / (n - 1)) as i32; - let mut after_active = false; - - active_map - .into_iter() - .enumerate() - .map(|(i, active)| { - if active { - after_active = true; - - ( - Disposition::Changed( - Region::new(pos.x + i as i32 * w, pos.y, cw, dim.h), - config.decoration, - ), - true, - ) - } else { - let mut x = pos.x + i as i32 * w; - - if after_active { - x += cw as i32 - w; - } - - ( - Disposition::Changed( - Region::new(x, pos.y, w as u32, dim.h), - config.decoration, - ), - true, - ) - } - }) - .collect() - }, - LayoutKind::SPaper => |region, data, active_map| { - let mut region = region.clone(); - Layout::adjust_for_gap_size(&mut region, data.gap_size, &MIN_ZONE_DIM); - - (Self::Paper.func())(&region, data, active_map) - }, - LayoutKind::Stack => |region, data, active_map| { - let (pos, dim) = region.values(); - let n = active_map.len(); - - if n == 1 { - return vec![(Disposition::Changed(*region, NO_DECORATION), true)]; - } - - 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 }; - - let div = if data.main_count > 0 { - (dim.w as f32 * data.main_factor) as i32 - } else { - 0 - }; - - let config = &LayoutKind::Stack.config(); - active_map - .into_iter() - .enumerate() - .map(|(i, _)| { - let i = i as u32; - - if i < data.main_count { - let w = if n_stack == 0 { dim.w } else { div as u32 }; - - ( - Disposition::Changed( - Region::new(pos.x, pos.y + (i * h_main) as i32, w, h_main), - config.decoration, - ), - true, - ) - } else { - let sn = (i - data.main_count) as i32; - - ( - Disposition::Changed( - Region::new( - pos.x + div, - pos.y + sn * h_stack as i32, - dim.w - div as u32, - h_stack, - ), - config.decoration, - ), - true, - ) - } - }) - .collect() - }, - LayoutKind::SStack => |region, data, active_map| { - let mut region = region.clone(); - Layout::adjust_for_gap_size(&mut region, data.gap_size, &MIN_ZONE_DIM); - - (Self::Stack.func())(&region, data, active_map) - }, - LayoutKind::BStack => |region, _data, active_map| { - let (_pos, _dim) = region.values(); - let n = active_map.len(); - - if n == 1 { - return vec![(Disposition::Changed(*region, NO_DECORATION), true)]; - } - - todo!() - }, - LayoutKind::BSStack => |region, data, active_map| { - let mut region = region.clone(); - Layout::adjust_for_gap_size(&mut region, data.gap_size, &MIN_ZONE_DIM); - - (Self::BStack.func())(&region, data, active_map) - }, - LayoutKind::Horz => |_region, _data, _active_map| { - todo!() - }, - LayoutKind::SHorz => |_region, _data, _active_map| { - todo!() - }, - LayoutKind::Vert => |_region, _data, _active_map| { - todo!() - }, - LayoutKind::SVert => |_region, _data, _active_map| { - todo!() - }, - - #[allow(unreachable_patterns)] - _ => unimplemented!("{:?} does not have an associated function", self), - } - } -} - -pub struct Layout { - kind: LayoutKind, - prev_kind: LayoutKind, - data: HashMap<LayoutKind, LayoutData>, -} - -impl Layout { - #[inline] - pub fn new() -> Self { - let kind = LayoutKind::Stack; - let mut data = HashMap::with_capacity(LayoutKind::COUNT); - - for kind in LayoutKind::iter() { - data.insert(kind, kind.default_data()); - } - - Self { - kind, - prev_kind: kind, - data, - } - } - - #[inline] - pub fn with_kind(kind: LayoutKind) -> Self { - let mut data = HashMap::with_capacity(LayoutKind::COUNT); - - for kind in LayoutKind::iter() { - data.insert(kind, kind.default_data()); - } - - Self { - kind, - prev_kind: kind, - data, - } - } - - #[inline] - fn config(&self) -> LayoutConfig { - self.kind.config() - } - - #[inline] - fn prev_data(&self) -> &LayoutData { - self.data.get(&self.prev_kind).unwrap() - } - - #[inline] - fn data(&self) -> &LayoutData { - self.data.get(&self.kind).unwrap() - } - - #[inline] - fn data_mut(&mut self) -> &mut LayoutData { - self.data.get_mut(&self.kind).unwrap() - } - - #[inline] - fn default_data(&self) -> LayoutData { - self.kind.default_data() - } - - #[inline] - fn set_kind( - &mut self, - kind: LayoutKind, - ) -> Result<LayoutKind, StateChangeError> { - if kind == self.kind { - return Err(StateChangeError::EarlyStop); - } - - self.prev_kind = self.kind; - self.kind = kind; - - Ok(self.prev_kind) - } - - #[inline] - fn adjust_for_margin( - region: Region, - extents: &Extents, - ) -> Region { - Region { - pos: Pos { - x: region.pos.x + extents.left as i32, - y: region.pos.y + extents.top as i32, - }, - dim: Dim { - w: region.dim.w - extents.left - extents.right, - h: region.dim.h - extents.top - extents.bottom, - }, - } - } - - #[inline] - fn adjust_for_gap_size( - region: &mut Region, - gap_size: u32, - min_dim: &Dim, - ) { - 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; - } - } -} - -impl Default for Layout { - fn default() -> Self { - Self { - kind: LayoutKind::Stack, - prev_kind: LayoutKind::Stack, - data: HashMap::new(), - } - } -} - -trait Apply { - fn apply( - &self, - region: Region, - active_map: Vec<bool>, - ) -> (PlacementMethod, Vec<(Disposition, bool)>); -} - -impl Apply for Layout { - fn apply( - &self, - region: Region, - active_map: Vec<bool>, - ) -> (PlacementMethod, Vec<(Disposition, bool)>) { - let config = self.kind.config(); - let data = self.data(); - - let region = if config.margin { - Self::adjust_for_margin(region, &data.margin) - } else { - region - }; - - ( - config.method, - (self.kind.func())(&region, &data, active_map) - .into_iter() - .map(|(mut disposition, is_visible)| { - if config.gap { - match disposition { - Disposition::Unchanged(_) => {}, - Disposition::Changed(ref mut region, _) => { - Self::adjust_for_gap_size(region, data.gap_size, &MIN_ZONE_DIM); - }, - } - } - - (disposition, is_visible) - }) - .collect(), - ) - } -} - #[derive(Debug, PartialEq)] pub enum ZoneContent { Client(Window), @@ -957,7 +62,7 @@ impl Zone { method: PlacementMethod::Free, content, region, - decoration: NO_DECORATION, + decoration: Decoration::NO_DECORATION, is_visible: true, }) } @@ -981,14 +86,14 @@ impl Zone { pub fn prev_kind(&self) -> Result<LayoutKind, StateChangeError> { match &self.content { - ZoneContent::Layout(layout, _) => Ok(layout.prev_kind), + ZoneContent::Layout(layout, _) => Ok(layout.prev_kind()), _ => Err(StateChangeError::InvalidCaller), } } pub fn kind(&self) -> Result<LayoutKind, StateChangeError> { match &self.content { - ZoneContent::Layout(layout, _) => Ok(layout.kind), + ZoneContent::Layout(layout, _) => Ok(layout.kind()), _ => Err(StateChangeError::InvalidCaller), } } @@ -1037,7 +142,7 @@ impl Zone { pub fn config(&self) -> Option<LayoutConfig> { match self.content { - ZoneContent::Layout(ref layout, _) => Some(layout.kind.config()), + ZoneContent::Layout(ref layout, _) => Some(layout.config()), _ => None, } } @@ -1238,11 +343,11 @@ impl ZoneManager { pub fn cycle_config( &self, id: ZoneId, - ) -> Option<LayoutConfig> { - let cycle = self.nearest_cycle(id); + ) -> Option<(ZoneId, Option<LayoutConfig>)> { + let cycle = self.next_cycle(id)?; let zone = self.zone(cycle); - zone.config() + Some((cycle, zone.config())) } pub fn is_cycle( @@ -1299,6 +404,31 @@ impl ZoneManager { None } + pub fn is_within_persisent( + &self, + mut id: ZoneId, + ) -> bool { + while let Some(next_id) = self.parent_id(id) { + let zone = self.zone_map.get(&next_id).unwrap(); + + match zone.content { + ZoneContent::Tab(_) => { + return true; + }, + ZoneContent::Layout(ref layout, _) => { + if layout.config().persistent { + return true; + } + }, + _ => {}, + } + + id = next_id; + } + + false + } + fn gather_subzones( &self, zone: ZoneId, @@ -1342,7 +472,7 @@ impl ZoneManager { let method = match &zone.content { ZoneContent::Tab(_) => PlacementMethod::Tile, - ZoneContent::Layout(layout, _) => layout.kind.config().method, + ZoneContent::Layout(layout, _) => layout.config().method, _ => panic!("attempting to derive method from non-cycle"), }; @@ -1366,7 +496,7 @@ impl ZoneManager { ZoneContent::Client(window) => { return vec![Placement { method, - kind: PlacementKind::Client(*window), + kind: PlacementTarget::Client(*window), zone: id, region: if method == PlacementMethod::Free { PlacementRegion::FreeRegion @@ -1379,7 +509,7 @@ impl ZoneManager { ZoneContent::Tab(zones) => { let mut placements = vec![Placement { method, - kind: PlacementKind::Tab(zones.len()), + kind: PlacementTarget::Tab(zones.len()), zone: id, region: if method == PlacementMethod::Free { PlacementRegion::FreeRegion @@ -1390,62 +520,50 @@ impl ZoneManager { decoration, }]; - let active_element = zones.active_element(); - active_element.into_iter().for_each(|&id| { - zone_changes.push((id, ZoneChange::Visible(true))); - - self.gather_subzones(id, true).into_iter().for_each(|id| { - zone_changes.push((id, ZoneChange::Visible(true))); - }); - }); + let mut region = region; + Layout::adjust_for_border(&mut region, 1, &Zone::MIN_ZONE_DIM); - zones + let active_element = zones.active_element().copied(); + let zones: Vec<ZoneId> = zones .iter() - .filter(|&id| Some(id) != active_element) - .for_each(|&id| { - zone_changes.push((id, ZoneChange::Visible(false))); - - self.gather_subzones(id, true).into_iter().for_each(|id| { - zone_changes.push((id, ZoneChange::Visible(false))); - }); - }); + .filter(|&id| !to_ignore.contains(id)) + .copied() + .collect(); - match active_element { - None => placements, - Some(&id) => { - let subzones = self.gather_subzones(id, true); - let method = PlacementMethod::Tile; + zones.into_iter().for_each(|id| { + let is_active_element = Some(id) == active_element; + let subzones = self.gather_subzones(id, !is_active_element); + let method = PlacementMethod::Tile; - subzones.into_iter().for_each(|id| { - zone_changes.push((id, ZoneChange::Visible(true))); - zone_changes.push((id, ZoneChange::Region(region))); - zone_changes.push((id, ZoneChange::Method(method))); - }); + subzones.into_iter().for_each(|id| { + zone_changes.push((id, ZoneChange::Visible(is_active_element))); + zone_changes.push((id, ZoneChange::Region(region))); + zone_changes.push((id, ZoneChange::Method(method))); + }); - placements.extend(self.arrange_subzones( - id, - region, - Decoration { - frame: None, - border: Some(Border { - width: 1, - colors: Default::default(), - }), - }, - method, - to_ignore, - )); + placements.extend(self.arrange_subzones( + id, + region, + Decoration { + frame: None, + border: Some(Border { + width: 1, + colors: Default::default(), + }), + }, + method, + to_ignore, + )); + }); - placements - }, - } + placements }, ZoneContent::Layout(layout, zones) => { let active_element = zones.active_element(); let mut subplacements = Vec::new(); let mut placements = vec![Placement { method, - kind: PlacementKind::Layout, + kind: PlacementTarget::Layout, zone: id, region: if method == PlacementMethod::Free { PlacementRegion::FreeRegion @@ -1458,7 +576,7 @@ impl ZoneManager { let zones: Vec<ZoneId> = zones .iter() .filter(|&id| layout.config().single || !to_ignore.contains(id)) - .map(|&id| id) + .copied() .collect(); let (method, application) = layout.apply( @@ -1481,7 +599,7 @@ impl ZoneManager { placements.push(Placement { method, - kind: PlacementKind::from_zone_content(&self.zone(id).content), + kind: PlacementTarget::from_zone_content(&self.zone(id).content), zone: id, region: PlacementRegion::NoRegion, decoration, @@ -1493,7 +611,7 @@ impl ZoneManager { .map(|id| { placements.push(Placement { method, - kind: PlacementKind::from_zone_content( + kind: PlacementTarget::from_zone_content( &self.zone(id).content, ), zone: id, @@ -1567,31 +685,3 @@ impl Identify for Zone { self.id as Ident } } - -impl std::cmp::PartialEq<Self> for Layout { - fn eq( - &self, - other: &Self, - ) -> bool { - self.kind == other.kind && self.data.get(&self.kind) == other.data.get(&other.kind) - } -} - -impl Identify for Layout { - fn id(&self) -> Ident { - self.kind as Ident - } -} - -impl std::fmt::Debug for Layout { - fn fmt( - &self, - f: &mut std::fmt::Formatter<'_>, - ) -> std::fmt::Result { - f.debug_struct("Layout") - .field("kind", &self.kind) - .field("prev_kind", &self.prev_kind) - .field("data", &self.data.get(&self.kind)) - .finish() - } -} diff --git a/src/winsys/common.rs b/src/winsys/common.rs @@ -1,1073 +0,0 @@ -use std::default::Default; -use std::ops::Add; -use std::ops::AddAssign; -use std::ops::Sub; -use std::ops::SubAssign; - -pub type Atom = u32; -pub type Window = u32; -pub type Pid = u32; -pub type Extents = Padding; - -pub struct Hex32(pub u32); - -impl std::fmt::Debug for Hex32 { - fn fmt( - &self, - f: &mut std::fmt::Formatter<'_>, - ) -> std::fmt::Result { - write!(f, "{:#0x}", &self.0) - } -} - -#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)] -pub enum IcccmWindowState { - Withdrawn, - Normal, - Iconic, -} - -#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)] -pub enum WindowState { - Modal, - Sticky, - MaximizedVert, - MaximizedHorz, - Shaded, - SkipTaskbar, - SkipPager, - Hidden, - Fullscreen, - Above, - Below, - DemandsAttention, -} - -#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)] -pub enum WindowType { - Desktop, - Dock, - Toolbar, - Menu, - Utility, - Splash, - Dialog, - DropdownMenu, - PopupMenu, - Tooltip, - Notification, - Combo, - Dnd, - Normal, -} - -#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)] -pub enum Grip { - Edge(Edge), - Corner(Corner), -} - -impl Grip { - pub fn is_top_grip(&self) -> bool { - *self == Grip::Edge(Edge::Top) - || *self == Grip::Corner(Corner::TopLeft) - || *self == Grip::Corner(Corner::TopRight) - } - - pub fn is_left_grip(&self) -> bool { - *self == Grip::Edge(Edge::Left) - || *self == Grip::Corner(Corner::TopLeft) - || *self == Grip::Corner(Corner::BottomLeft) - } -} - -#[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Hash)] -pub enum Edge { - Left, - Right, - Top, - Bottom, -} - -#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)] -pub enum Corner { - TopLeft, - TopRight, - BottomLeft, - BottomRight, -} - -#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)] -pub struct Region { - pub pos: Pos, - pub dim: Dim, -} - -impl Default for Region { - fn default() -> Self { - Self { - pos: Default::default(), - dim: Default::default(), - } - } -} - -impl Region { - pub fn new( - x: i32, - y: i32, - w: u32, - h: u32, - ) -> Self { - Self { - pos: Pos { - x, - y, - }, - dim: Dim { - w, - h, - }, - } - } - - pub fn values(&self) -> (Pos, Dim) { - (self.pos, self.dim) - } - - pub fn with_size_hints( - self, - size_hints: &Option<SizeHints>, - ) -> Self { - let mut geometry = self; - - if let Some(size_hints) = size_hints { - size_hints.apply(&mut geometry.dim); - } - - geometry - } - - pub fn encompasses( - &self, - pos: Pos, - ) -> bool { - pos.x >= self.pos.x - && pos.y >= self.pos.y - && pos.x <= self.pos.x + self.dim.w as i32 - && pos.y <= self.pos.y + self.dim.h as i32 - } - - pub fn contains( - &self, - region: Region, - ) -> bool { - self.encompasses(region.pos) && self.encompasses(region.bottom_right()) - } - - pub fn occludes( - &self, - region: Region, - ) -> bool { - self.encompasses(region.pos) || region.encompasses(self.pos) - } - - pub fn nearest_corner( - &self, - mut pos: Pos, - ) -> Corner { - pos += self.pos.dist(Pos { - x: 0, - y: 0, - }); - self.dim.nearest_corner(pos) - } - - pub fn quadrant_center_from_pos( - &self, - pos: Pos, - ) -> Option<Pos> { - if self.encompasses(pos) { - return None; - } - - let mut dists = vec![ - (Corner::TopLeft, self.pos.dist(pos).pythagorean()), - (Corner::TopRight, self.top_right().dist(pos).pythagorean()), - ( - Corner::BottomLeft, - self.bottom_left().dist(pos).pythagorean(), - ), - ( - Corner::BottomRight, - self.bottom_right().dist(pos).pythagorean(), - ), - ]; - - dists.sort_by_key(|&(corner, dist)| dist); - - match dists.first().unwrap() { - (Corner::TopLeft, _) => { - let (left, _) = self.split_at_width((self.dim.w as f64 / 2f64).round() as u32); - let (topleft, _) = left.split_at_height((left.dim.h as f64 / 2f64).round() as u32); - - Some(Pos::from_center_of_region(topleft)) - }, - (Corner::TopRight, _) => { - let (_, right) = self.split_at_width((self.dim.w as f64 / 2f64).round() as u32); - let (topright, _) = - right.split_at_height((right.dim.h as f64 / 2f64).round() as u32); - - Some(Pos::from_center_of_region(topright)) - }, - (Corner::BottomLeft, _) => { - let (left, _) = self.split_at_width((self.dim.w as f64 / 2f64).round() as u32); - let (_, bottomleft) = - left.split_at_height((left.dim.h as f64 / 2f64).round() as u32); - - Some(Pos::from_center_of_region(bottomleft)) - }, - (Corner::BottomRight, _) => { - let (_, right) = self.split_at_width((self.dim.w as f64 / 2f64).round() as u32); - let (_, bottomright) = - right.split_at_height((right.dim.h as f64 / 2f64).round() as u32); - - Some(Pos::from_center_of_region(bottomright)) - }, - } - } - - pub fn split_at_width( - &self, - width: u32, - ) -> (Self, Self) { - assert!(width < self.dim.w, "desired width exceeds divisible width."); - - ( - Self { - dim: Dim { - w: width, - ..self.dim - }, - ..*self - }, - Self { - pos: Pos { - x: self.pos.x + width as i32, - ..self.pos - }, - dim: Dim { - w: self.dim.w - width, - ..self.dim - }, - }, - ) - } - - pub fn split_at_height( - &self, - height: u32, - ) -> (Self, Self) { - assert!( - height < self.dim.h, - "desired height exceeds divisible height." - ); - - ( - Self { - dim: Dim { - h: height, - ..self.dim - }, - ..*self - }, - Self { - pos: Pos { - y: self.pos.y + height as i32, - ..self.pos - }, - dim: Dim { - h: self.dim.h - height, - ..self.dim - }, - }, - ) - } - - pub fn with_minimum_dim( - self, - minimum_dim: &Dim, - ) -> Self { - Self { - pos: self.pos, - dim: Dim { - w: std::cmp::max(minimum_dim.w, self.dim.w), - h: std::cmp::max(minimum_dim.h, self.dim.h), - }, - } - } - - pub fn with_maximum_dim( - self, - maximum_dim: &Dim, - ) -> Self { - Self { - pos: self.pos, - dim: Dim { - w: std::cmp::min(maximum_dim.w, self.dim.w), - h: std::cmp::min(maximum_dim.h, self.dim.h), - }, - } - } - - pub fn from_absolute_inner_center( - 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, - }, - dim: *dim, - } - } - - pub fn without_extents( - mut self, - extents: &Extents, - ) -> Self { - self.pos.x += extents.left as i32; - self.pos.y += extents.top as i32; - self.dim.w -= extents.left + extents.right; - self.dim.h -= extents.top + extents.bottom; - self - } - - pub fn with_extents( - mut self, - extents: &Extents, - ) -> Self { - self.pos.x -= extents.left as i32; - self.pos.y -= extents.top as i32; - self.dim.w += extents.left + extents.right; - self.dim.h += extents.top + extents.bottom; - self - } - - pub fn top_right(&self) -> Pos { - Pos { - x: self.pos.x + self.dim.w as i32, - y: self.pos.y, - } - } - - pub fn bottom_left(&self) -> Pos { - Pos { - x: self.pos.x, - y: self.pos.y + self.dim.h as i32, - } - } - - pub fn bottom_right(&self) -> Pos { - Pos { - x: self.pos.x + self.dim.w as i32, - y: self.pos.y + self.dim.h as i32, - } - } -} - -#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)] -pub struct Pos { - pub x: i32, - pub y: i32, -} - -impl Default for Pos { - fn default() -> Self { - Self { - x: 0, - y: 0, - } - } -} - -impl Pos { - pub fn from_center_of_region(region: Region) -> Self { - Self { - x: region.pos.x + (region.dim.w as f32 / 2f32) as i32, - y: region.pos.y + (region.dim.h as f32 / 2f32) as i32, - } - } - - pub fn from_center_of_dim(dim: Dim) -> Self { - Self { - x: dim.w as i32 / 2, - y: dim.h as i32 / 2, - } - } - - pub fn values(&self) -> (i32, i32) { - (self.x, self.y) - } - - pub fn dist( - &self, - pos: Self, - ) -> Distance { - Distance { - dx: (pos.x - self.x), - dy: (pos.y - self.y), - } - } - - pub fn relative_to( - &self, - pos: Self, - ) -> Self { - Pos { - x: self.x - pos.x, - y: self.y - pos.y, - } - } -} - -impl Add<Pos> for Pos { - type Output = Self; - - fn add( - self, - other: Pos, - ) -> Self::Output { - Self::Output { - x: self.x + other.x, - y: self.y + other.y, - } - } -} - -#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)] -pub struct Dim { - pub w: u32, - pub h: u32, -} - -impl Default for Dim { - fn default() -> Self { - Self { - w: 480, - h: 260, - } - } -} - -impl Dim { - pub fn values(&self) -> (u32, u32) { - (self.w, self.h) - } - - pub fn center(&self) -> Pos { - Pos { - x: (self.w as f32 / 2f32) as i32, - y: (self.h as f32 / 2f32) as i32, - } - } - - pub fn nearest_corner( - &self, - pos: Pos, - ) -> Corner { - let center = self.center(); - - if pos.x >= center.x { - if pos.y >= center.y { - Corner::BottomRight - } else { - Corner::TopRight - } - } else { - if pos.y >= center.y { - Corner::BottomLeft - } else { - Corner::TopLeft - } - } - } -} - -impl Add<Dim> for Pos { - type Output = Self; - - fn add( - self, - other: Dim, - ) -> Self::Output { - Self::Output { - x: self.x + other.w as i32, - y: self.y + other.h as i32, - } - } -} - -impl Sub<Dim> for Pos { - type Output = Self; - - fn sub( - self, - other: Dim, - ) -> Self::Output { - Self::Output { - x: self.x - other.w as i32, - y: self.y - other.h as i32, - } - } -} - -impl Sub for Pos { - type Output = Dim; - - fn sub( - self, - other: Self, - ) -> Self::Output { - Self::Output { - w: (self.x as i32 - other.x) as u32, - h: (self.y as i32 - other.y) as u32, - } - } -} - -#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)] -pub struct Distance { - pub dx: i32, - pub dy: i32, -} - -impl Distance { - pub fn values(&self) -> (i32, i32) { - (self.dx, self.dy) - } - - pub fn pythagorean(&self) -> u32 { - let dx = self.dx.pow(2) as f64; - let dy = self.dy.pow(2) as f64; - - (dx + dy).sqrt().round() as u32 - } -} - -impl Add<Distance> for Pos { - type Output = Self; - - fn add( - self, - dist: Distance, - ) -> Self::Output { - Self::Output { - x: self.x + dist.dx, - y: self.y + dist.dy, - } - } -} - -impl AddAssign<Distance> for Pos { - fn add_assign( - &mut self, - dist: Distance, - ) { - *self = Self { - x: self.x + dist.dx, - y: self.y + dist.dy, - }; - } -} - -impl Sub<Distance> for Pos { - type Output = Self; - - fn sub( - self, - dist: Distance, - ) -> Self::Output { - Self::Output { - x: self.x - dist.dx, - y: self.y - dist.dy, - } - } -} - -impl SubAssign<Distance> for Pos { - fn sub_assign( - &mut self, - dist: Distance, - ) { - *self = Self { - x: self.x - dist.dx, - y: self.y - dist.dy, - }; - } -} - -impl Add<Distance> for Dim { - type Output = Self; - - fn add( - self, - dist: Distance, - ) -> Self::Output { - Self::Output { - w: (self.w as i32 + dist.dx).abs() as u32, - h: (self.h as i32 + dist.dy).abs() as u32, - } - } -} - -impl AddAssign<Distance> for Dim { - fn add_assign( - &mut self, - dist: Distance, - ) { - *self = Self { - w: (self.w as i32 + dist.dx).abs() as u32, - h: (self.h as i32 + dist.dy).abs() as u32, - }; - } -} - -impl Sub<Distance> for Dim { - type Output = Self; - - fn sub( - self, - dist: Distance, - ) -> Self::Output { - Self::Output { - w: (self.w as i32 - dist.dx).abs() as u32, - h: (self.h as i32 - dist.dy).abs() as u32, - } - } -} - -impl SubAssign<Distance> for Dim { - fn sub_assign( - &mut self, - dist: Distance, - ) { - *self = Self { - w: (self.w as i32 - dist.dx).abs() as u32, - h: (self.h as i32 - dist.dy).abs() as u32, - }; - } -} - -#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)] -pub struct Ratio { - pub numerator: i32, - pub denominator: i32, -} - -impl Ratio { - pub fn new( - numerator: i32, - denominator: i32, - ) -> Self { - Self { - numerator, - denominator, - } - } -} - -#[derive(Debug, Copy, Clone, PartialOrd)] -pub struct SizeHints { - pub by_user: bool, - pub pos: Option<Pos>, - pub min_width: Option<u32>, - pub min_height: Option<u32>, - pub max_width: Option<u32>, - pub max_height: Option<u32>, - pub base_width: Option<u32>, - pub base_height: Option<u32>, - pub inc_width: Option<u32>, - pub inc_height: Option<u32>, - pub min_ratio: Option<f64>, - pub max_ratio: Option<f64>, - pub min_ratio_vulgar: Option<Ratio>, - pub max_ratio_vulgar: Option<Ratio>, -} - -impl SizeHints { - fn new( - by_user: bool, - pos: Option<Pos>, - min_width: Option<u32>, - min_height: Option<u32>, - max_width: Option<u32>, - max_height: Option<u32>, - base_width: Option<u32>, - base_height: Option<u32>, - inc_width: Option<u32>, - inc_height: Option<u32>, - min_ratio: Option<f64>, - max_ratio: Option<f64>, - min_ratio_vulgar: Option<Ratio>, - max_ratio_vulgar: Option<Ratio>, - ) -> Self { - Self { - by_user, - pos, - min_width, - min_height, - max_width, - max_height, - base_width, - base_height, - inc_width, - inc_height, - min_ratio, - max_ratio, - min_ratio_vulgar, - max_ratio_vulgar, - } - } - - pub fn apply( - &self, - dim: &mut Dim, - ) { - let mut dest_width = dim.w as i32; - let mut dest_height = dim.h as i32; - - if let Some(min_width) = self.min_width { - dest_width = std::cmp::max(dest_width, min_width as i32); - } - - if let Some(min_height) = self.min_height { - dest_height = std::cmp::max(dest_height, min_height as i32); - } - - if let Some(max_width) = self.max_width { - dest_width = std::cmp::min(dest_width, max_width as i32); - } - - if let Some(max_height) = self.max_height { - dest_height = std::cmp::min(dest_height, max_height as i32); - } - - let base_width = if let Some(base_width) = self.base_width { - base_width as i32 - } else { - 0 - }; - - let base_height = if let Some(base_height) = self.base_height { - base_height as i32 - } else { - 0 - }; - - let mut width = if base_width < dest_width { - dest_width - base_width as i32 - } else { - dest_width - }; - - let mut height = if base_height < dest_height { - dest_height - base_height as i32 - } else { - dest_height - }; - - if self.min_ratio.is_some() || self.max_ratio.is_some() { - if height == 0 { - height = 1; - } - - let current_ratio = width as f64 / height as f64; - let mut new_ratio = None; - - if let Some(min_ratio) = self.min_ratio { - if current_ratio < min_ratio { - new_ratio = Some(min_ratio); - } - } - - if new_ratio.is_none() { - if let Some(max_ratio) = self.max_ratio { - if current_ratio > max_ratio { - new_ratio = Some(max_ratio); - } - } - } - - if let Some(new_ratio) = new_ratio { - height = (width as f64 / new_ratio).round() as i32; - width = (height as f64 * new_ratio).round() as i32; - - dest_width = width + base_width as i32; - dest_height = height + base_height as i32; - } - } - - if let Some(inc_height) = self.inc_height { - if dest_height >= base_height { - dest_height -= base_height as i32; - dest_height -= dest_height % inc_height as i32; - dest_height += base_height as i32; - } - } - - if let Some(inc_width) = self.inc_width { - if dest_width >= base_width { - dest_width -= base_width as i32; - dest_width -= dest_width % inc_width as i32; - dest_width += base_width as i32; - } - } - - dim.w = std::cmp::max(dest_width, 0) as u32; - dim.h = std::cmp::max(dest_height, 0) as u32; - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct Padding { - pub left: u32, - pub right: u32, - pub top: u32, - pub bottom: u32, -} - -impl Default for Padding { - fn default() -> Self { - Self { - left: 0, - right: 0, - top: 0, - bottom: 0, - } - } -} - -impl Padding { - pub fn with_each_edge(size: u32) -> Self { - Self { - left: size, - right: size, - top: size, - bottom: size, - } - } -} - -impl Add<Padding> for Region { - type Output = Self; - - fn add( - self, - padding: Padding, - ) -> Self::Output { - Self::Output { - pos: Pos { - x: self.pos.x - padding.left as i32, - y: self.pos.y - padding.top as i32, - }, - dim: Dim { - w: self.dim.w + padding.left + padding.right, - h: self.dim.h + padding.top + padding.bottom, - }, - } - } -} - -impl Sub<Padding> for Region { - type Output = Self; - - fn sub( - self, - padding: Padding, - ) -> Self::Output { - Self::Output { - pos: Pos { - x: self.pos.x + padding.left as i32, - y: self.pos.y + padding.top as i32, - }, - dim: Dim { - w: self.dim.w - padding.left - padding.right, - h: self.dim.h - padding.top - padding.bottom, - }, - } - } -} - -impl AddAssign<Padding> for Region { - fn add_assign( - &mut self, - padding: Padding, - ) { - *self = Self { - pos: Pos { - x: self.pos.x - padding.left as i32, - y: self.pos.y - padding.top as i32, - }, - dim: Dim { - w: self.dim.w + padding.left + padding.right, - h: self.dim.h + padding.top + padding.bottom, - }, - }; - } -} - -impl SubAssign<Padding> for Region { - fn sub_assign( - &mut self, - padding: Padding, - ) { - *self = Self { - pos: Pos { - x: self.pos.x + padding.left as i32, - y: self.pos.y + padding.top as i32, - }, - dim: Dim { - w: self.dim.w - padding.left - padding.right, - h: self.dim.h - padding.top - padding.bottom, - }, - }; - } -} - -impl Add<Padding> for Dim { - type Output = Self; - - fn add( - self, - padding: Padding, - ) -> Self::Output { - Self::Output { - w: self.w + padding.left + padding.right, - h: self.h + padding.top + padding.bottom, - } - } -} - -impl Sub<Padding> for Dim { - type Output = Self; - - fn sub( - self, - padding: Padding, - ) -> Self::Output { - Self::Output { - w: self.w - padding.left - padding.right, - h: self.h - padding.top - padding.bottom, - } - } -} - -impl AddAssign<Padding> for Dim { - fn add_assign( - &mut self, - padding: Padding, - ) { - *self = Self { - w: self.w + padding.left + padding.right, - h: self.h + padding.top + padding.bottom, - }; - } -} - -impl SubAssign<Padding> for Dim { - fn sub_assign( - &mut self, - padding: Padding, - ) { - *self = Self { - w: self.w - padding.left - padding.right, - h: self.h - padding.top - padding.bottom, - }; - } -} - -impl PartialEq for SizeHints { - fn eq( - &self, - other: &Self, - ) -> bool { - self.min_width == other.min_width - && self.min_height == other.min_height - && self.max_width == other.max_width - && self.max_height == other.max_height - && self.base_width == other.base_width - && self.base_height == other.base_height - && self.inc_width == other.inc_width - && self.inc_height == other.inc_height - && self.min_ratio_vulgar == other.min_ratio_vulgar - && self.max_ratio_vulgar == other.max_ratio_vulgar - } -} - -impl Eq for SizeHints {} - -#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)] -pub struct Hints { - pub urgent: bool, - pub input: Option<bool>, - pub initial_state: Option<IcccmWindowState>, - pub group: Option<Window>, -} - -impl Hints { - fn new( - urgent: bool, - input: Option<bool>, - initial_state: Option<IcccmWindowState>, - group: Option<Window>, - ) -> Self { - Self { - urgent, - input, - initial_state, - group, - } - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct Strut { - pub window: Window, - pub width: u32, -} - -impl Strut { - pub fn new( - window: Window, - width: u32, - ) -> Self { - Self { - window, - width, - } - } -} - -impl PartialOrd for Strut { - fn partial_cmp( - &self, - other: &Self, - ) -> Option<std::cmp::Ordering> { - Some(self.cmp(other)) - } -} - -impl Ord for Strut { - fn cmp( - &self, - other: &Self, - ) -> std::cmp::Ordering { - other.width.cmp(&self.width) - } -} diff --git a/src/winsys/connection.rs b/src/winsys/connection.rs @@ -1,22 +1,23 @@ -use crate::common::Dim; -use crate::common::Extents; -use crate::common::Hints; -use crate::common::IcccmWindowState; -use crate::common::Pid; -use crate::common::Pos; -use crate::common::Region; -use crate::common::SizeHints; -use crate::common::Strut; -use crate::common::Window; -use crate::common::WindowState; -use crate::common::WindowType; use crate::event::Event; +use crate::geometry::Dim; +use crate::geometry::Extents; +use crate::geometry::Pos; +use crate::geometry::Region; +use crate::geometry::Strut; +use crate::hints::Hints; +use crate::hints::SizeHints; use crate::input::*; use crate::screen::Screen; +use crate::window::IcccmWindowState; +use crate::window::Window; +use crate::window::WindowState; +use crate::window::WindowType; use crate::Result; use std::collections::HashMap; +pub type Pid = u32; + pub trait Connection { fn flush(&self) -> bool; fn step(&self) -> Option<Event>; @@ -253,21 +254,6 @@ pub trait Connection { window: Window, index: usize, ); - fn set_window_fullscreen( - &self, - window: Window, - on: bool, - ); - fn set_window_above( - &self, - window: Window, - on: bool, - ); - fn set_window_below( - &self, - window: Window, - on: bool, - ); fn set_window_state( &self, window: Window, diff --git a/src/winsys/event.rs b/src/winsys/event.rs @@ -1,15 +1,15 @@ pub use crate::Result; -use crate::common::Dim; -use crate::common::Grip; -use crate::common::Pos; -use crate::common::Region; -use crate::common::Window; -use crate::common::WindowState; -use crate::common::WindowType; +use crate::geometry::Dim; +use crate::geometry::Pos; +use crate::geometry::Region; +use crate::input::Grip; use crate::input::KeyCode; use crate::input::MouseEvent; use crate::screen::Screen; +use crate::window::Window; +use crate::window::WindowState; +use crate::window::WindowType; #[derive(Debug, Clone)] pub enum Event { diff --git a/src/winsys/geometry.rs b/src/winsys/geometry.rs @@ -0,0 +1,801 @@ +use crate::hints::SizeHints; +use crate::window::Window; + +use std::ops::Add; +use std::ops::AddAssign; +use std::ops::Sub; +use std::ops::SubAssign; + +pub type Extents = Padding; + +#[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Hash)] +pub enum Edge { + Left, + Right, + Top, + Bottom, +} + +#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)] +pub enum Corner { + TopLeft, + TopRight, + BottomLeft, + BottomRight, +} + +#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)] +pub struct Pos { + pub x: i32, + pub y: i32, +} + +impl Default for Pos { + fn default() -> Self { + Self { + x: 0, + y: 0, + } + } +} + +impl Pos { + pub fn from_center_of_region(region: Region) -> Self { + Self { + x: region.pos.x + (region.dim.w as f32 / 2f32) as i32, + y: region.pos.y + (region.dim.h as f32 / 2f32) as i32, + } + } + + pub fn from_center_of_dim(dim: Dim) -> Self { + Self { + x: dim.w / 2, + y: dim.h / 2, + } + } + + pub fn values(&self) -> (i32, i32) { + (self.x, self.y) + } + + pub fn dist( + &self, + pos: Self, + ) -> Distance { + Distance { + dx: (pos.x - self.x), + dy: (pos.y - self.y), + } + } + + pub fn relative_to( + &self, + pos: Self, + ) -> Self { + Pos { + x: self.x - pos.x, + y: self.y - pos.y, + } + } +} + +impl Add<Pos> for Pos { + type Output = Self; + + fn add( + self, + other: Pos, + ) -> Self::Output { + Self::Output { + x: self.x + other.x, + y: self.y + other.y, + } + } +} + +#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)] +pub struct Dim { + pub w: i32, + pub h: i32, +} + +impl Default for Dim { + fn default() -> Self { + Self { + w: 0, + h: 0, + } + } +} + +impl Dim { + pub fn values(&self) -> (i32, i32) { + (self.w, self.h) + } + + pub fn center(&self) -> Pos { + Pos { + x: (self.w as f32 / 2f32) as i32, + y: (self.h as f32 / 2f32) as i32, + } + } + + pub fn nearest_corner( + &self, + pos: Pos, + ) -> Corner { + let center = self.center(); + + if pos.x >= center.x { + if pos.y >= center.y { + Corner::BottomRight + } else { + Corner::TopRight + } + } else { + if pos.y >= center.y { + Corner::BottomLeft + } else { + Corner::TopLeft + } + } + } +} + +impl Add<Dim> for Pos { + type Output = Self; + + fn add( + self, + other: Dim, + ) -> Self::Output { + Self::Output { + x: self.x + other.w, + y: self.y + other.h, + } + } +} + +impl Sub<Dim> for Pos { + type Output = Self; + + fn sub( + self, + other: Dim, + ) -> Self::Output { + Self::Output { + x: self.x - other.w, + y: self.y - other.h, + } + } +} + +impl Sub for Pos { + type Output = Dim; + + fn sub( + self, + other: Self, + ) -> Self::Output { + Self::Output { + w: self.x - other.x, + h: self.y - other.y, + } + } +} + +#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)] +pub struct Region { + pub pos: Pos, + pub dim: Dim, +} + +impl Default for Region { + fn default() -> Self { + Self { + pos: Default::default(), + dim: Default::default(), + } + } +} + +impl Region { + pub fn new( + x: i32, + y: i32, + w: i32, + h: i32, + ) -> Self { + Self { + pos: Pos { + x, + y, + }, + dim: Dim { + w, + h, + }, + } + } + + pub fn values(&self) -> (Pos, Dim) { + (self.pos, self.dim) + } + + pub fn with_size_hints( + self, + size_hints: &Option<SizeHints>, + ) -> Self { + let mut geometry = self; + + if let Some(size_hints) = size_hints { + size_hints.apply(&mut geometry.dim); + } + + geometry + } + + pub fn encompasses( + &self, + pos: Pos, + ) -> bool { + pos.x >= self.pos.x + && pos.y >= self.pos.y + && pos.x <= self.pos.x + self.dim.w + && pos.y <= self.pos.y + self.dim.h + } + + pub fn contains( + &self, + region: Region, + ) -> bool { + self.encompasses(region.pos) && self.encompasses(region.bottom_right()) + } + + pub fn occludes( + &self, + region: Region, + ) -> bool { + self.encompasses(region.pos) || region.encompasses(self.pos) + } + + pub fn nearest_corner( + &self, + mut pos: Pos, + ) -> Corner { + pos += self.pos.dist(Pos { + x: 0, + y: 0, + }); + self.dim.nearest_corner(pos) + } + + pub fn quadrant_center_from_pos( + &self, + pos: Pos, + ) -> Option<Pos> { + if self.encompasses(pos) { + return None; + } + + let mut dists = vec![ + (Corner::TopLeft, self.pos.dist(pos).pythagorean()), + (Corner::TopRight, self.top_right().dist(pos).pythagorean()), + ( + Corner::BottomLeft, + self.bottom_left().dist(pos).pythagorean(), + ), + ( + Corner::BottomRight, + self.bottom_right().dist(pos).pythagorean(), + ), + ]; + + dists.sort_by_key(|&(corner, dist)| dist); + + match dists.first().unwrap() { + (Corner::TopLeft, _) => { + let (left, _) = self.split_at_width((self.dim.w as f64 / 2f64).round() as i32); + let (topleft, _) = left.split_at_height((left.dim.h as f64 / 2f64).round() as i32); + + Some(Pos::from_center_of_region(topleft)) + }, + (Corner::TopRight, _) => { + let (_, right) = self.split_at_width((self.dim.w as f64 / 2f64).round() as i32); + let (topright, _) = + right.split_at_height((right.dim.h as f64 / 2f64).round() as i32); + + Some(Pos::from_center_of_region(topright)) + }, + (Corner::BottomLeft, _) => { + let (left, _) = self.split_at_width((self.dim.w as f64 / 2f64).round() as i32); + let (_, bottomleft) = + left.split_at_height((left.dim.h as f64 / 2f64).round() as i32); + + Some(Pos::from_center_of_region(bottomleft)) + }, + (Corner::BottomRight, _) => { + let (_, right) = self.split_at_width((self.dim.w as f64 / 2f64).round() as i32); + let (_, bottomright) = + right.split_at_height((right.dim.h as f64 / 2f64).round() as i32); + + Some(Pos::from_center_of_region(bottomright)) + }, + } + } + + pub fn split_at_width( + &self, + width: i32, + ) -> (Self, Self) { + let width = std::cmp::min(width, self.dim.w); + + ( + Self { + dim: Dim { + w: width, + ..self.dim + }, + ..*self + }, + Self { + pos: Pos { + x: self.pos.x + width, + ..self.pos + }, + dim: Dim { + w: self.dim.w - width, + ..self.dim + }, + }, + ) + } + + pub fn split_at_height( + &self, + height: i32, + ) -> (Self, Self) { + let height = std::cmp::min(height, self.dim.h); + + ( + Self { + dim: Dim { + h: height, + ..self.dim + }, + ..*self + }, + Self { + pos: Pos { + y: self.pos.y + height, + ..self.pos + }, + dim: Dim { + h: self.dim.h - height, + ..self.dim + }, + }, + ) + } + + pub fn with_minimum_dim( + self, + minimum_dim: &Dim, + ) -> Self { + Self { + pos: self.pos, + dim: Dim { + w: std::cmp::max(minimum_dim.w, self.dim.w), + h: std::cmp::max(minimum_dim.h, self.dim.h), + }, + } + } + + pub fn with_maximum_dim( + self, + maximum_dim: &Dim, + ) -> Self { + Self { + pos: self.pos, + dim: Dim { + w: std::cmp::min(maximum_dim.w, self.dim.w), + h: std::cmp::min(maximum_dim.h, self.dim.h), + }, + } + } + + pub fn from_absolute_inner_center( + 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, + }, + dim: *dim, + } + } + + pub fn without_extents( + mut self, + extents: &Extents, + ) -> Self { + self.pos.x += extents.left; + self.pos.y += extents.top; + self.dim.w -= extents.left + extents.right; + self.dim.h -= extents.top + extents.bottom; + self + } + + pub fn with_extents( + mut self, + extents: &Extents, + ) -> Self { + self.pos.x -= extents.left; + self.pos.y -= extents.top; + self.dim.w += extents.left + extents.right; + self.dim.h += extents.top + extents.bottom; + self + } + + pub fn top_right(&self) -> Pos { + Pos { + x: self.pos.x + self.dim.w, + y: self.pos.y, + } + } + + pub fn bottom_left(&self) -> Pos { + Pos { + x: self.pos.x, + y: self.pos.y + self.dim.h, + } + } + + pub fn bottom_right(&self) -> Pos { + Pos { + x: self.pos.x + self.dim.w, + y: self.pos.y + self.dim.h, + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct Padding { + pub left: i32, + pub right: i32, + pub top: i32, + pub bottom: i32, +} + +impl Default for Padding { + fn default() -> Self { + Self { + left: 0, + right: 0, + top: 0, + bottom: 0, + } + } +} + +impl Padding { + pub fn with_each_edge(size: i32) -> Self { + Self { + left: size, + right: size, + top: size, + bottom: size, + } + } +} + +impl Add<Padding> for Region { + type Output = Self; + + fn add( + self, + padding: Padding, + ) -> Self::Output { + Self::Output { + pos: Pos { + x: self.pos.x - padding.left, + y: self.pos.y - padding.top, + }, + dim: Dim { + w: self.dim.w + padding.left + padding.right, + h: self.dim.h + padding.top + padding.bottom, + }, + } + } +} + +impl Sub<Padding> for Region { + type Output = Self; + + fn sub( + self, + padding: Padding, + ) -> Self::Output { + Self::Output { + pos: Pos { + x: self.pos.x + padding.left, + y: self.pos.y + padding.top, + }, + dim: Dim { + w: self.dim.w - padding.left - padding.right, + h: self.dim.h - padding.top - padding.bottom, + }, + } + } +} + +impl AddAssign<Padding> for Region { + fn add_assign( + &mut self, + padding: Padding, + ) { + *self = Self { + pos: Pos { + x: self.pos.x - padding.left, + y: self.pos.y - padding.top, + }, + dim: Dim { + w: self.dim.w + padding.left + padding.right, + h: self.dim.h + padding.top + padding.bottom, + }, + }; + } +} + +impl SubAssign<Padding> for Region { + fn sub_assign( + &mut self, + padding: Padding, + ) { + *self = Self { + pos: Pos { + x: self.pos.x + padding.left, + y: self.pos.y + padding.top, + }, + dim: Dim { + w: self.dim.w - padding.left - padding.right, + h: self.dim.h - padding.top - padding.bottom, + }, + }; + } +} + +impl Add<Padding> for Dim { + type Output = Self; + + fn add( + self, + padding: Padding, + ) -> Self::Output { + Self::Output { + w: self.w + padding.left + padding.right, + h: self.h + padding.top + padding.bottom, + } + } +} + +impl Sub<Padding> for Dim { + type Output = Self; + + fn sub( + self, + padding: Padding, + ) -> Self::Output { + Self::Output { + w: self.w - padding.left - padding.right, + h: self.h - padding.top - padding.bottom, + } + } +} + +impl AddAssign<Padding> for Dim { + fn add_assign( + &mut self, + padding: Padding, + ) { + *self = Self { + w: self.w + padding.left + padding.right, + h: self.h + padding.top + padding.bottom, + }; + } +} + +impl SubAssign<Padding> for Dim { + fn sub_assign( + &mut self, + padding: Padding, + ) { + *self = Self { + w: self.w - padding.left - padding.right, + h: self.h - padding.top - padding.bottom, + }; + } +} + +#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)] +pub struct Distance { + pub dx: i32, + pub dy: i32, +} + +impl Distance { + pub fn values(&self) -> (i32, i32) { + (self.dx, self.dy) + } + + pub fn pythagorean(&self) -> i32 { + let dx = self.dx.pow(2) as f64; + let dy = self.dy.pow(2) as f64; + + (dx + dy).sqrt().round() as i32 + } +} + +impl Add<Distance> for Pos { + type Output = Self; + + fn add( + self, + dist: Distance, + ) -> Self::Output { + Self::Output { + x: self.x + dist.dx, + y: self.y + dist.dy, + } + } +} + +impl AddAssign<Distance> for Pos { + fn add_assign( + &mut self, + dist: Distance, + ) { + *self = Self { + x: self.x + dist.dx, + y: self.y + dist.dy, + }; + } +} + +impl Sub<Distance> for Pos { + type Output = Self; + + fn sub( + self, + dist: Distance, + ) -> Self::Output { + Self::Output { + x: self.x - dist.dx, + y: self.y - dist.dy, + } + } +} + +impl SubAssign<Distance> for Pos { + fn sub_assign( + &mut self, + dist: Distance, + ) { + *self = Self { + x: self.x - dist.dx, + y: self.y - dist.dy, + }; + } +} + +impl Add<Distance> for Dim { + type Output = Self; + + fn add( + self, + dist: Distance, + ) -> Self::Output { + Self::Output { + w: (self.w + dist.dx).abs(), + h: (self.h + dist.dy).abs(), + } + } +} + +impl AddAssign<Distance> for Dim { + fn add_assign( + &mut self, + dist: Distance, + ) { + *self = Self { + w: (self.w + dist.dx).abs(), + h: (self.h + dist.dy).abs(), + }; + } +} + +impl Sub<Distance> for Dim { + type Output = Self; + + fn sub( + self, + dist: Distance, + ) -> Self::Output { + Self::Output { + w: (self.w - dist.dx).abs(), + h: (self.h - dist.dy).abs(), + } + } +} + +impl SubAssign<Distance> for Dim { + fn sub_assign( + &mut self, + dist: Distance, + ) { + *self = Self { + w: (self.w - dist.dx).abs(), + h: (self.h - dist.dy).abs(), + }; + } +} + +#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)] +pub struct Ratio { + pub numerator: i32, + pub denominator: i32, +} + +impl Ratio { + pub fn new( + numerator: i32, + denominator: i32, + ) -> Self { + Self { + numerator, + denominator, + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct Strut { + pub window: Window, + pub width: u32, +} + +impl Strut { + pub fn new( + window: Window, + width: u32, + ) -> Self { + Self { + window, + width, + } + } +} + +impl PartialOrd for Strut { + fn partial_cmp( + &self, + other: &Self, + ) -> Option<std::cmp::Ordering> { + Some(self.cmp(other)) + } +} + +impl Ord for Strut { + fn cmp( + &self, + other: &Self, + ) -> std::cmp::Ordering { + other.width.cmp(&self.width) + } +} diff --git a/src/winsys/hints.rs b/src/winsys/hints.rs @@ -0,0 +1,201 @@ +use crate::geometry::Dim; +use crate::geometry::Pos; +use crate::geometry::Ratio; +use crate::window::IcccmWindowState; +use crate::window::Window; + +#[derive(Debug, Copy, Clone, PartialOrd)] +pub struct SizeHints { + pub by_user: bool, + pub pos: Option<Pos>, + pub min_width: Option<i32>, + pub min_height: Option<i32>, + pub max_width: Option<i32>, + pub max_height: Option<i32>, + pub base_width: Option<i32>, + pub base_height: Option<i32>, + pub inc_width: Option<i32>, + pub inc_height: Option<i32>, + pub min_ratio: Option<f64>, + pub max_ratio: Option<f64>, + pub min_ratio_vulgar: Option<Ratio>, + pub max_ratio_vulgar: Option<Ratio>, +} + +impl SizeHints { + fn new( + by_user: bool, + pos: Option<Pos>, + min_width: Option<i32>, + min_height: Option<i32>, + max_width: Option<i32>, + max_height: Option<i32>, + base_width: Option<i32>, + base_height: Option<i32>, + inc_width: Option<i32>, + inc_height: Option<i32>, + min_ratio: Option<f64>, + max_ratio: Option<f64>, + min_ratio_vulgar: Option<Ratio>, + max_ratio_vulgar: Option<Ratio>, + ) -> Self { + Self { + by_user, + pos, + min_width, + min_height, + max_width, + max_height, + base_width, + base_height, + inc_width, + inc_height, + min_ratio, + max_ratio, + min_ratio_vulgar, + max_ratio_vulgar, + } + } + + pub fn apply( + &self, + dim: &mut Dim, + ) { + let mut dest_width = dim.w; + let mut dest_height = dim.h; + + if let Some(min_width) = self.min_width { + dest_width = std::cmp::max(dest_width, min_width); + } + + if let Some(min_height) = self.min_height { + dest_height = std::cmp::max(dest_height, min_height); + } + + if let Some(max_width) = self.max_width { + dest_width = std::cmp::min(dest_width, max_width); + } + + if let Some(max_height) = self.max_height { + dest_height = std::cmp::min(dest_height, max_height); + } + + let base_width = if let Some(base_width) = self.base_width { + base_width + } else { + 0 + }; + + let base_height = if let Some(base_height) = self.base_height { + base_height + } else { + 0 + }; + + let mut width = if base_width < dest_width { + dest_width - base_width + } else { + dest_width + }; + + let mut height = if base_height < dest_height { + dest_height - base_height + } else { + dest_height + }; + + if self.min_ratio.is_some() || self.max_ratio.is_some() { + if height == 0 { + height = 1; + } + + let current_ratio = width as f64 / height as f64; + let mut new_ratio = None; + + if let Some(min_ratio) = self.min_ratio { + if current_ratio < min_ratio { + new_ratio = Some(min_ratio); + } + } + + if new_ratio.is_none() { + if let Some(max_ratio) = self.max_ratio { + if current_ratio > max_ratio { + new_ratio = Some(max_ratio); + } + } + } + + if let Some(new_ratio) = new_ratio { + height = (width as f64 / new_ratio).round() as i32; + width = (height as f64 * new_ratio).round() as i32; + + dest_width = width + base_width; + dest_height = height + base_height; + } + } + + if let Some(inc_height) = self.inc_height { + if dest_height >= base_height { + dest_height -= base_height; + dest_height -= dest_height % inc_height; + dest_height += base_height; + } + } + + if let Some(inc_width) = self.inc_width { + if dest_width >= base_width { + dest_width -= base_width; + dest_width -= dest_width % inc_width; + dest_width += base_width; + } + } + + dim.w = std::cmp::max(dest_width, 0i32); + dim.h = std::cmp::max(dest_height, 0i32); + } +} + +impl PartialEq for SizeHints { + fn eq( + &self, + other: &Self, + ) -> bool { + self.min_width == other.min_width + && self.min_height == other.min_height + && self.max_width == other.max_width + && self.max_height == other.max_height + && self.base_width == other.base_width + && self.base_height == other.base_height + && self.inc_width == other.inc_width + && self.inc_height == other.inc_height + && self.min_ratio_vulgar == other.min_ratio_vulgar + && self.max_ratio_vulgar == other.max_ratio_vulgar + } +} + +impl Eq for SizeHints {} + +#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)] +pub struct Hints { + pub urgent: bool, + pub input: Option<bool>, + pub initial_state: Option<IcccmWindowState>, + pub group: Option<Window>, +} + +impl Hints { + fn new( + urgent: bool, + input: Option<bool>, + initial_state: Option<IcccmWindowState>, + group: Option<Window>, + ) -> Self { + Self { + urgent, + input, + initial_state, + group, + } + } +} diff --git a/src/winsys/input.rs b/src/winsys/input.rs @@ -1,7 +1,9 @@ pub use crate::Result; -use crate::common::Pos; -use crate::common::Window; +use crate::geometry::Corner; +use crate::geometry::Edge; +use crate::geometry::Pos; +use crate::window::Window; use std::collections::HashMap; use std::convert::TryFrom; @@ -10,6 +12,26 @@ use std::vec::Vec; use strum::EnumIter; use strum::IntoEnumIterator; +#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)] +pub enum Grip { + Edge(Edge), + Corner(Corner), +} + +impl Grip { + pub fn is_top_grip(&self) -> bool { + *self == Grip::Edge(Edge::Top) + || *self == Grip::Corner(Corner::TopLeft) + || *self == Grip::Corner(Corner::TopRight) + } + + pub fn is_left_grip(&self) -> bool { + *self == Grip::Edge(Edge::Left) + || *self == Grip::Corner(Corner::TopLeft) + || *self == Grip::Corner(Corner::BottomLeft) + } +} + pub type CodeMap = HashMap<String, u8>; #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] diff --git a/src/winsys/macros.rs b/src/winsys/macros.rs @@ -0,0 +1,12 @@ +#[macro_export] +macro_rules! map( + { $($key:expr => $val:expr,)+ } => { + { + let mut map = ::std::collections::HashMap::new(); + $( + map.insert($key, $val); + )+ + map + } + }; +); diff --git a/src/winsys/mod.rs b/src/winsys/mod.rs @@ -2,11 +2,16 @@ #![allow(unused_imports)] #![allow(unused_variables)] +#[macro_use] +mod macros; + pub type Result<T> = anyhow::Result<T>; -pub mod common; pub mod connection; pub mod event; +pub mod geometry; +pub mod hints; pub mod input; pub mod screen; +pub mod window; pub mod xdata; diff --git a/src/winsys/screen.rs b/src/winsys/screen.rs @@ -1,9 +1,9 @@ -use crate::common::Dim; -use crate::common::Edge; -use crate::common::Pos; -use crate::common::Region; -use crate::common::Strut; -use crate::common::Window; +use crate::geometry::Dim; +use crate::geometry::Edge; +use crate::geometry::Pos; +use crate::geometry::Region; +use crate::geometry::Strut; +use crate::window::Window; use std::{ collections::HashMap, @@ -104,20 +104,20 @@ impl Screen { if self.showing_struts { if let Some(strut) = self.struts.get(&Edge::Left).unwrap().last() { region.pos.x += strut.width as i32; - region.dim.w -= strut.width; + region.dim.w -= strut.width as i32; } if let Some(strut) = self.struts.get(&Edge::Right).unwrap().last() { - region.dim.w -= strut.width; + region.dim.w -= strut.width as i32; } if let Some(strut) = self.struts.get(&Edge::Top).unwrap().last() { region.pos.y += strut.width as i32; - region.dim.h -= strut.width; + region.dim.h -= strut.width as i32; } if let Some(strut) = self.struts.get(&Edge::Bottom).unwrap().last() { - region.dim.h -= strut.width; + region.dim.h -= strut.width as i32; } } diff --git a/src/winsys/window.rs b/src/winsys/window.rs @@ -0,0 +1,42 @@ +pub type Window = u32; + +#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)] +pub enum IcccmWindowState { + Withdrawn, + Normal, + Iconic, +} + +#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)] +pub enum WindowState { + Modal, + Sticky, + MaximizedVert, + MaximizedHorz, + Shaded, + SkipTaskbar, + SkipPager, + Hidden, + Fullscreen, + Above, + Below, + DemandsAttention, +} + +#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)] +pub enum WindowType { + Desktop, + Dock, + Toolbar, + Menu, + Utility, + Splash, + Dialog, + DropdownMenu, + PopupMenu, + Tooltip, + Notification, + Combo, + Dnd, + Normal, +} diff --git a/src/winsys/xdata/event.rs b/src/winsys/xdata/event.rs @@ -1,12 +1,12 @@ use super::super::event::Result; pub use super::super::event::*; -use crate::common::Pos; -use crate::common::Region; -use crate::common::Window; -use crate::common::WindowState; -use crate::common::WindowType; +use crate::geometry::Pos; +use crate::geometry::Region; use crate::input::KeyCode; use crate::screen::Screen; +use crate::window::Window; +use crate::window::WindowState; +use crate::window::WindowType; use x11rb::protocol::xproto; diff --git a/src/winsys/xdata/input.rs b/src/winsys/xdata/input.rs @@ -1,18 +1,17 @@ use super::super::input::Result; pub use super::super::input::*; -use crate::common::Pos; -use crate::common::Window; +use crate::geometry::Pos; +use crate::window::Window; use std::collections::HashMap; use std::convert::TryFrom; use std::vec::Vec; +use anyhow::anyhow; use strum::EnumIter; use strum::IntoEnumIterator; -use anyhow::anyhow; - use x11rb::protocol::xproto::ButtonPressEvent; use x11rb::protocol::xproto::ButtonReleaseEvent; use x11rb::protocol::xproto::KeyPressEvent; diff --git a/src/winsys/xdata/xconnection.rs b/src/winsys/xdata/xconnection.rs @@ -1,40 +1,36 @@ -use crate::common::Atom; -use crate::common::Corner; -use crate::common::Dim; -use crate::common::Edge; -use crate::common::Extents; -use crate::common::Grip; -use crate::common::Hints; -use crate::common::IcccmWindowState; -use crate::common::Pid; -use crate::common::Pos; -use crate::common::Ratio; -use crate::common::Region; -use crate::common::SizeHints; -use crate::common::Strut; -use crate::common::Window; -use crate::common::WindowState; -use crate::common::WindowType; use crate::connection::Connection; +use crate::connection::Pid; use crate::event::Event; use crate::event::PropertyKind; use crate::event::StackMode; use crate::event::ToggleAction; +use crate::geometry::Corner; +use crate::geometry::Dim; +use crate::geometry::Edge; +use crate::geometry::Extents; +use crate::geometry::Pos; +use crate::geometry::Ratio; +use crate::geometry::Region; +use crate::geometry::Strut; +use crate::hints::Hints; +use crate::hints::SizeHints; use crate::input::Button; +use crate::input::Grip; use crate::input::KeyCode; use crate::input::MouseEvent; use crate::input::MouseEventKey; use crate::input::MouseShortcut; use crate::screen::Screen; +use crate::window::IcccmWindowState; +use crate::window::Window; +use crate::window::WindowState; +use crate::window::WindowType; use crate::Result; use std::collections::HashMap; use std::convert::TryFrom; use std::str::FromStr; -use anyhow::anyhow; -use strum::*; - use x11rb::connection; use x11rb::cursor::Handle as CursorHandle; use x11rb::errors::ReplyError; @@ -51,6 +47,11 @@ use x11rb::protocol::Event as XEvent; use x11rb::resource_manager::Database; use x11rb::wrapper::ConnectionExt as _; +use anyhow::anyhow; +use strum::*; + +type Atom = u32; + x11rb::atom_manager! { pub Atoms: AtomsCookie { Any, @@ -165,8 +166,8 @@ x11rb::atom_manager! { } } -pub struct XConnection<'a, C: connection::Connection> { - conn: &'a C, +pub struct XConnection<'conn, Conn: connection::Connection> { + conn: &'conn Conn, atoms: Atoms, type_map: HashMap<Atom, WindowType>, state_map: HashMap<Atom, WindowState>, @@ -183,9 +184,9 @@ pub struct XConnection<'a, C: connection::Connection> { regrab_event_mask: EventMask, } -impl<'a, C: connection::Connection> XConnection<'a, C> { +impl<'conn, Conn: connection::Connection> XConnection<'conn, Conn> { pub fn new( - conn: &'a C, + conn: &'conn Conn, screen_num: usize, ) -> Result<Self> { let screen = conn.setup().roots[screen_num].clone(); @@ -195,7 +196,6 @@ impl<'a, C: connection::Connection> XConnection<'a, C> { .event_mask(EventMask::SUBSTRUCTURE_REDIRECT | EventMask::SUBSTRUCTURE_NOTIFY); let res = conn.change_window_attributes(screen.root, &aux)?.check(); - if let Err(ReplyError::X11Error(err)) = res { if err.error_kind == ErrorKind::Access { return Err(anyhow!("another window manager is already running")); @@ -207,56 +207,37 @@ impl<'a, C: connection::Connection> XConnection<'a, C> { let atoms = Atoms::new(conn)?.reply()?; let check_window = conn.generate_id()?; - let type_map: HashMap<Atom, WindowType> = { - let mut types = HashMap::with_capacity(10); - types.insert(atoms._NET_WM_WINDOW_TYPE_DESKTOP, WindowType::Desktop); - types.insert(atoms._NET_WM_WINDOW_TYPE_DOCK, WindowType::Dock); - types.insert(atoms._NET_WM_WINDOW_TYPE_TOOLBAR, WindowType::Toolbar); - types.insert(atoms._NET_WM_WINDOW_TYPE_MENU, WindowType::Menu); - types.insert(atoms._NET_WM_WINDOW_TYPE_UTILITY, WindowType::Utility); - types.insert(atoms._NET_WM_WINDOW_TYPE_SPLASH, WindowType::Splash); - types.insert(atoms._NET_WM_WINDOW_TYPE_DIALOG, WindowType::Dialog); - types.insert( - atoms._NET_WM_WINDOW_TYPE_DROPDOWN_MENU, - WindowType::DropdownMenu, - ); - types.insert(atoms._NET_WM_WINDOW_TYPE_POPUP_MENU, WindowType::PopupMenu); - types.insert(atoms._NET_WM_WINDOW_TYPE_TOOLTIP, WindowType::Tooltip); - types.insert( - atoms._NET_WM_WINDOW_TYPE_NOTIFICATION, - WindowType::Notification, - ); - types.insert(atoms._NET_WM_WINDOW_TYPE_COMBO, WindowType::Combo); - types.insert(atoms._NET_WM_WINDOW_TYPE_DND, WindowType::Dnd); - types.insert(atoms._NET_WM_WINDOW_TYPE_NORMAL, WindowType::Normal); - types - }; + let type_map: HashMap<Atom, WindowType> = map!( + atoms._NET_WM_WINDOW_TYPE_DESKTOP => WindowType::Desktop, + atoms._NET_WM_WINDOW_TYPE_DOCK => WindowType::Dock, + atoms._NET_WM_WINDOW_TYPE_TOOLBAR => WindowType::Toolbar, + atoms._NET_WM_WINDOW_TYPE_MENU => WindowType::Menu, + atoms._NET_WM_WINDOW_TYPE_UTILITY => WindowType::Utility, + atoms._NET_WM_WINDOW_TYPE_SPLASH => WindowType::Splash, + atoms._NET_WM_WINDOW_TYPE_DIALOG => WindowType::Dialog, + atoms._NET_WM_WINDOW_TYPE_DROPDOWN_MENU => WindowType::DropdownMenu, + atoms._NET_WM_WINDOW_TYPE_POPUP_MENU => WindowType::PopupMenu, + atoms._NET_WM_WINDOW_TYPE_TOOLTIP => WindowType::Tooltip, + atoms._NET_WM_WINDOW_TYPE_NOTIFICATION => WindowType::Notification, + atoms._NET_WM_WINDOW_TYPE_COMBO => WindowType::Combo, + atoms._NET_WM_WINDOW_TYPE_DND => WindowType::Dnd, + atoms._NET_WM_WINDOW_TYPE_NORMAL => WindowType::Normal, + ); - let state_map: HashMap<Atom, WindowState> = { - let mut states = HashMap::with_capacity(10); - states.insert(atoms._NET_WM_STATE_MODAL, WindowState::Modal); - states.insert(atoms._NET_WM_STATE_STICKY, WindowState::Sticky); - states.insert( - atoms._NET_WM_STATE_MAXIMIZED_VERT, - WindowState::MaximizedVert, - ); - states.insert( - atoms._NET_WM_STATE_MAXIMIZED_HORZ, - WindowState::MaximizedHorz, - ); - states.insert(atoms._NET_WM_STATE_SHADED, WindowState::Shaded); - states.insert(atoms._NET_WM_STATE_SKIP_TASKBAR, WindowState::SkipTaskbar); - states.insert(atoms._NET_WM_STATE_SKIP_PAGER, WindowState::SkipPager); - states.insert(atoms._NET_WM_STATE_HIDDEN, WindowState::Hidden); - states.insert(atoms._NET_WM_STATE_FULLSCREEN, WindowState::Fullscreen); - states.insert(atoms._NET_WM_STATE_ABOVE, WindowState::Above); - states.insert(atoms._NET_WM_STATE_BELOW, WindowState::Below); - states.insert( - atoms._NET_WM_STATE_DEMANDS_ATTENTION, - WindowState::DemandsAttention, - ); - states - }; + let state_map: HashMap<Atom, WindowState> = map!( + atoms._NET_WM_STATE_MODAL => WindowState::Modal, + atoms._NET_WM_STATE_STICKY => WindowState::Sticky, + atoms._NET_WM_STATE_MAXIMIZED_VERT => WindowState::MaximizedVert, + atoms._NET_WM_STATE_MAXIMIZED_HORZ => WindowState::MaximizedHorz, + atoms._NET_WM_STATE_SHADED => WindowState::Shaded, + atoms._NET_WM_STATE_SKIP_TASKBAR => WindowState::SkipTaskbar, + atoms._NET_WM_STATE_SKIP_PAGER => WindowState::SkipPager, + atoms._NET_WM_STATE_HIDDEN => WindowState::Hidden, + atoms._NET_WM_STATE_FULLSCREEN => WindowState::Fullscreen, + atoms._NET_WM_STATE_ABOVE => WindowState::Above, + atoms._NET_WM_STATE_BELOW => WindowState::Below, + atoms._NET_WM_STATE_DEMANDS_ATTENTION => WindowState::DemandsAttention, + ); conn.create_window( x11rb::COPY_DEPTH_FROM_PARENT, @@ -272,11 +253,11 @@ impl<'a, C: connection::Connection> XConnection<'a, C> { &xproto::CreateWindowAux::default().override_redirect(1), )?; - drop(conn.map_window(check_window)); - - let aux = xproto::ConfigureWindowAux::default().stack_mode(xproto::StackMode::BELOW); - - drop(conn.configure_window(check_window, &aux)); + conn.map_window(check_window)?; + conn.configure_window( + check_window, + &xproto::ConfigureWindowAux::default().stack_mode(xproto::StackMode::BELOW), + )?; randr::select_input( conn, @@ -290,7 +271,6 @@ impl<'a, C: connection::Connection> XConnection<'a, C> { conn.create_gc(background_gc, screen.root, &xproto::CreateGCAux::default())?; let database = Database::new_from_default(conn).ok(); - if let Some(ref database) = database { drop( CursorHandle::new(conn, screen_num, &database).map(|cookie| { @@ -373,7 +353,7 @@ impl<'a, C: connection::Connection> XConnection<'a, C> { }) } - pub fn window_is_any_of_state( + pub fn window_is_any_of_states( &self, window: Window, states: &[Atom], @@ -457,7 +437,7 @@ impl<'a, C: connection::Connection> XConnection<'a, C> { &self, atom: Atom, ) -> Option<WindowState> { - self.state_map.get(&atom).map(|&state| state) + self.state_map.get(&atom).copied() } #[inline] @@ -486,7 +466,7 @@ impl<'a, C: connection::Connection> XConnection<'a, C> { &self, atom: Atom, ) -> Option<WindowType> { - self.type_map.get(&atom).map(|&type_| type_) + self.type_map.get(&atom).copied() } #[inline] @@ -519,7 +499,7 @@ impl<'a, C: connection::Connection> XConnection<'a, C> { on: bool, ) { if on { - if self.window_is_any_of_state(window, &[state_atom]) { + if self.window_is_any_of_states(window, &[state_atom]) { return; } @@ -541,13 +521,13 @@ impl<'a, C: connection::Connection> XConnection<'a, C> { 0, std::u32::MAX, ) - .map_or(Vec::new(), |cookie| { - cookie.reply().map_or(Vec::new(), |reply| { - reply.value32().map_or(Vec::new(), |window_states| { - let mut states = Vec::with_capacity(reply.value_len as usize); - window_states.for_each(|state| states.push(state)); - states - }) + .map_or(Vec::with_capacity(0), |cookie| { + cookie.reply().map_or(Vec::with_capacity(0), |reply| { + reply + .value32() + .map_or(Vec::with_capacity(0), |window_states| { + window_states.collect() + }) }) }); @@ -688,13 +668,13 @@ impl<'a, C: connection::Connection> XConnection<'a, C> { ) -> Option<Event> { self.conn .get_window_attributes(event.window) + .ok() .map(|cookie| Event::Unmap { window: event.window, ignore: cookie .reply() .map_or(false, |reply| reply.override_redirect), }) - .ok() } #[inline] @@ -718,11 +698,11 @@ impl<'a, C: connection::Connection> XConnection<'a, C> { } if event.value_mask & u16::from(xproto::ConfigWindow::WIDTH) != 0 { - w = Some(event.width as u32); + w = Some(event.width as i32); } if event.value_mask & u16::from(xproto::ConfigWindow::HEIGHT) != 0 { - h = Some(event.height as u32); + h = Some(event.height as i32); } let pos = match (x, y) { @@ -805,8 +785,8 @@ impl<'a, C: connection::Connection> XConnection<'a, C> { region: Region::new( event.x as i32, event.y as i32, - event.width as u32, - event.height as u32, + event.width as i32, + event.height as i32, ), on_root: event.window == self.screen.root, }) @@ -864,7 +844,7 @@ impl<'a, C: connection::Connection> XConnection<'a, C> { 8 => event.data.as_data8().iter().map(|&i| i as usize).collect(), 16 => event.data.as_data16().iter().map(|&i| i as usize).collect(), 32 => event.data.as_data32().iter().map(|&i| i as usize).collect(), - _ => Vec::new(), + _ => Vec::with_capacity(0), }; if event.type_ == self.atoms._NET_WM_STATE { @@ -897,19 +877,10 @@ impl<'a, C: connection::Connection> XConnection<'a, C> { } } else if event.type_ == self.atoms._NET_MOVERESIZE_WINDOW { // TODO: handle gravity - let x = data.get(1); - let y = data.get(2); - let width = data.get(3); - let height = data.get(4); - - if x.is_none() || y.is_none() || width.is_none() || height.is_none() { - return None; - } - - let x = *x.unwrap(); - let y = *y.unwrap(); - let width = *width.unwrap(); - let height = *height.unwrap(); + let x = data.get(1)?.clone(); + let y = data.get(2)?.clone(); + let width = data.get(3)?.clone(); + let height = data.get(4)?.clone(); return Some(Event::PlacementRequest { window: event.window, @@ -918,23 +889,15 @@ impl<'a, C: connection::Connection> XConnection<'a, C> { y: y as i32, }), dim: Some(Dim { - w: width as u32, - h: height as u32, + w: width as i32, + h: height as i32, }), on_root: event.window == self.screen.root, }); } else if event.type_ == self.atoms._NET_WM_MOVERESIZE { - let x_root = data.get(0); - let y_root = data.get(1); - let direction = data.get(2); - - if x_root.is_none() || y_root.is_none() || direction.is_none() { - return None; - } - - let x_root = *x_root.unwrap(); - let y_root = *y_root.unwrap(); - let direction = *direction.unwrap(); + let x_root = data.get(0)?.clone(); + let y_root = data.get(1)?.clone(); + let direction = data.get(2)?.clone(); return Some(Event::GripRequest { window: event.window, @@ -962,13 +925,11 @@ impl<'a, C: connection::Connection> XConnection<'a, C> { on_root: event.window == self.screen.root, }); } else if event.type_ == self.atoms._NET_CURRENT_DESKTOP { - if let Some(&index) = data.get(0) { - return Some(Event::WorkspaceRequest { - window: None, - index, - on_root: event.window == self.screen.root, - }); - } + return Some(Event::WorkspaceRequest { + window: None, + index: data.get(0)?.clone(), + on_root: event.window == self.screen.root, + }); } else if event.type_ == self.atoms._NET_CLOSE_WINDOW { return Some(Event::CloseRequest { window: event.window, @@ -1007,7 +968,7 @@ impl<'a, C: connection::Connection> XConnection<'a, C> { } } -impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { +impl<'conn, Conn: connection::Connection> Connection for XConnection<'conn, Conn> { #[inline] fn flush(&self) -> bool { self.conn.flush().is_ok() @@ -1046,28 +1007,30 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { if let Ok(reply) = resources.reply() { return reply .crtcs - .iter() + .into_iter() .flat_map(|crtc| { - randr::get_crtc_info(self.conn, *crtc, 0) - .map(|cookie| cookie.reply().map(|reply| reply)) + randr::get_crtc_info(self.conn, crtc, 0).map(|cookie| cookie.reply()) }) .enumerate() - .map(|(i, r)| { - let r = r.unwrap(); + .map(|(i, reply)| { + let reply = reply.unwrap(); let region = Region { pos: Pos { - x: r.x as i32, - y: r.y as i32, + x: reply.x as i32, + y: reply.y as i32, }, dim: Dim { - w: r.width as u32, - h: r.height as u32, + w: reply.width as i32, + h: reply.height as i32, }, }; Screen::new(region, i) }) - .filter(|screen| screen.full_region().dim.w > 0) + .filter(|screen| { + let region = screen.full_region(); + region.dim.w > 0 && region.dim.h > 0 + }) .collect(); } } @@ -1078,13 +1041,12 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { fn top_level_windows(&self) -> Vec<Window> { self.conn .query_tree(self.screen.root) - .map_or(Vec::new(), |cookie| { - cookie.reply().map_or(Vec::new(), |reply| { + .map_or(Vec::with_capacity(0), |cookie| { + cookie.reply().map_or(Vec::with_capacity(0), |reply| { reply .children - .iter() - .filter(|&w| self.must_manage_window(*w)) - .cloned() + .into_iter() + .filter(|&w| self.must_manage_window(w)) .collect() }) }) @@ -1108,26 +1070,23 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { window: Option<Window>, screen: &Screen, ) { - let (pos, window) = match window { - Some(window) => { - let geometry = self.get_window_geometry(window); - - if geometry.is_err() { - return; - } - - (Pos::from_center_of_dim(geometry.unwrap().dim), window) - }, - None => ( - Pos::from_center_of_dim(screen.placeable_region().dim), - self.screen.root, - ), - }; - - drop( - self.conn - .warp_pointer(x11rb::NONE, window, 0, 0, 0, 0, pos.x as i16, pos.y as i16), + let pos = Pos::from_center_of_dim( + window + .and_then(|window| self.get_window_geometry(window).ok()) + .unwrap_or(screen.placeable_region()) + .dim, ); + + drop(self.conn.warp_pointer( + x11rb::NONE, + window.unwrap_or(self.screen.root), + 0, + 0, + 0, + 0, + pos.x as i16, + pos.y as i16, + )); } #[inline] @@ -1164,16 +1123,20 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { window: Window, ) { if self.confined_to.is_none() { - if let Ok(_) = self.conn.grab_pointer( - false, - self.screen.root, - u32::from(EventMask::POINTER_MOTION | EventMask::BUTTON_RELEASE) as u16, - xproto::GrabMode::ASYNC, - xproto::GrabMode::ASYNC, - self.screen.root, - x11rb::NONE, - x11rb::CURRENT_TIME, - ) { + if self + .conn + .grab_pointer( + false, + self.screen.root, + u32::from(EventMask::POINTER_MOTION | EventMask::BUTTON_RELEASE) as u16, + xproto::GrabMode::ASYNC, + xproto::GrabMode::ASYNC, + self.screen.root, + x11rb::NONE, + x11rb::CURRENT_TIME, + ) + .is_ok() + { drop(self.conn.grab_keyboard( false, self.screen.root, @@ -1264,23 +1227,21 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { .backing_store(Some(xproto::BackingStore::ALWAYS)) .event_mask(EventMask::EXPOSURE | EventMask::KEY_PRESS); - drop( - self.conn - .create_window( - x11rb::COPY_DEPTH_FROM_PARENT, - frame, - self.screen.root, - region.pos.x as i16, - region.pos.y as i16, - region.dim.w as u16, - region.dim.h as u16, - 0, - xproto::WindowClass::INPUT_OUTPUT, - 0, - &aux, - ) - .expect(ERR), - ); + self.conn + .create_window( + x11rb::COPY_DEPTH_FROM_PARENT, + frame, + self.screen.root, + region.pos.x as i16, + region.pos.y as i16, + region.dim.w as u16, + region.dim.h as u16, + 0, + xproto::WindowClass::INPUT_OUTPUT, + 0, + &aux, + ) + .expect(ERR); self.flush(); @@ -1294,23 +1255,21 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { let handle = self.conn.generate_id().expect(ERR); let aux = xproto::CreateWindowAux::new().override_redirect(1); - drop( - self.conn - .create_window( - x11rb::COPY_DEPTH_FROM_PARENT, - handle, - self.screen.root, - -2, - -2, - 1, - 1, - 0, - xproto::WindowClass::INPUT_ONLY, - 0, - &aux, - ) - .expect(ERR), - ); + self.conn + .create_window( + x11rb::COPY_DEPTH_FROM_PARENT, + handle, + self.screen.root, + -2, + -2, + 1, + 1, + 0, + xproto::WindowClass::INPUT_ONLY, + 0, + &aux, + ) + .expect(ERR); self.flush(); @@ -1323,9 +1282,10 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { window: Window, focus_follows_mouse: bool, ) { - let aux = xproto::ChangeWindowAttributesAux::default().event_mask(self.window_event_mask); - - drop(self.conn.change_window_attributes(window, &aux)); + drop(self.conn.change_window_attributes( + window, + &xproto::ChangeWindowAttributesAux::default().event_mask(self.window_event_mask), + )); } #[inline] @@ -1334,16 +1294,17 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { window: Window, focus_follows_mouse: bool, ) { - let aux = xproto::ChangeWindowAttributesAux::default().event_mask( - self.frame_event_mask - | if focus_follows_mouse { - EventMask::ENTER_WINDOW - } else { - EventMask::NO_EVENT - }, - ); - - drop(self.conn.change_window_attributes(window, &aux)); + drop(self.conn.change_window_attributes( + window, + &xproto::ChangeWindowAttributesAux::default().event_mask( + self.frame_event_mask + | if focus_follows_mouse { + EventMask::ENTER_WINDOW + } else { + EventMask::NO_EVENT + }, + ), + )); } #[inline] @@ -1351,10 +1312,10 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { &self, window: Window, ) { - let aux = - xproto::ChangeWindowAttributesAux::default().event_mask(EventMask::STRUCTURE_NOTIFY); - - drop(self.conn.change_window_attributes(window, &aux)); + drop(self.conn.change_window_attributes( + window, + &xproto::ChangeWindowAttributesAux::default().event_mask(EventMask::STRUCTURE_NOTIFY), + )); } #[inline] @@ -1423,10 +1384,8 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { &self, window: Window, ) -> bool { - match self.send_protocol_client_message(window, self.atoms.WM_DELETE_WINDOW) { - Ok(_) => self.flush(), - Err(_) => false, - } + self.send_protocol_client_message(window, self.atoms.WM_DELETE_WINDOW) + .map_or(false, |_| self.flush()) } #[inline] @@ -1439,11 +1398,9 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { if self.window_has_any_of_protocols(window, protocols) { self.close_window(window) } else { - if self.conn.kill_client(window).is_ok() { - self.flush() - } else { - false - } + self.conn + .kill_client(window) + .map_or(false, |_| self.flush()) } } @@ -1453,13 +1410,16 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { window: Window, region: &Region, ) { - let aux = xproto::ConfigureWindowAux::default() - .x(region.pos.x as i32) - .y(region.pos.y as i32) - .width(region.dim.w as u32) - .height(region.dim.h as u32); - - drop(self.conn.configure_window(window, &aux)); + drop( + self.conn.configure_window( + window, + &xproto::ConfigureWindowAux::default() + .x(region.pos.x as i32) + .y(region.pos.y as i32) + .width(region.dim.w as u32) + .height(region.dim.h as u32), + ), + ); } #[inline] @@ -1468,11 +1428,14 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { window: Window, pos: Pos, ) { - let aux = xproto::ConfigureWindowAux::default() - .x(pos.x as i32) - .y(pos.y as i32); - - drop(self.conn.configure_window(window, &aux)); + drop( + self.conn.configure_window( + window, + &xproto::ConfigureWindowAux::default() + .x(pos.x as i32) + .y(pos.y as i32), + ), + ); } #[inline] @@ -1481,11 +1444,14 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { window: Window, dim: Dim, ) { - let aux = xproto::ConfigureWindowAux::default() - .width(dim.w as u32) - .height(dim.h as u32); - - drop(self.conn.configure_window(window, &aux)); + drop( + self.conn.configure_window( + window, + &xproto::ConfigureWindowAux::default() + .width(dim.w as u32) + .height(dim.h as u32), + ), + ); } #[inline] @@ -1550,12 +1516,12 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { key_codes: &[KeyCode], mouse_bindings: &[&(MouseEventKey, MouseShortcut)], ) { - for m in &[0, u16::from(ModMask::M2)] { + for &m in &[0, u16::from(ModMask::M2)] { for k in key_codes { drop(self.conn.grab_key( false, self.screen.root, - if *m != 0 { k.mask | *m } else { k.mask }, + if m != 0 { k.mask | m } else { k.mask }, k.code, xproto::GrabMode::ASYNC, xproto::GrabMode::ASYNC, @@ -1572,14 +1538,15 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { x11rb::NONE, x11rb::NONE, xproto::ButtonIndex::try_from(state.button()).unwrap(), - state.mask() | *m, + state.mask() | m, )); } } - let aux = xproto::ChangeWindowAttributesAux::default().event_mask(self.root_event_mask); - - drop(self.conn.change_window_attributes(self.screen.root, &aux)); + drop(self.conn.change_window_attributes( + self.screen.root, + &xproto::ChangeWindowAttributesAux::default().event_mask(self.root_event_mask), + )); self.flush(); } @@ -1633,9 +1600,10 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { window: Window, width: u32, ) { - let aux = xproto::ConfigureWindowAux::default().border_width(width); - - drop(self.conn.configure_window(window, &aux)); + drop(self.conn.configure_window( + window, + &xproto::ConfigureWindowAux::default().border_width(width), + )); } #[inline] @@ -1644,9 +1612,10 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { window: Window, color: u32, ) { - let aux = xproto::ChangeWindowAttributesAux::default().border_pixel(color); - - drop(self.conn.change_window_attributes(window, &aux)); + drop(self.conn.change_window_attributes( + window, + &xproto::ChangeWindowAttributesAux::default().border_pixel(color), + )); } #[inline] @@ -1717,14 +1686,14 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { &self, window: Window, ) -> Result<Region> { - let geometry = self.conn.get_geometry(window)?.reply()?; - - Ok(Region::new( - geometry.x as i32, - geometry.y as i32, - geometry.width as u32, - geometry.height as u32, - )) + Ok(self.conn.get_geometry(window)?.reply().map(|reply| { + Region::new( + reply.x as i32, + reply.y as i32, + reply.width as i32, + reply.height as i32, + ) + })?) } #[inline] @@ -1787,45 +1756,28 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { &self, window: Window, ) -> bool { - let has_float_type = self.window_is_any_of_types(window, &[ - self.atoms._NET_WM_WINDOW_TYPE_DIALOG, - self.atoms._NET_WM_WINDOW_TYPE_UTILITY, - self.atoms._NET_WM_WINDOW_TYPE_TOOLBAR, - self.atoms._NET_WM_WINDOW_TYPE_SPLASH, - ]); - - if has_float_type { - return true; - } - - let has_float_state = - self.window_is_any_of_state(window, &[self.atoms._NET_WM_STATE_MODAL]); - - if has_float_state { + if self.get_window_desktop(window) == Some(0xFFFFFFFF) + || self.window_is_any_of_states(window, &[self.atoms._NET_WM_STATE_MODAL]) + || self.window_is_any_of_types(window, &[ + self.atoms._NET_WM_WINDOW_TYPE_DIALOG, + self.atoms._NET_WM_WINDOW_TYPE_UTILITY, + self.atoms._NET_WM_WINDOW_TYPE_TOOLBAR, + self.atoms._NET_WM_WINDOW_TYPE_SPLASH, + ]) + { return true; } - if let Some(desktop) = self.get_window_desktop(window) { - if desktop == 0xFFFFFFFF { - return true; - } - } - self.get_window_geometry(window).map_or(false, |geometry| { - let (_, size_hints) = self.get_icccm_window_size_hints(window, None, &None); - size_hints.map_or(false, |size_hints| { - size_hints.min_width.map_or(false, |min_width| { - size_hints.min_height.map_or(false, |min_height| { - size_hints.max_width.map_or(false, |max_width| { - size_hints.max_height.map_or(false, |max_height| { - max_width > 0 - && max_height > 0 - && max_width == min_width - && max_height == min_height - }) - }) - }) - }) + let (_, sh) = self.get_icccm_window_size_hints(window, None, &None); + + sh.map_or(false, |sh| { + match (sh.min_width, sh.min_height, sh.max_width, sh.max_height) { + (Some(miw), Some(mih), Some(maw), Some(mah)) => { + maw > 0 && mah > 0 && maw == miw && mah == mih + }, + _ => false, + } }) }) } @@ -1843,13 +1795,13 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { default_state, |cookie| { cookie.reply().map_or(default_state, |reply| { - reply.initial_state.map_or(default_state, |i| i) + reply.initial_state.unwrap_or(default_state) }) }, ); reply.class != xproto::WindowClass::INPUT_ONLY - && !self.window_is_any_of_state(window, &[self.atoms._NET_WM_STATE_HIDDEN]) + && !self.window_is_any_of_states(window, &[self.atoms._NET_WM_STATE_HIDDEN]) && match initial_state { properties::WmHintsState::Normal => true, _ => false, @@ -1909,7 +1861,7 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { &self, window: Window, ) -> String { - const NO_NAME: &str = "n/a"; + static NO_NAME: &str = "n/a"; self.conn .get_property( @@ -1920,14 +1872,14 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { 0, std::u32::MAX, ) - .map_or(String::from(NO_NAME), |cookie| { - cookie.reply().map_or(String::from(NO_NAME), |reply| { + .map_or(NO_NAME.to_owned(), |cookie| { + cookie.reply().map_or(NO_NAME.to_owned(), |reply| { std::str::from_utf8( &reply .value8() - .map_or(Vec::new(), |value| value.collect::<Vec<u8>>()), + .map_or(Vec::with_capacity(0), |value| value.collect::<Vec<u8>>()), ) - .map_or(String::from(NO_NAME), |name| name.to_string()) + .map_or(NO_NAME.to_owned(), |name| name.to_owned()) }) }) } @@ -1937,12 +1889,12 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { &self, window: Window, ) -> String { - const NO_CLASS: &str = "n/a"; + static NO_CLASS: &str = "n/a"; - properties::WmClass::get(self.conn, window).map_or(String::from(NO_CLASS), |cookie| { - cookie.reply().map_or(String::from(NO_CLASS), |reply| { + properties::WmClass::get(self.conn, window).map_or(NO_CLASS.to_owned(), |cookie| { + cookie.reply().map_or(NO_CLASS.to_owned(), |reply| { std::str::from_utf8(reply.class()) - .map_or(String::from(NO_CLASS), |class| String::from(class)) + .map_or(NO_CLASS.to_owned(), |class| class.to_owned()) }) }) } @@ -1952,12 +1904,12 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { &self, window: Window, ) -> String { - const NO_INSTANCE: &str = "n/a"; + static NO_INSTANCE: &str = "n/a"; - properties::WmClass::get(self.conn, window).map_or(String::from(NO_INSTANCE), |cookie| { - cookie.reply().map_or(String::from(NO_INSTANCE), |reply| { + properties::WmClass::get(self.conn, window).map_or(NO_INSTANCE.to_owned(), |cookie| { + cookie.reply().map_or(NO_INSTANCE.to_owned(), |reply| { std::str::from_utf8(reply.instance()) - .map_or(String::from(NO_INSTANCE), |instance| String::from(instance)) + .map_or(NO_INSTANCE.to_owned(), |instance| instance.to_owned()) }) }) } @@ -1978,7 +1930,8 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { ) .ok()? .reply() - .map_or(None, |transient_for| { + .ok() + .and_then(|transient_for| { let transient_for: Vec<u32> = transient_for.value32()?.collect(); if transient_for.is_empty() { @@ -2005,7 +1958,8 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { ) .ok()? .reply() - .map_or(None, |client_leader| { + .ok() + .and_then(|client_leader| { let client_leader: Vec<u32> = client_leader.value32()?.collect(); if client_leader.is_empty() { @@ -2029,6 +1983,7 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { let urgent = hints.urgent; let input = hints.input; let group = hints.window_group; + let initial_state = hints.initial_state.map(|state| match state { properties::WmHintsState::Normal => IcccmWindowState::Normal, properties::WmHintsState::Iconic => IcccmWindowState::Iconic, @@ -2051,10 +2006,10 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { ) -> (bool, Option<SizeHints>) { let size_hints = properties::WmSizeHints::get_normal_hints(self.conn, window) .ok() - .map_or(None, |cookie| cookie.reply().ok()); + .and_then(|cookie| cookie.reply().ok()); if size_hints.is_none() { - return (current_size_hints.is_none(), None); + return (!current_size_hints.is_none(), None); } let size_hints = size_hints.unwrap(); @@ -2079,9 +2034,9 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { let (sh_min_width, sh_min_height) = size_hints.min_size.map_or((None, None), |(width, height)| { ( - if width > 0 { Some(width as u32) } else { None }, + if width > 0 { Some(width) } else { None }, if height > 0 { - Some(height as u32) + Some(height as i32) } else { None }, @@ -2093,24 +2048,16 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { .base_size .map_or((None, None), |(width, height)| { ( - if width > 0 { Some(width as u32) } else { None }, - if height > 0 { - Some(height as u32) - } else { - None - }, + if width > 0 { Some(width) } else { None }, + if height > 0 { Some(height) } else { None }, ) }); let (max_width, max_height) = size_hints.max_size.map_or((None, None), |(width, height)| { ( - if width > 0 { Some(width as u32) } else { None }, - if height > 0 { - Some(height as u32) - } else { - None - }, + if width > 0 { Some(width) } else { None }, + if height > 0 { Some(height) } else { None }, ) }); @@ -2174,12 +2121,12 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { .map_or((None, None), |(inc_width, inc_height)| { ( if inc_width > 0 && inc_width < 0xFFFF { - Some(inc_width as u32) + Some(inc_width) } else { None }, if inc_height > 0 && inc_height < 0xFFFF { - Some(inc_height as u32) + Some(inc_height) } else { None }, @@ -2232,7 +2179,7 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { max_ratio_vulgar, }); - (*current_size_hints == size_hints, size_hints) + (*current_size_hints != size_hints, size_hints) } fn init_wm_properties( @@ -2404,30 +2351,6 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { } #[inline] - fn set_window_above( - &self, - window: Window, - on: bool, - ) { - } - - #[inline] - fn set_window_fullscreen( - &self, - window: Window, - on: bool, - ) { - } - - #[inline] - fn set_window_below( - &self, - window: Window, - on: bool, - ) { - } - - #[inline] fn set_window_state( &self, window: Window, @@ -2462,10 +2385,10 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { ) { let mut frame_extents: Vec<u32> = Vec::with_capacity(4); - frame_extents.push(extents.left); - frame_extents.push(extents.right); - frame_extents.push(extents.top); - frame_extents.push(extents.bottom); + frame_extents.push(extents.left as u32); + frame_extents.push(extents.right as u32); + frame_extents.push(extents.top as u32); + frame_extents.push(extents.bottom as u32); drop(self.conn.change_property32( xproto::PropMode::REPLACE, @@ -2486,8 +2409,8 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { geometries.iter().for_each(|geometry| { areas.push(geometry.pos.x as u32); areas.push(geometry.pos.y as u32); - areas.push(geometry.dim.w); - areas.push(geometry.dim.h); + areas.push(geometry.dim.w as u32); + areas.push(geometry.dim.h as u32); }); drop(self.conn.change_property32( @@ -2509,8 +2432,8 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { viewports.iter().for_each(|viewport| { areas.push(viewport.pos.x as u32); areas.push(viewport.pos.y as u32); - areas.push(viewport.dim.w); - areas.push(viewport.dim.h); + areas.push(viewport.dim.w as u32); + areas.push(viewport.dim.h as u32); }); drop(self.conn.change_property32( @@ -2532,8 +2455,8 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { workareas.iter().for_each(|workarea| { areas.push(workarea.pos.x as u32); areas.push(workarea.pos.y as u32); - areas.push(workarea.dim.w); - areas.push(workarea.dim.h); + areas.push(workarea.dim.w as u32); + areas.push(workarea.dim.h as u32); }); drop(self.conn.change_property32( @@ -2615,7 +2538,8 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { ) .ok()? .reply() - .map_or(None, |strut| { + .ok() + .and_then(|strut| { let widths: Vec<u32> = strut.value32()?.collect(); if widths.is_empty() { @@ -2659,7 +2583,8 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { ) .ok()? .reply() - .map_or(None, |strut_partial| { + .ok() + .and_then(|strut_partial| { let widths: Vec<u32> = strut_partial.value32()?.collect(); if widths.is_empty() { @@ -2703,7 +2628,8 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { ) .ok()? .reply() - .map_or(None, |desktop| { + .ok() + .and_then(|desktop| { let desktop: Vec<u32> = desktop.value32()?.collect(); if desktop.is_empty() { @@ -2743,8 +2669,9 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { .ok() .and_then(|cookie| cookie.reply().ok()) .map(|types| { - let types: Vec<u32> = - types.value32().map_or(Vec::new(), |value| value.collect()); + let types: Vec<u32> = types + .value32() + .map_or(Vec::with_capacity(0), |value| value.collect()); for type_ in types { if let Some(type_) = self.get_window_type_from_atom(type_) { @@ -2762,7 +2689,7 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { &self, window: Window, ) -> Option<WindowState> { - self.get_window_states(window).get(0).map(|&state| state) + self.get_window_states(window).get(0).copied() } fn get_window_states( @@ -2784,8 +2711,9 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { .ok() .and_then(|cookie| cookie.reply().ok()) .map(|states| { - let states: Vec<u32> = - states.value32().map_or(Vec::new(), |value| value.collect()); + let states: Vec<u32> = states + .value32() + .map_or(Vec::with_capacity(0), |value| value.collect()); for state in states { if let Some(state) = self.get_window_state_from_atom(state) { @@ -2803,7 +2731,7 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { &self, window: Window, ) -> bool { - self.window_is_any_of_state(window, &[self.atoms._NET_WM_STATE_FULLSCREEN]) + self.window_is_any_of_states(window, &[self.atoms._NET_WM_STATE_FULLSCREEN]) } #[inline] @@ -2811,7 +2739,7 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { &self, window: Window, ) -> bool { - self.window_is_any_of_state(window, &[self.atoms._NET_WM_STATE_ABOVE]) + self.window_is_any_of_states(window, &[self.atoms._NET_WM_STATE_ABOVE]) } #[inline] @@ -2819,7 +2747,7 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { &self, window: Window, ) -> bool { - self.window_is_any_of_state(window, &[self.atoms._NET_WM_STATE_BELOW]) + self.window_is_any_of_states(window, &[self.atoms._NET_WM_STATE_BELOW]) } #[inline] @@ -2827,17 +2755,7 @@ impl<'a, C: connection::Connection> Connection for XConnection<'a, C> { &self, window: Window, ) -> bool { - let has_sticky_state = - self.window_is_any_of_state(window, &[self.atoms._NET_WM_STATE_STICKY]); - - if has_sticky_state { - return true; - } - - if let Some(desktop) = self.get_window_desktop(window) { - desktop == 0xFFFFFFFF - } else { - false - } + self.window_is_any_of_states(window, &[self.atoms._NET_WM_STATE_STICKY]) + || self.get_window_desktop(window) == Some(0xFFFFFFFF) } }