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 754009d630cdfe78d466837556098b4192953a8e
parent 8d854765c3bce1d1f83e3aaf10249d182ed728fa
Author: deurzen <m.deurzen@tum.de>
Date:   Thu, 11 Mar 2021 22:24:04 +0100

implements initial layout arranging capabilities

Diffstat:
Msrc/core/client.rs | 23-----------------------
Msrc/core/common.rs | 48------------------------------------------------
Msrc/core/model.rs | 217++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/core/workspace.rs | 38+++++++++++++++++++++++++++-----------
Msrc/core/zone.rs | 230++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Msrc/winsys/common.rs | 4++--
6 files changed, 326 insertions(+), 234 deletions(-)

diff --git a/src/core/client.rs b/src/core/client.rs @@ -26,7 +26,6 @@ pub struct Client { inner_region: Region, free_region: Region, tile_region: Region, - tree_region: Region, frame_extents: Option<Extents>, size_hints: Option<SizeHints>, warp_pos: Option<Pos>, @@ -86,7 +85,6 @@ impl Client { inner_region: Region::default(), free_region: Region::default(), tile_region: Region::default(), - tree_region: Region::default(), frame_extents: None, size_hints: None, warp_pos: None, @@ -294,26 +292,6 @@ impl Client { } #[inline] - pub fn tree_region(&self) -> &Region { - &self.tree_region - } - - #[inline] - pub fn set_tree_region( - &mut self, - tree_region: &Region, - ) { - if let Some(warp_pos) = self.warp_pos { - if !tree_region.encompasses(self.active_region.pos + warp_pos) { - self.unset_warp_pos(); - } - } - - self.tree_region = *tree_region; - self.set_active_region(tree_region); - } - - #[inline] pub fn frame_extents_unchecked(&self) -> Extents { if let Some(frame_extents) = self.frame_extents { frame_extents @@ -706,7 +684,6 @@ impl std::fmt::Debug for Client { .field("inner_region", &self.inner_region) .field("free_region", &self.free_region) .field("tile_region", &self.tile_region) - .field("tree_region", &self.tree_region) .field("frame_extents", &self.frame_extents) .field("size_hints", &self.size_hints) .field("warp_pos", &self.warp_pos) diff --git a/src/core/common.rs b/src/core/common.rs @@ -89,51 +89,3 @@ pub struct BorderSize { pub top: u32, pub bottom: u32, } - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct Placement { - pub window: Window, - pub region: Option<Region>, - pub extents: Option<Extents>, -} - -impl Placement { - pub fn new( - window: Window, - region: Option<Region>, - extents: Option<Extents>, - ) -> Self { - Self { - window, - region, - extents, - } - } - - pub fn inner_region(&self) -> Option<Region> { - if let Some(region) = self.region { - if let Some(extents) = self.extents { - return Some(Region { - pos: Pos { - x: extents.left as i32, - y: extents.top as i32, - }, - dim: Dim { - w: region.dim.w - extents.left - extents.right, - h: region.dim.h - extents.top - extents.bottom, - }, - }); - } else { - return Some(Region { - pos: Pos { - x: 0, - y: 0, - }, - dim: region.dim, - }); - } - } - - None - } -} diff --git a/src/core/model.rs b/src/core/model.rs @@ -24,11 +24,12 @@ use crate::stack::StackManager; use crate::workspace::Buffer; use crate::workspace::BufferKind; use crate::workspace::Workspace; +use crate::zone::Decoration; +use crate::zone::Frame; use crate::zone::Layout; +use crate::zone::LayoutMethod; use crate::zone::Placement; use crate::zone::PlacementKind; -use crate::zone::Frame; -use crate::zone::Decoration; use crate::zone::ZoneContent; use crate::zone::ZoneId; use crate::zone::ZoneManager; @@ -155,6 +156,8 @@ impl<'a> Model<'a> { i as u32, id, )); + + model.zone_manager.get_zone_mut(id).set_region(region); } model.workspaces.activate_for(&Selector::AtIndex(0)); @@ -347,35 +350,40 @@ impl<'a> Model<'a> { // TODO: zone change let region = self.active_screen().placeable_region(); - for placement in workspace.arrange_with_filter( - &mut self.zone_manager, - region, - &self.client_map, - |client| { - // TODO: zone change - Self::is_applyable(client) - }, - ) { + let (method, placements) = + workspace.arrange(&mut self.zone_manager, region); + let (show, hide): (Vec<&Placement>, Vec<&Placement>) = placements + .iter() + .partition(|&placement| placement.region.is_some()); + + for placement in show { match placement.kind { PlacementKind::Client(window) => { let frame = self.frame(window).unwrap(); - // TODO: sort placements by .region = Some > None - if placement.region.is_some() { - // TODO: zone change - self.update_client_placement(&placement); - // TODO: zone change - self.place_client(window); - - self.map_client(frame); - } else { - self.unmap_client(frame); - } + // TODO: zone change + self.update_client_placement( + &placement, + LayoutMethod::Tile, + ); + // TODO: zone change + self.place_client(window, LayoutMethod::Tile); + self.map_client(frame); }, PlacementKind::Tab(size) => {}, PlacementKind::Layout => {}, }; + } + for placement in hide { + match placement.kind { + PlacementKind::Client(window) => { + let frame = self.frame(window).unwrap(); + self.unmap_client(frame); + }, + PlacementKind::Tab(size) => {}, + PlacementKind::Layout => {}, + }; } if must_apply_stack { @@ -804,7 +812,7 @@ impl<'a> Model<'a> { if let Some(current_workspace) = self.workspaces.get(workspace) { let parent_zone = current_workspace .active_zone() - .and_then(|id| self.zone_manager.nearest_cycle(id)); + .map(|id| self.zone_manager.nearest_cycle(id)); let id = self .zone_manager @@ -877,11 +885,13 @@ impl<'a> Model<'a> { let active_region = client.active_region(); let current_pos = self.conn.get_pointer_position(); - if let Some(warp_pos) = - active_region.quadrant_center_from_pos(current_pos) - { - self.conn.warp_pointer(warp_pos); - } + println!("ACTIVE REGION {:?}", active_region); + + // if let Some(warp_pos) = + // active_region.quadrant_center_from_pos(current_pos) + // { + // self.conn.warp_pointer(warp_pos); + // } } fn remanage( @@ -1087,34 +1097,30 @@ impl<'a> Model<'a> { fn update_client_placement( &mut self, placement: &Placement, - // method: LayoutMethod, + method: LayoutMethod, ) { match placement.kind { PlacementKind::Client(window) => { let client = self.client_mut(window).unwrap(); - client.set_frame_extents(placement.decoration.frame.map(|f| f.extents)); - } - _ => {} - } - + let region = &placement.region.unwrap(); - // LayoutMethod::Free => client.set_free_region(&region), - // LayoutMethod::Tile => client.set_tile_region(&region), + client.set_frame_extents( + placement.decoration.frame.map(|f| f.extents), + ); - // TODO: zone change - // if let Some(region) = placement.region { - // match method { - // LayoutMethod::Free => client.set_free_region(&region), - // LayoutMethod::Tile => client.set_tile_region(&region), - // LayoutMethod::Tree => client.set_tree_region(&region), - // }; - // } + match method { + LayoutMethod::Free => client.set_free_region(region), + LayoutMethod::Tile => client.set_tile_region(region), + }; + }, + _ => panic!("attempting to update non-client placement"), + } } fn place_client( &self, window: Window, - // method: LayoutMethod, + method: LayoutMethod, ) { let client = self.client(window).unwrap(); @@ -1124,11 +1130,10 @@ impl<'a> Model<'a> { self.conn.place_window(window, inner_region); // TODO: zone change - // self.conn.place_window(frame, match method { - // LayoutMethod::Free => &client.free_region(), - // LayoutMethod::Tile => &client.tile_region(), - // LayoutMethod::Tree => &client.tree_region(), - // }); + self.conn.place_window(frame, match method { + LayoutMethod::Free => &client.free_region(), + LayoutMethod::Tile => &client.tile_region(), + }); self.refresh_client(window); self.conn.update_window_offset(window, frame); @@ -2314,27 +2319,26 @@ impl<'a> Model<'a> { let id = self.zone_manager.client_zone(window); let extents = *client.frame_extents(); + let decoration = Decoration { + border: None, + frame: extents.map(|e| Frame { + extents: e, + colors: Default::default(), + }), + }; let placement = Placement { kind: PlacementKind::Client(window), zone: id, region: Some(region), // TODO: zone change: should be proper frame - decoration: Decoration { - border: None, - frame: extents.map(|e| { - Frame { - extents: e, - colors: Default::default(), - } - }), - } + decoration, }; // TODO: zone change - self.update_client_placement(&placement); + self.update_client_placement(&placement, LayoutMethod::Free); // TODO: zone change - self.place_client(window); + self.place_client(window, LayoutMethod::Free); } } } @@ -2382,19 +2386,17 @@ impl<'a> Model<'a> { // TODO: zone change: should be proper frame decoration: Decoration { border: None, - frame: extents.map(|e| { - Frame { - extents: e, - colors: Default::default(), - } + frame: extents.map(|e| Frame { + extents: e, + colors: Default::default(), }), - } + }, }; // TODO: zone change - self.update_client_placement(&placement); + self.update_client_placement(&placement, LayoutMethod::Free); // TODO: zone change - self.place_client(window); + self.place_client(window, LayoutMethod::Free); } } } @@ -2462,19 +2464,17 @@ impl<'a> Model<'a> { // TODO: zone change: should be proper frame decoration: Decoration { border: None, - frame: extents.map(|e| { - Frame { - extents: e, - colors: Default::default(), - } + frame: extents.map(|e| Frame { + extents: e, + colors: Default::default(), }), - } + }, }; // TODO: zone change - self.update_client_placement(&placement); + self.update_client_placement(&placement, LayoutMethod::Free); // TODO: zone change - self.place_client(window); + self.place_client(window, LayoutMethod::Free); } } } @@ -2576,19 +2576,17 @@ impl<'a> Model<'a> { // TODO: zone change: should be proper frame decoration: Decoration { border: None, - frame: extents.map(|e| { - Frame { - extents: e, - colors: Default::default(), - } + frame: extents.map(|e| Frame { + extents: e, + colors: Default::default(), }), - } + }, }; // TODO: zone change - self.update_client_placement(&placement); + self.update_client_placement(&placement, LayoutMethod::Free); // TODO: zone change - self.place_client(window); + self.place_client(window, LayoutMethod::Free); } } } @@ -2635,31 +2633,33 @@ impl<'a> Model<'a> { let window = client.window(); let id = self.zone_manager.client_zone(window); let extents = *client.frame_extents(); + let region = Region { + pos: window_region.pos + grip_pos.dist(*pos), + dim: client.free_region().dim, + }; let placement = Placement { kind: PlacementKind::Client(window), zone: id, - region: Some(Region { - pos: window_region.pos + grip_pos.dist(*pos), - dim: client.free_region().dim, - }), + region: Some(region), // TODO: zone change: should be proper frame decoration: Decoration { border: None, - frame: extents.map(|e| { - Frame { - extents: e, - colors: Default::default(), - } + frame: extents.map(|e| Frame { + extents: e, + colors: Default::default(), }), - } + }, }; // TODO: zone change - self.update_client_placement(&placement); + self.update_client_placement( + &placement, + LayoutMethod::Free, + ); // TODO: zone change - self.place_client(window); + self.place_client(window, LayoutMethod::Free); } } } @@ -2773,13 +2773,13 @@ impl<'a> Model<'a> { extents: frame_extents, colors: Default::default(), }), - } + }, }; // TODO: zone change - self.update_client_placement(&placement); + self.update_client_placement(&placement, LayoutMethod::Free); // TODO: zone change - self.place_client(window); + self.place_client(window, LayoutMethod::Free); } } } @@ -3334,19 +3334,20 @@ impl<'a> Model<'a> { // TODO: zone change: should be proper frame decoration: Decoration { border: None, - frame: extents.map(|e| { - Frame { - extents: e, - colors: Default::default(), - } + frame: extents.map(|e| Frame { + extents: e, + colors: Default::default(), }), - } + }, }; // TODO: zone change - self.update_client_placement(&placement); + self.update_client_placement( + &placement, + LayoutMethod::Free, + ); // TODO: zone change - self.place_client(window); + self.place_client(window, LayoutMethod::Free); } } } else { diff --git a/src/core/workspace.rs b/src/core/workspace.rs @@ -7,6 +7,7 @@ use crate::common::Index; use crate::cycle::Cycle; use crate::cycle::InsertPos; use crate::cycle::Selector; +use crate::zone::LayoutMethod; use crate::zone::Placement; use crate::zone::ZoneId; use crate::zone::ZoneManager; @@ -148,7 +149,7 @@ impl Workspace { number, name: name.into(), root_zone, - zones: Cycle::new(Vec::new(), true), + zones: Cycle::new(vec![root_zone], true), clients: Cycle::new(Vec::new(), true), icons: Cycle::new(Vec::new(), true), } @@ -162,6 +163,10 @@ impl Workspace { &self.name } + pub fn root_zone(&self) -> ZoneId { + self.root_zone + } + pub fn set_name( &mut self, name: impl Into<String>, @@ -263,6 +268,19 @@ impl Workspace { self.clients.remove_for(&Selector::AtIdent(window)); } + pub fn activate_zone( + &mut self, + id: ZoneId, + ) -> Option<ZoneId> { + let prev_active = match self.zones.active_element() { + Some(z) => *z, + None => return None, + }; + + self.zones.activate_for(&Selector::AtIdent(id)); + Some(prev_active) + } + pub fn focus_client( &mut self, window: Window, @@ -294,22 +312,20 @@ impl Workspace { self.clients.remove_for(&Selector::AtActive) } - pub fn arrange_with_filter<F>( + pub fn arrange( &self, zone_manager: &mut ZoneManager, screen_region: Region, - client_map: &HashMap<Window, Client>, - filter: F, - ) -> Vec<Placement> - where - F: Fn(&Client) -> bool, - { + ) -> (LayoutMethod, Vec<Placement>) { if !self.clients.is_empty() { // TODO: zone change - // zone_manager.arrange(self.root_zone) - Vec::with_capacity(0) + let placements = zone_manager.arrange(self.root_zone); + + println!("!!!!! {:#?}", placements); + + placements } else { - Vec::with_capacity(0) + (LayoutMethod::Free, Vec::with_capacity(0)) } } diff --git a/src/core/zone.rs b/src/core/zone.rs @@ -1,9 +1,12 @@ use crate::common::Ident; use crate::common::Identify; use crate::cycle::Cycle; +use crate::cycle::InsertPos; +use winsys::common::Dim; use winsys::common::Extents; use winsys::common::Padding; +use winsys::common::Pos; use winsys::common::Region; use winsys::common::Window; @@ -108,6 +111,37 @@ pub struct Placement { pub decoration: Decoration, } +impl Placement { + pub fn inner_region(&self) -> Option<Region> { + if let Some(region) = self.region { + if let Some(frame) = self.decoration.frame { + let extents = frame.extents; + + return Some(Region { + pos: Pos { + x: extents.left as i32, + y: extents.top as i32, + }, + dim: Dim { + w: region.dim.w - extents.left - extents.right, + h: region.dim.h - extents.top - extents.bottom, + }, + }); + } else { + return Some(Region { + pos: Pos { + x: 0, + y: 0, + }, + dim: region.dim, + }); + } + } + + None + } +} + type LayoutFn = fn(&Region, &LayoutData, Vec<bool>) -> Vec<(Disposition, bool)>; #[derive(Debug, PartialEq, Eq, Clone, Copy)] @@ -117,9 +151,6 @@ pub enum LayoutMethod { /// Arranges clients along a predefined layout Tile, - - /// Semi-adjustable tree-based layout - Tree, } #[non_exhaustive] @@ -140,6 +171,20 @@ pub enum LayoutKind { Stack = b'S', } +#[inline] +fn stack_split( + n: usize, + n_main: u32, +) -> (u32, u32) { + let n = n as u32; + + if n <= n_main { + (n, 0) + } else { + (n_main, n - n_main) + } +} + impl LayoutKind { pub fn symbol(&self) -> char { (*self as u8) as char @@ -158,7 +203,25 @@ impl LayoutKind { LayoutKind::Monocle => LayoutConfig::default(), LayoutKind::Paper => LayoutConfig::default(), LayoutKind::SStack => LayoutConfig::default(), - LayoutKind::Stack => LayoutConfig::default(), + LayoutKind::Stack => LayoutConfig { + method: LayoutMethod::Tile, + decoration: Decoration { + frame: Some(Frame { + extents: Extents { + left: 0, + right: 0, + top: 3, + bottom: 0, + }, + colors: Default::default(), + }), + border: None, + }, + root_only: false, + persistent: false, + single: false, + wraps: true, + }, #[allow(unreachable_patterns)] _ => unimplemented!( @@ -180,7 +243,75 @@ impl LayoutKind { .map(|&b| (Disposition::Unchanged, b)) .collect() }, - _ => |_, _, _| Vec::with_capacity(0), + LayoutKind::Stack => |region, data, active_map| { + let n = active_map.len(); + let (pos, dim) = region.values(); + + if n == 1 { + return vec![( + Disposition::Changed(*region, Decoration { + border: None, + frame: None, + }), + true, + )]; + } + + let (n_main, n_stack) = 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 split = if data.main_count > 0 { + (dim.w as f32 * data.main_factor) as i32 + } else { + 0 + }; + + let config = &LayoutKind::Stack.config(); + active_map + .iter() + .enumerate() + .map(|(i, _)| { + let i = i as u32; + + if i < data.main_count { + let w = + if n_stack == 0 { dim.w } else { split as u32 }; + + println!("UNDER MAINCOUNT = {}", i); + + ( + Disposition::Changed( + Region::new( + pos.x, + pos.y + (i * h_main) as i32, + w, + h_main, + ), + config.decoration, + ), + true, + ) + } else { + println!("OVER MAINCOUNT = {}", i); + let sn = (i - data.main_count) as i32; + + ( + Disposition::Changed( + Region::new( + pos.x + split, + pos.y + sn * h_stack as i32, + dim.w - split as u32, + h_stack, + ), + config.decoration, + ), + true, + ) + } + }) + .collect() + }, #[allow(unreachable_patterns)] _ => unimplemented!( @@ -197,7 +328,6 @@ struct LayoutConfig { method: LayoutMethod, decoration: Decoration, root_only: bool, - free: bool, persistent: bool, single: bool, wraps: bool, @@ -209,7 +339,6 @@ impl Default for LayoutConfig { method: LayoutMethod::Free, decoration: Default::default(), root_only: true, - free: true, persistent: false, single: false, wraps: true, @@ -235,8 +364,8 @@ impl Default for LayoutData { margin: None, gap_size: 0u32, - main_count: 0u32, - main_factor: 0f32, + main_count: 1u32, + main_factor: 0.50f32, } } } @@ -259,7 +388,7 @@ impl Layout { } Self { - kind: LayoutKind::Float, + kind: LayoutKind::Stack, prev_kind: LayoutKind::Float, data, default_data, @@ -367,6 +496,13 @@ impl Zone { is_visible, }) } + + pub fn set_region( + &mut self, + region: Region, + ) { + self.region = region; + } } enum ZoneChange { @@ -401,7 +537,22 @@ impl ZoneManager { self.client_zones.insert(*window, id); } + let parent = parent.and_then(|p| self.zone_map.get_mut(&p)); + + if let Some(parent) = parent { + match &mut parent.content { + ZoneContent::Tab(zones) | ZoneContent::Layout(_, zones) => { + zones.insert_at(&InsertPos::AfterActive, id) + }, + _ => panic!("attempted to insert into non-cycle"), + } + } + self.zone_map.insert(id, zone); + + let cycle = self.nearest_cycle(id); + self.arrange(cycle); + id } @@ -436,23 +587,23 @@ impl ZoneManager { pub fn nearest_cycle( &self, id: ZoneId, - ) -> Option<ZoneId> { - let mut id = id; + ) -> ZoneId { + let mut next = id; loop { - let zone = self.zone_map.get(&id).unwrap(); + let zone = self.zone_map.get(&next).unwrap(); match zone.content { ZoneContent::Tab(_) | ZoneContent::Layout(..) => { - return Some(id) + return next; }, _ => {}, } if let Some(parent) = zone.parent { - id = parent; + next = parent; } else { - return None; + panic!("no nearest cycle found"); } } } @@ -502,19 +653,13 @@ impl ZoneManager { pub fn arrange( &mut self, zone: ZoneId, - ) -> Vec<Placement> { - let id = zone; - let zone = self.zone_map.get_mut(&id); - let region; - - if let Some(zone) = zone { - region = zone.region; - zone.is_visible = true; - } else { - return Vec::with_capacity(0); - } + ) -> (LayoutMethod, Vec<Placement>) { + let id = self.nearest_cycle(zone); + let zone = self.zone_map.get_mut(&id).unwrap(); + let region = zone.region; - self.arrange_subzones(id, region) + zone.is_visible = true; + (LayoutMethod::Tile, self.arrange_subzones(id, region)) } fn arrange_subzones( @@ -556,15 +701,18 @@ impl ZoneManager { ); let active_element = zones.active_element(); - zones.iter().filter(|&id| { - if let Some(active_element) = active_element { - active_element != id - } else { - true - } - }).for_each(|&id| { - zone_changes.push((id, ZoneChange::Visible(false))); - }); + zones + .iter() + .filter(|&id| { + if let Some(active_element) = active_element { + active_element != id + } else { + true + } + }) + .for_each(|&id| { + zone_changes.push((id, ZoneChange::Visible(false))); + }); match active_element { None => placements, @@ -602,10 +750,7 @@ impl ZoneManager { &region, zones .iter() - .map(|id| { - let zone = self.zone_map.get(id).unwrap(); - zone.is_active - }) + .map(|id| self.zone_map.get(id).unwrap().is_active) .collect(), ); @@ -653,7 +798,8 @@ impl ZoneManager { zone_changes.iter().for_each(|(id, change)| { let zone = self.zone_map.get_mut(id).unwrap(); - let placement_kind = PlacementKind::from_zone_content(&zone.content); + let placement_kind = + PlacementKind::from_zone_content(&zone.content); let region = zone.region; let decoration = zone.decoration; diff --git a/src/winsys/common.rs b/src/winsys/common.rs @@ -250,7 +250,7 @@ impl Region { &self, width: u32, ) -> (Self, Self) { - assert!(width < self.dim.w, "Desired width exceeds divisible width."); + assert!(width < self.dim.w, "desired width exceeds divisible width."); ( Self { @@ -279,7 +279,7 @@ impl Region { ) -> (Self, Self) { assert!( height < self.dim.h, - "Desired height exceeds divisible height." + "desired height exceeds divisible height." ); (