tiling: Don't store trees for outputs, but for indices

This commit is contained in:
Victoria Brekenfeld 2022-03-31 13:44:16 +02:00
parent 91863adcf1
commit 3bc58ec02a
7 changed files with 283 additions and 291 deletions

7
Cargo.lock generated
View file

@ -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",

View file

@ -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"

View file

@ -26,7 +26,7 @@ impl Layout for FloatingLayout {
seat: &Seat,
_focus_stack: Box<dyn Iterator<Item = &'a Window> + '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();

View file

@ -87,9 +87,9 @@ pub trait Layout {
}
}
pub fn new_default_layout(idx: u8) -> Box<dyn Layout> {
pub fn new_default_layout() -> Box<dyn Layout> {
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<dyn Layout> {
))
}
fn output_from_seat(seat: &Seat, space: &Space) -> Output {
seat.user_data()
.get::<ActiveOutput>()
.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::<ActiveOutput>()
.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())
}

View file

@ -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<i32, Logical>,
pub initial_ratio: f64,
pub idx: u8,
pub output: Output,
pub ratio: Arc<AtomicF64>,
}
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::<RefCell<OutputInfo>>()
.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(

View file

@ -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<Tree<Data>>,
}
#[derive(Debug)]
pub enum Data {
Fork {
orientation: Orientation,
ratio: f64,
ratio: Arc<AtomicF64>,
},
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<u8, Tree<Data>>,
}
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<dyn Iterator<Item = &'a Window> + 'a>,
focus_stack: Box<dyn Iterator<Item = &'a Window> + '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::<RefCell<OutputInfo>>()
.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::<RefCell<WindowInfo>>().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<dyn Iterator<Item = &'a Window> + 'a>,
) -> Option<Window> {
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::<RefCell<OutputInfo>>()
.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<dyn Iterator<Item = &'a Window> + '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::<RefCell<OutputInfo>>()
.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::<Vec<_>>()
.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::<RefCell<WindowInfo>>() {
let output = &info.borrow().output;
let mut output_info = output
.user_data()
.get::<RefCell<OutputInfo>>()
.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<Tree<Data>>, output: usize) -> &'a mut Tree<Data> {
while trees.len() <= output {
trees.push(Tree::new())
}
&mut trees[output]
}
fn last_active_window<'a>(
tree: &mut Tree<Data>,
mut focus_stack: Box<dyn Iterator<Item = &'a Window> + 'a>,
mut focus_stack: impl Iterator<Item = &'a Window>,
) -> Option<NodeId> {
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<Box<dyn Iterator<Item = &'a Window> + '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::<RefCell<WindowInfo>>().unwrap().borrow_mut() = window_info;
}
}
}
fn unmap_window(&mut self, window: &Window) {
if let Some(info) = window.user_data().get::<RefCell<WindowInfo>>() {
let output = &info.borrow().output;
output
.user_data()
.insert_if_missing(|| RefCell::new(OutputInfo::default()));
let mut output_info = output
.user_data()
.get::<RefCell<OutputInfo>>()
.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<Window> {
fn update_space_positions(
trees: &mut Vec<Tree<Data>>,
space: &mut Space,
gaps: (i32, i32),
) -> Vec<Window> {
let mut dead_windows = Vec::new();
let (outer, inner) = gaps;
for output in space.outputs().cloned().collect::<Vec<_>>().into_iter() {
if let Some(mut info) = output
.user_data()
.get::<RefCell<OutputInfo>>()
.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::<Vec<_>>()
.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());
}
}
}

View file

@ -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(),
}
}