From 3bc58ec02a00a243ab8b5f47baa625cc6a509a41 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Thu, 31 Mar 2022 13:44:16 +0200 Subject: [PATCH] tiling: Don't store trees for outputs, but for indices --- Cargo.lock | 7 + Cargo.toml | 1 + src/shell/layout/floating/mod.rs | 2 +- src/shell/layout/mod.rs | 26 +- src/shell/layout/tiling/grabs.rs | 46 +-- src/shell/layout/tiling/mod.rs | 490 +++++++++++++++---------------- src/shell/workspace.rs | 2 +- 7 files changed, 283 insertions(+), 291 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2d623265..496e0bba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -74,6 +74,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5d78ce20460b82d3fa150275ed9d55e21064fc7951177baacf86a145c4a4b1f" +[[package]] +name = "atomic_float" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62af46d040ba9df09edc6528dae9d8e49f5f3e82f55b7d2ec31a733c38dbc49d" + [[package]] name = "atomic_refcell" version = "0.1.8" @@ -292,6 +298,7 @@ name = "cosmic-comp" version = "0.1.0" dependencies = [ "anyhow", + "atomic_float", "bitflags", "edid-rs", "egui", diff --git a/Cargo.toml b/Cargo.toml index c89fe9b8..6f97f120 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ xkbcommon = "0.4" indexmap = "1.8.0" xdg = "^2.1" ron = "0.7" +atomic_float = "0.1" [dependencies.smithay] version = "0.3" diff --git a/src/shell/layout/floating/mod.rs b/src/shell/layout/floating/mod.rs index 08806b90..e33b86c4 100644 --- a/src/shell/layout/floating/mod.rs +++ b/src/shell/layout/floating/mod.rs @@ -26,7 +26,7 @@ impl Layout for FloatingLayout { seat: &Seat, _focus_stack: Box + 'a>, ) { - let output = super::output_from_seat(seat, space); + let output = super::output_from_seat(Some(seat), space); let win_geo = window.bbox(); let layers = layer_map_for_output(&output); let geometry = layers.non_exclusive_zone(); diff --git a/src/shell/layout/mod.rs b/src/shell/layout/mod.rs index 798b6a1a..4de5fa68 100644 --- a/src/shell/layout/mod.rs +++ b/src/shell/layout/mod.rs @@ -87,9 +87,9 @@ pub trait Layout { } } -pub fn new_default_layout(idx: u8) -> Box { +pub fn new_default_layout() -> Box { Box::new(combined::Combined::new( - tiling::TilingLayout::new(idx), + tiling::TilingLayout::new(), floating::FloatingLayout, |window| { if let Some(surface) = window.toplevel().get_surface() { @@ -128,14 +128,16 @@ pub fn new_default_layout(idx: u8) -> Box { )) } -fn output_from_seat(seat: &Seat, space: &Space) -> Output { - seat.user_data() - .get::() - .map(|active| active.0.borrow().clone()) - .or_else(|| { - seat.get_pointer() - .map(|ptr| space.output_under(ptr.current_location()).next().unwrap()) - .cloned() - }) - .unwrap_or_else(|| space.outputs().next().cloned().unwrap()) +fn output_from_seat(seat: Option<&Seat>, space: &Space) -> Output { + seat.and_then(|seat| { + seat.user_data() + .get::() + .map(|active| active.0.borrow().clone()) + .or_else(|| { + seat.get_pointer() + .map(|ptr| space.output_under(ptr.current_location()).next().unwrap()) + .cloned() + }) + }) + .unwrap_or_else(|| space.outputs().next().cloned().unwrap()) } diff --git a/src/shell/layout/tiling/grabs.rs b/src/shell/layout/tiling/grabs.rs index 77ee7b37..da7de702 100644 --- a/src/shell/layout/tiling/grabs.rs +++ b/src/shell/layout/tiling/grabs.rs @@ -1,24 +1,22 @@ // SPDX-License-Identifier: GPL-3.0-only -use super::{Data, Orientation, OutputInfo}; -use id_tree::{NodeId, Tree}; +use super::Orientation; +use atomic_float::AtomicF64; use smithay::{ - desktop::layer_map_for_output, reexports::wayland_server::protocol::{wl_pointer::ButtonState, wl_surface}, - utils::{Logical, Point}, + utils::{Logical, Point, Size}, wayland::{ - output::Output, seat::{AxisFrame, PointerGrab, PointerGrabStartData, PointerInnerHandle}, Serial, }, }; -use std::cell::RefCell; +use std::sync::{atomic::Ordering, Arc}; pub struct ResizeForkGrab { pub start_data: PointerGrabStartData, - pub node_id: NodeId, + pub orientation: Orientation, + pub initial_size: Size, pub initial_ratio: f64, - pub idx: u8, - pub output: Output, + pub ratio: Arc, } impl PointerGrab for ResizeForkGrab { @@ -34,28 +32,14 @@ impl PointerGrab for ResizeForkGrab { handle.motion(location, None, serial, time); let delta = location - self.start_data.location; - - let mut output_info = self - .output - .user_data() - .get::>() - .unwrap() - .borrow_mut(); - let tree = &mut output_info.trees.entry(self.idx).or_insert_with(Tree::new); - if let Some(&mut Data::Fork { - ref mut ratio, - ref orientation, - }) = tree.get_mut(&self.node_id).map(|x| x.data_mut()).ok() - { - let size = layer_map_for_output(&self.output).non_exclusive_zone().size; - let delta = match orientation { - Orientation::Vertical => delta.x / size.w as f64, - Orientation::Horizontal => delta.y / size.h as f64, - }; - *ratio = 0.9f64.min(0.1f64.max(self.initial_ratio + delta)); - } else { - handle.unset_grab(serial, time); - } + let delta = match self.orientation { + Orientation::Vertical => delta.x / self.initial_size.w as f64, + Orientation::Horizontal => delta.y / self.initial_size.h as f64, + }; + self.ratio.store( + 0.9f64.min(0.1f64.max(self.initial_ratio + delta)), + Ordering::SeqCst, + ); } fn button( diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index e2f09dfe..ff2456af 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only use crate::shell::layout::{FocusDirection, Layout, Orientation}; +use atomic_float::AtomicF64; use id_tree::{InsertBehavior, MoveBehavior, Node, NodeId, NodeIdError, RemoveBehavior, Tree}; use smithay::{ desktop::{layer_map_for_output, Kind, Space, Window}, @@ -9,27 +10,29 @@ use smithay::{ }, utils::Rectangle, wayland::{ - output::Output, seat::{PointerGrabStartData, Seat}, Serial, }, }; -use std::{cell::RefCell, collections::HashMap}; +use std::{ + cell::RefCell, + sync::{atomic::Ordering, Arc}, +}; mod grabs; pub use self::grabs::*; #[derive(Debug)] pub struct TilingLayout { - idx: u8, gaps: (i32, i32), + trees: Vec>, } #[derive(Debug)] pub enum Data { Fork { orientation: Orientation, - ratio: f64, + ratio: Arc, }, Stack { active: usize, @@ -41,33 +44,24 @@ pub enum Data { #[derive(Debug, Clone)] pub struct WindowInfo { node: NodeId, - output: Output, -} - -pub struct OutputInfo { - trees: HashMap>, -} - -impl Default for OutputInfo { - fn default() -> OutputInfo { - OutputInfo { - trees: HashMap::new(), - } - } + output: usize, } impl Data { fn fork() -> Data { Data::Fork { orientation: Orientation::Vertical, - ratio: 0.5, + ratio: Arc::new(AtomicF64::new(0.5)), } } } impl TilingLayout { - pub fn new(idx: u8) -> TilingLayout { - TilingLayout { idx, gaps: (0, 4) } + pub fn new() -> TilingLayout { + TilingLayout { + gaps: (0, 4), + trees: Vec::new(), + } } } @@ -77,72 +71,9 @@ impl Layout for TilingLayout { space: &mut Space, window: &Window, seat: &Seat, - mut focus_stack: Box + 'a>, + focus_stack: Box + 'a>, ) { - { - let output = super::output_from_seat(seat, space); - output - .user_data() - .insert_if_missing(|| RefCell::new(OutputInfo::default())); - let mut output_info = output - .user_data() - .get::>() - .unwrap() - .borrow_mut(); - let tree = &mut output_info.trees.entry(self.idx).or_insert_with(Tree::new); - - let new_window = Node::new(Data::Window(window.clone())); - - let last_active = focus_stack - .find_map(|window| tree.root_node_id() - .and_then(|root| tree.traverse_pre_order_ids(root).unwrap() - .find(|id| matches!(tree.get(id).map(|n| n.data()), Ok(Data::Window(w)) if w == window)) - ) - ); - let window_id = if let Some(ref node_id) = last_active { - let parent_id = tree.get(node_id).unwrap().parent().cloned(); - if let Some(stack_id) = parent_id - .filter(|id| matches!(tree.get(id).unwrap().data(), Data::Stack { .. })) - { - // we add to the stack - let window_id = tree - .insert(new_window, InsertBehavior::UnderNode(&stack_id)) - .unwrap(); - if let Data::Stack { - ref mut len, - ref mut active, - } = tree.get_mut(&stack_id).unwrap().data_mut() - { - *active = *len; - *len += 1; - } - Ok(window_id) - } else { - // we create a new fork - TilingLayout::new_fork(tree, node_id, new_window) - } - } else { - // nothing? then we add to the root - if let Some(root_id) = tree.root_node_id().cloned() { - TilingLayout::new_fork(tree, &root_id, new_window) - } else { - tree.insert(new_window, InsertBehavior::AsRoot) - } - } - .unwrap(); - - { - let user_data = window.user_data(); - let window_info = WindowInfo { - node: window_id.clone(), - output: output.clone(), - }; - // insert or update - if !user_data.insert_if_missing(|| RefCell::new(window_info.clone())) { - *user_data.get::>().unwrap().borrow_mut() = window_info; - } - } - } + self.map_window(space, window, Some(seat), Some(focus_stack)); self.refresh(space); } @@ -153,18 +84,10 @@ impl Layout for TilingLayout { space: &mut Space, focus_stack: Box + 'a>, ) -> Option { - let output = super::output_from_seat(seat, space); - output - .user_data() - .insert_if_missing(|| RefCell::new(OutputInfo::default())); - let mut output_info = output - .user_data() - .get::>() - .unwrap() - .borrow_mut(); - let tree = output_info.trees.entry(self.idx).or_insert_with(Tree::new); - - if let Some(last_active) = TilingLayout::last_active(tree, focus_stack) { + let output = super::output_from_seat(Some(seat), space); + let idx = space.outputs().position(|o| *o == output).unwrap_or(0); + let tree = TilingLayout::active_tree(&mut self.trees, idx); + if let Some(last_active) = TilingLayout::last_active_window(tree, focus_stack) { let mut node_id = last_active; while let Some((fork, child)) = TilingLayout::find_fork(tree, node_id) { if let &Data::Fork { @@ -217,36 +140,45 @@ impl Layout for TilingLayout { space: &mut Space, focus_stack: Box + 'a>, ) { - { - let output = super::output_from_seat(seat, space); - output - .user_data() - .insert_if_missing(|| RefCell::new(OutputInfo::default())); - let mut output_info = output - .user_data() - .get::>() - .unwrap() - .borrow_mut(); - let tree = &mut output_info.trees.entry(self.idx).or_insert_with(Tree::new); - - if let Some(last_active) = TilingLayout::last_active(tree, focus_stack) { - if let Some((fork, _child)) = TilingLayout::find_fork(tree, last_active) { - if let &mut Data::Fork { - ref mut orientation, - .. - } = tree.get_mut(&fork).unwrap().data_mut() - { - *orientation = new_orientation; - } + let output = super::output_from_seat(Some(seat), space); + let idx = space.outputs().position(|o| *o == output).unwrap_or(0); + let tree = TilingLayout::active_tree(&mut self.trees, idx); + if let Some(last_active) = TilingLayout::last_active_window(tree, focus_stack) { + if let Some((fork, _child)) = TilingLayout::find_fork(tree, last_active) { + if let &mut Data::Fork { + ref mut orientation, + .. + } = tree.get_mut(&fork).unwrap().data_mut() + { + *orientation = new_orientation; } } } self.refresh(space); } - fn refresh(&mut self, space: &mut Space) { + fn refresh<'a>(&mut self, space: &mut Space) { + let active_outputs = space.outputs().count(); + if self.trees.len() > active_outputs { + for tree in self + .trees + .drain(active_outputs..self.trees.len()) + .collect::>() + .into_iter() + { + if let Some(root_id) = tree.root_node_id() { + for node in tree.traverse_pre_order(root_id).unwrap() { + if let Data::Window(window) = node.data() { + self.map_window(space, window, None, None); + } + } + } + } + } while let Some(dead_windows) = Some(TilingLayout::update_space_positions( - self.idx, space, self.gaps, + &mut self.trees, + space, + self.gaps, )) .filter(|v| !v.is_empty()) { @@ -264,7 +196,7 @@ impl Layout for TilingLayout { fn resize_request( &mut self, - _space: &mut Space, + space: &mut Space, window: &Window, seat: &Seat, serial: Serial, @@ -273,14 +205,8 @@ impl Layout for TilingLayout { ) { if let Some(pointer) = seat.get_pointer() { if let Some(info) = window.user_data().get::>() { - let output = &info.borrow().output; - let mut output_info = output - .user_data() - .get::>() - .unwrap() - .borrow_mut(); - let tree = &mut output_info.trees.entry(self.idx).or_insert_with(Tree::new); - + let output = info.borrow().output; + let tree = TilingLayout::active_tree(&mut self.trees, output); let mut node_id = info.borrow().node.clone(); while let Some((fork, child)) = TilingLayout::find_fork(tree, node_id) { @@ -297,15 +223,19 @@ impl Layout for TilingLayout { | (false, Orientation::Horizontal, ResizeEdge::Top) | (true, Orientation::Vertical, ResizeEdge::Right) | (false, Orientation::Vertical, ResizeEdge::Left) => { - let grab = ResizeForkGrab { - start_data, - node_id: fork, - initial_ratio: *ratio, - idx: self.idx, - output: output.clone(), - }; + if let Some(output) = space.outputs().nth(output) { + let grab = ResizeForkGrab { + start_data, + orientation: *orientation, + initial_ratio: ratio.load(Ordering::SeqCst), + initial_size: layer_map_for_output(output) + .non_exclusive_zone() + .size, + ratio: ratio.clone(), + }; - pointer.set_grab(grab, serial, 0); + pointer.set_grab(grab, serial, 0); + } return; } _ => {} // continue iterating @@ -319,9 +249,16 @@ impl Layout for TilingLayout { } impl TilingLayout { - fn last_active<'a>( + fn active_tree<'a>(trees: &'a mut Vec>, output: usize) -> &'a mut Tree { + while trees.len() <= output { + trees.push(Tree::new()) + } + &mut trees[output] + } + + fn last_active_window<'a>( tree: &mut Tree, - mut focus_stack: Box + 'a>, + mut focus_stack: impl Iterator, ) -> Option { let last_active = focus_stack .find_map(|window| tree.root_node_id() @@ -343,18 +280,74 @@ impl TilingLayout { None } + fn map_window<'a>( + &mut self, + space: &mut Space, + window: &Window, + seat: Option<&Seat>, + focus_stack: Option + 'a>>, + ) { + let output = super::output_from_seat(seat, space); + let idx = space.outputs().position(|o| *o == output).unwrap_or(0); + let tree = TilingLayout::active_tree(&mut self.trees, idx); + let new_window = Node::new(Data::Window(window.clone())); + + let last_active = focus_stack.and_then(|mut iter| + iter.find_map(|window| tree.root_node_id() + .and_then(|root| tree.traverse_pre_order_ids(root).unwrap() + .find(|id| matches!(tree.get(id).map(|n| n.data()), Ok(Data::Window(w)) if w == window)) + ) + ) + ); + let window_id = if let Some(ref node_id) = last_active { + let parent_id = tree.get(node_id).unwrap().parent().cloned(); + if let Some(stack_id) = + parent_id.filter(|id| matches!(tree.get(id).unwrap().data(), Data::Stack { .. })) + { + // we add to the stack + let window_id = tree + .insert(new_window, InsertBehavior::UnderNode(&stack_id)) + .unwrap(); + if let Data::Stack { + ref mut len, + ref mut active, + } = tree.get_mut(&stack_id).unwrap().data_mut() + { + *active = *len; + *len += 1; + } + Ok(window_id) + } else { + // we create a new fork + TilingLayout::new_fork(tree, node_id, new_window) + } + } else { + // nothing? then we add to the root + if let Some(root_id) = tree.root_node_id().cloned() { + TilingLayout::new_fork(tree, &root_id, new_window) + } else { + tree.insert(new_window, InsertBehavior::AsRoot) + } + } + .unwrap(); + + { + let user_data = window.user_data(); + let window_info = WindowInfo { + node: window_id.clone(), + output: idx, + }; + // insert or update + if !user_data.insert_if_missing(|| RefCell::new(window_info.clone())) { + *user_data.get::>().unwrap().borrow_mut() = window_info; + } + } + } + fn unmap_window(&mut self, window: &Window) { if let Some(info) = window.user_data().get::>() { - let output = &info.borrow().output; - output - .user_data() - .insert_if_missing(|| RefCell::new(OutputInfo::default())); - let mut output_info = output - .user_data() - .get::>() - .unwrap() - .borrow_mut(); - let tree = output_info.trees.entry(self.idx).or_insert_with(Tree::new); + let output = info.borrow().output; + let tree = TilingLayout::active_tree(&mut self.trees, output); let node_id = info.borrow().node.clone(); let parent_id = tree @@ -453,110 +446,115 @@ impl TilingLayout { tree.insert(new, InsertBehavior::UnderNode(&fork_id)) } - fn update_space_positions(idx: u8, space: &mut Space, gaps: (i32, i32)) -> Vec { + fn update_space_positions( + trees: &mut Vec>, + space: &mut Space, + gaps: (i32, i32), + ) -> Vec { let mut dead_windows = Vec::new(); let (outer, inner) = gaps; - for output in space.outputs().cloned().collect::>().into_iter() { - if let Some(mut info) = output - .user_data() - .get::>() - .map(|x| x.borrow_mut()) - { - let tree = &mut info.trees.entry(idx).or_insert_with(Tree::new); - if let Some(root) = tree.root_node_id() { - let mut stack = Vec::new(); + for (idx, output) in space + .outputs() + .cloned() + .enumerate() + .collect::>() + .into_iter() + { + let tree = TilingLayout::active_tree(trees, idx); + if let Some(root) = tree.root_node_id() { + let mut stack = Vec::new(); - let mut geo = Some(layer_map_for_output(&output).non_exclusive_zone()); - // TODO saturate? minimum? - if let Some(mut geo) = geo.as_mut() { - geo.loc.x += outer; - geo.loc.y += outer; - geo.size.w -= outer * 2; - geo.size.h -= outer * 2; - } + let mut geo = Some(layer_map_for_output(&output).non_exclusive_zone()); + // TODO saturate? minimum? + if let Some(mut geo) = geo.as_mut() { + geo.loc.x += outer; + geo.loc.y += outer; + geo.size.w -= outer * 2; + geo.size.h -= outer * 2; + } - for node in tree.traverse_pre_order(root).unwrap() { - let geo = stack.pop().unwrap_or(geo); - match node.data() { - Data::Fork { orientation, ratio } => { - if let Some(geo) = geo { - match orientation { - Orientation::Horizontal => { - let top_size = ( - geo.size.w, - ((geo.size.h as f64) * ratio).ceil() as i32, - ); - stack.push(Some(Rectangle::from_loc_and_size( - (geo.loc.x, geo.loc.y + top_size.1), - (geo.size.w, geo.size.h - top_size.1), - ))); - stack.push(Some(Rectangle::from_loc_and_size( - geo.loc, top_size, - ))); - } - Orientation::Vertical => { - let left_size = ( - ((geo.size.w as f64) * ratio).ceil() as i32, - geo.size.h, - ); - stack.push(Some(Rectangle::from_loc_and_size( - (geo.loc.x + left_size.0, geo.loc.y), - (geo.size.w - left_size.0, geo.size.h), - ))); - stack.push(Some(Rectangle::from_loc_and_size( - geo.loc, left_size, - ))); - } - } - } else { - stack.push(None); - stack.push(None); - } - } - Data::Stack { active, len } => { - for i in 0..*len { - if i == *active { - stack.push(geo); - } else { - stack.push(None); - } - } - } - Data::Window(window) => { - if window.toplevel().alive() { - if let Some(geo) = geo { - #[allow(irrefutable_let_patterns)] - if let Kind::Xdg(xdg) = &window.toplevel() { - let ret = xdg.with_pending_state(|state| { - state.size = Some( - ( - geo.size.w - inner * 2, - geo.size.h - inner * 2, - ) - .into(), - ); - state.states.set(XdgState::TiledLeft); - state.states.set(XdgState::TiledRight); - state.states.set(XdgState::TiledTop); - state.states.set(XdgState::TiledBottom); - }); - if ret.is_ok() { - xdg.send_configure(); - } - } - let window_geo = window.geometry(); - space.map_window( - &window, - ( - geo.loc.x + inner - window_geo.loc.x, - geo.loc.y + inner - window_geo.loc.y, - ), - false, + for node in tree.traverse_pre_order(root).unwrap() { + let geo = stack.pop().unwrap_or(geo); + match node.data() { + Data::Fork { orientation, ratio } => { + if let Some(geo) = geo { + match orientation { + Orientation::Horizontal => { + let top_size = ( + geo.size.w, + ((geo.size.h as f64) * ratio.load(Ordering::SeqCst)) + .ceil() + as i32, ); + stack.push(Some(Rectangle::from_loc_and_size( + (geo.loc.x, geo.loc.y + top_size.1), + (geo.size.w, geo.size.h - top_size.1), + ))); + stack.push(Some(Rectangle::from_loc_and_size( + geo.loc, top_size, + ))); + } + Orientation::Vertical => { + let left_size = ( + ((geo.size.w as f64) * ratio.load(Ordering::SeqCst)) + .ceil() + as i32, + geo.size.h, + ); + stack.push(Some(Rectangle::from_loc_and_size( + (geo.loc.x + left_size.0, geo.loc.y), + (geo.size.w - left_size.0, geo.size.h), + ))); + stack.push(Some(Rectangle::from_loc_and_size( + geo.loc, left_size, + ))); } - } else { - dead_windows.push(window.clone()); } + } else { + stack.push(None); + stack.push(None); + } + } + Data::Stack { active, len } => { + for i in 0..*len { + if i == *active { + stack.push(geo); + } else { + stack.push(None); + } + } + } + Data::Window(window) => { + if window.toplevel().alive() { + if let Some(geo) = geo { + #[allow(irrefutable_let_patterns)] + if let Kind::Xdg(xdg) = &window.toplevel() { + let ret = xdg.with_pending_state(|state| { + state.size = Some( + (geo.size.w - inner * 2, geo.size.h - inner * 2) + .into(), + ); + state.states.set(XdgState::TiledLeft); + state.states.set(XdgState::TiledRight); + state.states.set(XdgState::TiledTop); + state.states.set(XdgState::TiledBottom); + }); + if ret.is_ok() { + xdg.send_configure(); + } + } + let window_geo = window.geometry(); + space.map_window( + &window, + ( + geo.loc.x + inner - window_geo.loc.x, + geo.loc.y + inner - window_geo.loc.y, + ), + false, + ); + } + } else { + dead_windows.push(window.clone()); } } } diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 5c67d170..720378b8 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -59,7 +59,7 @@ impl Workspace { Workspace { idx, space: Space::new(None), - layout: layout::new_default_layout(idx), + layout: layout::new_default_layout(), pending_windows: Vec::new(), } }