From 72df9d07e6f93791769b45bcc7d3635ee9f3f6d1 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 25 Oct 2023 19:40:26 +0200 Subject: [PATCH] shell: One workspace per output --- src/backend/kms/mod.rs | 1 - src/backend/winit.rs | 1 - src/backend/x11.rs | 1 - src/input/mod.rs | 38 +- src/shell/element/mod.rs | 4 +- src/shell/focus/mod.rs | 26 +- src/shell/focus/target.rs | 5 +- src/shell/grabs/moving.rs | 2 +- src/shell/layout/floating/mod.rs | 274 +-- src/shell/layout/tiling/grabs/resize.rs | 117 +- src/shell/layout/tiling/grabs/swap.rs | 3 +- src/shell/layout/tiling/mod.rs | 2065 ++++++++++------------- src/shell/mod.rs | 747 ++++---- src/shell/workspace.rs | 132 +- src/state.rs | 76 +- src/wayland/handlers/compositor.rs | 2 +- src/wayland/handlers/layer_shell.rs | 5 +- src/wayland/handlers/screencopy.rs | 2 +- src/wayland/handlers/workspace.rs | 16 +- src/wayland/handlers/xdg_shell/mod.rs | 16 +- src/xwayland.rs | 15 +- 21 files changed, 1561 insertions(+), 1987 deletions(-) diff --git a/src/backend/kms/mod.rs b/src/backend/kms/mod.rs index 60eea7ce..598d7242 100644 --- a/src/backend/kms/mod.rs +++ b/src/backend/kms/mod.rs @@ -1507,7 +1507,6 @@ impl KmsState { false }; - shell.refresh_outputs(); if recreated { let sessions = output.pending_buffers().collect::>(); if let Err(err) = self.schedule_render( diff --git a/src/backend/winit.rs b/src/backend/winit.rs index da2cb26d..b82d6fa7 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -349,7 +349,6 @@ impl State { output.change_current_state(Some(mode), None, None, None); layer_map_for_output(output).arrange(); self.common.output_configuration_state.update(); - self.common.shell.refresh_outputs(); render_ping.ping(); } WinitEvent::Refresh => render_ping.ping(), diff --git a/src/backend/x11.rs b/src/backend/x11.rs index 7952eaa7..49dabcd4 100644 --- a/src/backend/x11.rs +++ b/src/backend/x11.rs @@ -448,7 +448,6 @@ pub fn init_backend( output.set_preferred(mode); layer_map_for_output(output).arrange(); state.common.output_configuration_state.update(); - state.common.shell.refresh_outputs(); surface.dirty = true; if !surface.pending { surface.render.ping(); diff --git a/src/input/mod.rs b/src/input/mod.rs index 0c4265b1..cbc1f262 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -350,7 +350,7 @@ impl State { if let Some(old_workspace) = old_w.get_mut(0) { if let Some(new_workspace) = other_w.iter_mut().find(|w| w.handle == new_workspace) { if new_workspace.tiling_layer.windows().next().is_none() { - if let Some(focus) = TilingLayout::move_tree(&mut old_workspace.tiling_layer, &mut new_workspace.tiling_layer, ¤t_output, &new_workspace.handle, &seat, new_workspace.focus_stack.get(&seat).iter(), old_descriptor, &mut data.common.shell.toplevel_info_state) { + if let Some(focus) = TilingLayout::move_tree(&mut old_workspace.tiling_layer, &mut new_workspace.tiling_layer, &new_workspace.handle, &seat, new_workspace.focus_stack.get(&seat).iter(), old_descriptor, &mut data.common.shell.toplevel_info_state) { let seat = seat.clone(); data.common.event_loop_handle.insert_idle(move |state| { Common::set_focus(state, Some(&focus), &seat, None); @@ -1333,16 +1333,15 @@ impl State { } Action::NextOutput => { let current_output = seat.active_output(); - if let Some(next_output) = self + let next_output = self .common .shell - .outputs - .iter() + .outputs() .skip_while(|o| *o != ¤t_output) .skip(1) .next() - .cloned() - { + .cloned(); + if let Some(next_output) = next_output { let idx = self.common.shell.workspaces.active_num(&next_output).1; match self.common.shell.activate(&next_output, idx) { Ok(Some(new_pos)) => { @@ -1369,17 +1368,16 @@ impl State { } Action::PreviousOutput => { let current_output = seat.active_output(); - if let Some(prev_output) = self + let prev_output = self .common .shell - .outputs - .iter() + .outputs() .rev() .skip_while(|o| *o != ¤t_output) .skip(1) .next() - .cloned() - { + .cloned(); + if let Some(prev_output) = prev_output { let idx = self.common.shell.workspaces.active_num(&prev_output).1; match self.common.shell.activate(&prev_output, idx) { Ok(Some(new_pos)) => { @@ -1406,16 +1404,15 @@ impl State { } x @ Action::MoveToNextOutput | x @ Action::SendToNextOutput => { let current_output = seat.active_output(); - if let Some(next_output) = self + let next_output = self .common .shell - .outputs - .iter() + .outputs() .skip_while(|o| *o != ¤t_output) .skip(1) .next() - .cloned() - { + .cloned(); + if let Some(next_output) = next_output { if let Ok(Some(new_pos)) = Shell::move_current_window( self, seat, @@ -1441,17 +1438,16 @@ impl State { } x @ Action::MoveToPreviousOutput | x @ Action::SendToPreviousOutput => { let current_output = seat.active_output(); - if let Some(prev_output) = self + let prev_output = self .common .shell - .outputs - .iter() + .outputs() .rev() .skip_while(|o| *o != ¤t_output) .skip(1) .next() - .cloned() - { + .cloned(); + if let Some(prev_output) = prev_output { if let Ok(Some(new_pos)) = Shell::move_current_window( self, seat, diff --git a/src/shell/element/mod.rs b/src/shell/element/mod.rs index dbaf8f16..361a301a 100644 --- a/src/shell/element/mod.rs +++ b/src/shell/element/mod.rs @@ -492,7 +492,7 @@ impl CosmicMapped { pub fn convert_to_stack<'a>( &mut self, - outputs: impl Iterator)>, + (output, overlap): (&'a Output, Rectangle), ) { match &self.element { CosmicMappedInternal::Window(window) => { @@ -518,7 +518,7 @@ impl CosmicMapped { pub fn convert_to_surface<'a>( &mut self, surface: CosmicSurface, - outputs: impl Iterator)>, + (output, overlap): (&'a Output, Rectangle), ) { let handle = self.loop_handle(); surface.try_force_undecorated(false); diff --git a/src/shell/focus/mod.rs b/src/shell/focus/mod.rs index 07f24deb..bc3f5e6a 100644 --- a/src/shell/focus/mod.rs +++ b/src/shell/focus/mod.rs @@ -154,7 +154,7 @@ impl Shell { return None; } - Some(self.outputs.iter().flat_map(|o| { + Some(self.outputs().flat_map(|o| { let space = self.active_space(o); let stack = space.focus_stack.get(seat); stack.last().cloned() @@ -163,8 +163,9 @@ impl Shell { .flatten() .collect::>(); - for output in self.outputs.iter() { - let workspace = self.workspaces.active_mut(output); + for output in self.outputs().cloned().collect::>().into_iter() { + // TODO: Add self.workspaces.active_workspaces() + let workspace = self.workspaces.active_mut(&output); for focused in focused_windows.iter() { if let CosmicSurface::X11(window) = focused.active_window() { if let Some(xwm) = xwm.as_mut().and_then(|state| state.xwm.as_mut()) { @@ -223,8 +224,8 @@ impl Common { let seats = state.common.seats().cloned().collect::>(); for seat in seats { let output = seat.active_output(); - if !state.common.shell.outputs.contains(&output) { - seat.set_active_output(&state.common.shell.outputs[0]); + if !state.common.shell.outputs().any(|o| o == &output) { + seat.set_active_output(&state.common.shell.outputs().next().unwrap()); continue; } let last_known_focus = ActiveFocus::get(&seat); @@ -248,11 +249,16 @@ impl Common { continue; // Focus is valid } } - KeyboardFocusTarget::Group(WindowGroup { - output: weak_output, - .. - }) => { - if weak_output == output { + KeyboardFocusTarget::Group(WindowGroup { node, .. }) => { + if state + .common + .shell + .workspaces + .active(&output) + .1 + .tiling_layer + .has_node(&node) + { continue; // Focus is valid, } } diff --git a/src/shell/focus/target.rs b/src/shell/focus/target.rs index 72d41ae7..a8663a91 100644 --- a/src/shell/focus/target.rs +++ b/src/shell/focus/target.rs @@ -87,16 +87,13 @@ impl KeyboardFocusTarget { #[derive(Debug, Clone)] pub struct WindowGroup { pub node: NodeId, - pub output: WeakOutput, pub alive: Weak<()>, pub focus_stack: Vec, } impl PartialEq for WindowGroup { fn eq(&self, other: &Self) -> bool { - self.node == other.node - && self.output == other.output - && Weak::ptr_eq(&self.alive, &other.alive) + self.node == other.node && Weak::ptr_eq(&self.alive, &other.alive) } } diff --git a/src/shell/grabs/moving.rs b/src/shell/grabs/moving.rs index e044e4fc..6af828c9 100644 --- a/src/shell/grabs/moving.rs +++ b/src/shell/grabs/moving.rs @@ -229,7 +229,7 @@ impl PointerGrab for MoveGrab { .workspaces .active_mut(&self.cursor_output) .tiling_layer - .cleanup_drag(&self.cursor_output); + .cleanup_drag(); self.cursor_output = current_output.clone(); } diff --git a/src/shell/layout/floating/mod.rs b/src/shell/layout/floating/mod.rs index 8955acfe..7f8452f3 100644 --- a/src/shell/layout/floating/mod.rs +++ b/src/shell/layout/floating/mod.rs @@ -11,7 +11,6 @@ use smithay::{ utils::{Logical, Point, Rectangle, Size}, wayland::seat::WaylandFocus, }; -use std::collections::HashMap; use crate::{ backend::render::{element::AsGlowRenderer, IndicatorShader, Key, Usage}, @@ -28,9 +27,7 @@ use crate::{ }, state::State, utils::prelude::*, - wayland::{ - handlers::xdg_shell::popup::get_popup_toplevel, protocols::toplevel_info::ToplevelInfoState, - }, + wayland::handlers::xdg_shell::popup::get_popup_toplevel, }; mod grabs; @@ -42,67 +39,53 @@ pub struct FloatingLayout { } impl FloatingLayout { - pub fn new() -> FloatingLayout { - Default::default() + pub fn new(output: &Output) -> FloatingLayout { + let mut layout = Self::default(); + layout.space.map_output(output, (0, 0)); + layout } - pub fn map_output(&mut self, output: &Output, location: Point) { - self.space.map_output(output, location) - } + pub fn set_output(&mut self, output: &Output) { + let old_output = self.space.outputs().next().unwrap().clone(); + self.space.unmap_output(&old_output); + self.space.map_output(output, (0, 0)); + + /* + TODO: rescale all positions? (evem rescale windows?) + */ - pub fn unmap_output( - &mut self, - output: &Output, - toplevel_info: &mut ToplevelInfoState, - ) { - let windows = self - .space - .elements_for_output(output) - .cloned() - .collect::>(); - for window in &windows { - for (toplevel, _) in window.windows() { - toplevel_info.toplevel_leave_output(&toplevel, output); - } - } - self.space.unmap_output(output); self.refresh(); - for window in &windows { - for output in self.space.outputs_for_element(&window) { - for (toplevel, _) in window.windows() { - toplevel_info.toplevel_enter_output(&toplevel, &output); - } - } - } } pub fn map( &mut self, mapped: impl Into, - seat: &Seat, - position: impl Into>>, + position: impl Into>>, ) { let mapped = mapped.into(); - let output = seat.active_output(); let position = position.into(); - self.map_internal(mapped, &output, position) + self.map_internal(mapped, position, None) } pub(in crate::shell) fn map_internal( &mut self, mapped: CosmicMapped, - output: &Output, - position: Option>, + position: Option>, + size: Option>, ) { - let mut win_geo = mapped.geometry(); + let mut win_geo = mapped.geometry().as_local(); + let output = self.space.outputs().next().unwrap().clone(); let layers = layer_map_for_output(&output); let geometry = layers.non_exclusive_zone(); mapped.set_bounds(geometry.size); let last_geometry = mapped.last_geometry.lock().unwrap().clone(); - if let Some(size) = last_geometry.map(|g| g.size) { + if let Some(size) = size + .map(SizeExt::as_local) + .or(last_geometry.map(|g| g.size)) + { win_geo.size = size; } else { let (min_size, max_size) = ( @@ -150,18 +133,10 @@ impl FloatingLayout { }); mapped.set_tiled(false); - let offset = output.geometry().loc - - self - .space - .output_geometry(output) - .map(|g| g.loc) - .unwrap_or_default(); - mapped.set_geometry(Rectangle::from_loc_and_size( - position + offset, - win_geo.size, - )); + mapped + .set_geometry(Rectangle::from_loc_and_size(position, win_geo.size).to_global(&output)); mapped.configure(); - self.space.map_element(mapped, position, false); + self.space.map_element(mapped, position.as_logical(), false); } pub fn unmap(&mut self, window: &CosmicMapped) -> bool { @@ -170,10 +145,8 @@ impl FloatingLayout { if !is_maximized { if let Some(location) = self.space.element_location(window) { - *window.last_geometry.lock().unwrap() = Some(Rectangle::from_loc_and_size( - location, - window.geometry().size, - )); + *window.last_geometry.lock().unwrap() = + Some(Rectangle::from_loc_and_size(location, window.geometry().size).as_local()); } } @@ -182,8 +155,8 @@ impl FloatingLayout { was_unmaped } - pub fn element_geometry(&self, elem: &CosmicMapped) -> Option> { - self.space.element_geometry(elem) + pub fn element_geometry(&self, elem: &CosmicMapped) -> Option> { + self.space.element_geometry(elem).map(RectExt::as_local) } pub fn maximize_request(&mut self, window: &CosmicSurface) { @@ -339,13 +312,10 @@ impl FloatingLayout { })); mapped.set_resizing(true); - mapped.set_geometry(Rectangle::from_loc_and_size( - match mapped.active_window() { - CosmicSurface::X11(s) => s.geometry().loc, - _ => (0, 0).into(), - }, - geo.size, - )); + mapped.set_geometry( + geo.as_local() + .to_global(self.space.outputs().next().unwrap()), + ); mapped.configure(); true @@ -503,83 +473,26 @@ impl FloatingLayout { { // TODO what about windows leaving to the top with no headerbar to drag? can that happen? (Probably if the user is moving outputs down) *element.last_geometry.lock().unwrap() = None; - let output = self.space.outputs().next().unwrap().clone(); - self.map_internal(element, &output, None); + self.map_internal(element, None, None); } } - pub fn most_overlapped_output_for_element(&self, elem: &CosmicMapped) -> Option { - let elem_geo = self.space.element_geometry(elem)?; - - if self.space.outputs().nth(1).is_none() { - return self.space.outputs().next().cloned(); - } - - Some( - self.space - .outputs_for_element(elem) - .into_iter() - .max_by_key(|o| { - let output_geo = self.space.output_geometry(o).unwrap(); - if let Some(intersection) = output_geo.intersection(elem_geo) { - intersection.size.w * intersection.size.h - } else { - 0 - } - }) - .unwrap_or(self.space.outputs().next().unwrap().clone()), - ) - } - pub fn merge(&mut self, other: FloatingLayout) { - let mut output_pos_map = HashMap::new(); - for output in self.space.outputs() { - output_pos_map.insert( - output.clone(), - self.space.output_geometry(output).unwrap().loc - - other - .space - .output_geometry(output) - .map(|geo| geo.loc) - .unwrap_or_else(|| (0, 0).into()), - ); - } for element in other.space.elements() { - let mut elem_geo = other.space.element_geometry(element).unwrap(); - let output = other + let elem_loc = other .space - .outputs_for_element(element) - .into_iter() - .filter(|o| self.space.outputs().any(|o2| o == o2)) - .max_by_key(|o| { - let output_geo = other.space.output_geometry(o).unwrap(); - let intersection = output_geo.intersection(elem_geo).unwrap(); - intersection.size.w * intersection.size.h - }) - .unwrap_or(self.space.outputs().next().unwrap().clone()); - elem_geo.loc += output_pos_map - .get(&output) - .copied() - .unwrap_or_else(|| (0, 0).into()); - let offset = output.geometry().loc - - self - .space - .output_geometry(&output) - .map(|g| g.loc) - .unwrap_or_default(); - element.set_geometry(Rectangle::from_loc_and_size( - elem_geo.loc + offset, - elem_geo.size, - )); - self.space.map_element(element.clone(), elem_geo.loc, false); + .element_geometry(element) + .unwrap() + .loc + .as_local(); + self.map_internal(element.clone(), Some(elem_loc), None); } self.refresh(); //fixup any out of bounds elements } - pub fn render_output( + pub fn render( &self, renderer: &mut R, - output: &Output, focused: Option<&CosmicMapped>, mut resize_indicator: Option<(ResizeMode, ResizeIndicator)>, indicator_thickness: u8, @@ -598,68 +511,65 @@ impl FloatingLayout { #[cfg(feature = "debug")] puffin::profile_function!(); + let output = self.space.outputs().next().unwrap(); let output_scale = output.current_scale().fractional_scale(); - let output_geo = self.space.output_geometry(output).unwrap(); let mut window_elements = Vec::new(); let mut popup_elements = Vec::new(); - self.space - .elements_for_output(output) - .rev() - .for_each(|elem| { - let render_location = self.space.element_location(elem).unwrap() - - output_geo.loc - - elem.geometry().loc; - let (w_elements, p_elements) = elem.split_render_elements( - renderer, - render_location.to_physical_precise_round(output_scale), - output_scale.into(), - alpha, - ); + self.space.elements().rev().for_each(|elem| { + let render_location = self.space.element_location(elem).unwrap() - elem.geometry().loc; + let (w_elements, p_elements) = elem.split_render_elements( + renderer, + render_location.to_physical_precise_round(output_scale), + output_scale.into(), + alpha, + ); - if focused == Some(elem) { - let mut indicator_geometry = Rectangle::from_loc_and_size( - self.space.element_location(elem).unwrap() - output_geo.loc, - elem.geometry().size, + if focused == Some(elem) { + let mut indicator_geometry = Rectangle::from_loc_and_size( + self.space.element_location(elem).unwrap(), + elem.geometry().size, + ) + .as_local(); + + if let Some((mode, resize)) = resize_indicator.as_mut() { + indicator_geometry.loc -= (18, 18).into(); + indicator_geometry.size += (36, 36).into(); + resize.resize(indicator_geometry.size.as_logical()); + resize.output_enter(output, Rectangle::default() /* unused */); + window_elements.extend( + resize + .render_elements::>( + renderer, + indicator_geometry + .loc + .as_logical() + .to_physical_precise_round(output_scale), + output_scale.into(), + alpha * mode.alpha().unwrap_or(1.0), + ) + .into_iter() + .map(CosmicMappedRenderElement::Window), ); - - if let Some((mode, resize)) = resize_indicator.as_mut() { - indicator_geometry.loc -= (18, 18).into(); - indicator_geometry.size += (36, 36).into(); - resize.resize(indicator_geometry.size); - resize.output_enter(output, output_geo); - window_elements.extend( - resize - .render_elements::>( - renderer, - indicator_geometry - .loc - .to_physical_precise_round(output_scale), - output_scale.into(), - alpha * mode.alpha().unwrap_or(1.0), - ) - .into_iter() - .map(CosmicMappedRenderElement::Window), - ); - } - - if indicator_thickness > 0 { - let element = IndicatorShader::focus_element( - renderer, - Key::Window(Usage::FocusIndicator, elem.clone()), - indicator_geometry, - indicator_thickness, - output_scale, - alpha, - ); - window_elements.push(element.into()); - } } - window_elements.extend(w_elements); - popup_elements.extend(p_elements); - }); + if indicator_thickness > 0 { + let element = IndicatorShader::focus_element( + renderer, + Key::Window(Usage::FocusIndicator, elem.clone()), + indicator_geometry, + indicator_thickness, + output_scale, + alpha, + ); + window_elements.push(element.into()); + } + } + + window_elements.extend(w_elements); + popup_elements.extend(p_elements); + }); (window_elements, popup_elements) } diff --git a/src/shell/layout/tiling/grabs/resize.rs b/src/shell/layout/tiling/grabs/resize.rs index b78a9837..db9dbfcb 100644 --- a/src/shell/layout/tiling/grabs/resize.rs +++ b/src/shell/layout/tiling/grabs/resize.rs @@ -137,67 +137,64 @@ impl PointerGrab for ResizeForkGrab { if let Some(output) = self.output.upgrade() { let tiling_layer = &mut data.common.shell.active_space_mut(&output).tiling_layer; - if let Some(queue) = tiling_layer.queues.get_mut(&output) { - let tree = &mut queue.trees.back_mut().unwrap().0; - if tree.get(&self.node).is_ok() { - let delta = match self.orientation { - Orientation::Vertical => delta.x, - Orientation::Horizontal => delta.y, - } - .round() as i32; - - // check that we are still alive - let mut iter = tree - .children_ids(&self.node) - .unwrap() - .skip(self.left_up_idx); - let first_elem = iter.next(); - let second_elem = iter.next(); - if first_elem.is_none() || second_elem.is_none() { - return handle.unset_grab(data, event.serial, event.time); - }; - - match tree.get_mut(&self.node).unwrap().data_mut() { - Data::Group { - sizes, orientation, .. - } => { - if sizes[self.left_up_idx] + sizes[self.left_up_idx + 1] - < match orientation { - Orientation::Vertical => 720, - Orientation::Horizontal => 480, - } - { - return; - }; - - let old_size = sizes[self.left_up_idx]; - sizes[self.left_up_idx] = (old_size + delta).max( - if self.orientation == Orientation::Vertical { - 360 - } else { - 240 - }, - ); - let diff = old_size - sizes[self.left_up_idx]; - let next_size = sizes[self.left_up_idx + 1] + diff; - sizes[self.left_up_idx + 1] = - next_size.max(if self.orientation == Orientation::Vertical { - 360 - } else { - 240 - }); - let next_diff = next_size - sizes[self.left_up_idx + 1]; - sizes[self.left_up_idx] += next_diff; - } - _ => unreachable!(), - } - - self.last_loc = event.location; - let blocker = TilingLayout::update_positions(&output, tree, tiling_layer.gaps); - tiling_layer.pending_blockers.extend(blocker); - } else { - handle.unset_grab(data, event.serial, event.time); + let tree = &mut tiling_layer.queue.trees.back_mut().unwrap().0; + if tree.get(&self.node).is_ok() { + let delta = match self.orientation { + Orientation::Vertical => delta.x, + Orientation::Horizontal => delta.y, } + .round() as i32; + + // check that we are still alive + let mut iter = tree + .children_ids(&self.node) + .unwrap() + .skip(self.left_up_idx); + let first_elem = iter.next(); + let second_elem = iter.next(); + if first_elem.is_none() || second_elem.is_none() { + return handle.unset_grab(data, event.serial, event.time); + }; + + match tree.get_mut(&self.node).unwrap().data_mut() { + Data::Group { + sizes, orientation, .. + } => { + if sizes[self.left_up_idx] + sizes[self.left_up_idx + 1] + < match orientation { + Orientation::Vertical => 720, + Orientation::Horizontal => 480, + } + { + return; + }; + + let old_size = sizes[self.left_up_idx]; + sizes[self.left_up_idx] = + (old_size + delta).max(if self.orientation == Orientation::Vertical { + 360 + } else { + 240 + }); + let diff = old_size - sizes[self.left_up_idx]; + let next_size = sizes[self.left_up_idx + 1] + diff; + sizes[self.left_up_idx + 1] = + next_size.max(if self.orientation == Orientation::Vertical { + 360 + } else { + 240 + }); + let next_diff = next_size - sizes[self.left_up_idx + 1]; + sizes[self.left_up_idx] += next_diff; + } + _ => unreachable!(), + } + + self.last_loc = event.location; + let blocker = TilingLayout::update_positions(&output, tree, tiling_layer.gaps); + tiling_layer.pending_blockers.extend(blocker); + } else { + handle.unset_grab(data, event.serial, event.time); } } } diff --git a/src/shell/layout/tiling/grabs/swap.rs b/src/shell/layout/tiling/grabs/swap.rs index 733d921f..25a4748a 100644 --- a/src/shell/layout/tiling/grabs/swap.rs +++ b/src/shell/layout/tiling/grabs/swap.rs @@ -39,8 +39,7 @@ impl KeyboardGrab for SwapWindowGrab { serial: Serial, time: u32, ) { - if self.desc.output.upgrade().is_none() - || !matches!(&data.common.shell.overview_mode, OverviewMode::Started(Trigger::KeyboardSwap(_, d), _) if d == &self.desc) + if !matches!(&data.common.shell.overview_mode, OverviewMode::Started(Trigger::KeyboardSwap(_, d), _) if d == &self.desc) { handle.unset_grab(data, serial, false); return; diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index cdb6de1d..415e7b86 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -48,15 +48,13 @@ use smithay::{ }, desktop::{layer_map_for_output, space::SpaceElement, PopupKind}, input::Seat, - output::{Output, WeakOutput}, + output::Output, reexports::wayland_server::Client, utils::{IsAlive, Logical, Point, Rectangle, Scale, Size}, wayland::{compositor::add_blocker, seat::WaylandFocus}, }; use std::{ - borrow::Borrow, collections::{HashMap, VecDeque}, - hash::Hash, sync::{Arc, Weak}, time::{Duration, Instant}, }; @@ -72,42 +70,9 @@ pub const ANIMATION_DURATION: Duration = Duration::from_millis(200); pub const MOUSE_ANIMATION_DELAY: Duration = Duration::from_millis(150); pub const INITIAL_MOUSE_ANIMATION_DELAY: Duration = Duration::from_millis(500); -#[derive(Debug, Clone)] -struct OutputData { - output: Output, - location: Point, -} - -impl Borrow for OutputData { - fn borrow(&self) -> &Output { - &self.output - } -} - -impl PartialEq for OutputData { - fn eq(&self, other: &Self) -> bool { - self.output == other.output - } -} - -impl Eq for OutputData {} - -impl PartialEq for OutputData { - fn eq(&self, other: &Output) -> bool { - &self.output == other - } -} - -impl Hash for OutputData { - fn hash(&self, state: &mut H) { - self.output.hash(state) - } -} - #[derive(Debug, Clone, PartialEq)] pub struct NodeDesc { pub handle: WorkspaceHandle, - pub output: WeakOutput, pub node: NodeId, pub stack_window: Option, } @@ -152,8 +117,8 @@ impl TreeQueue { #[derive(Debug, Clone)] pub struct TilingLayout { gaps: (i32, i32), - queues: HashMap, - standby_tree: Option>, + output: Output, + queue: TreeQueue, pending_blockers: Vec, placeholder_id: Id, swapping_stack_surface_id: Id, @@ -352,11 +317,18 @@ enum FocusedNodeData { } impl TilingLayout { - pub fn new(gaps: (u8, u8)) -> TilingLayout { + pub fn new(gaps: (u8, u8), output: &Output) -> TilingLayout { TilingLayout { gaps: (gaps.0 as i32, gaps.1 as i32), - queues: HashMap::new(), - standby_tree: None, + queue: TreeQueue { + trees: { + let mut queue = VecDeque::new(); + queue.push_back((Tree::new(), Duration::ZERO, None)); + queue + }, + animation_start: None, + }, + output: output.clone(), pending_blockers: Vec::new(), placeholder_id: Id::new(), swapping_stack_surface_id: Id::new(), @@ -364,117 +336,51 @@ impl TilingLayout { } } - pub fn map_output(&mut self, output: &Output, location: Point) { - if !self.queues.contains_key(output) { - self.queues.insert( - OutputData { - output: output.clone(), - location, - }, - TreeQueue { - trees: { - let mut queue = VecDeque::new(); - queue.push_back(( - self.standby_tree.take().unwrap_or_else(Tree::new), - Duration::ZERO, - None, - )); - queue - }, - animation_start: None, - }, - ); - } else { - let tree = self.queues.remove(output).unwrap(); - self.queues.insert( - OutputData { - output: output.clone(), - location, - }, - tree, - ); - } - } + pub fn set_output(&mut self, output: &Output) { + let mut tree = self.queue.trees.back().unwrap().0.copy_clone(); - pub fn unmap_output( - &mut self, - output: &Output, - toplevel_info: &mut ToplevelInfoState, - ) { - if let Some(mut src) = self.queues.remove(output) { - // Operate on last pending tree & unblock queue - for blocker in src - .trees - .iter_mut() - .flat_map(|(_, _, blocker)| blocker.take()) + for node in tree + .root_node_id() + .and_then(|root_id| tree.traverse_pre_order(root_id).ok()) + .into_iter() + .flatten() + { + if let Data::Mapped { + mapped, + last_geometry: _, + } = node.data() { - self.pending_blockers.push(blocker); + mapped.output_leave(&self.output); + mapped.output_enter(output, mapped.bbox()); } - let (src, _, _) = src.trees.pop_back().expect("No tree in queue"); - - let Some((new_output, dst_queue)) = self.queues.iter_mut().next() else { - self.standby_tree = Some(src); - return; - }; - - let mut dst = dst_queue.trees.back().unwrap().0.copy_clone(); - let orientation = match new_output.output.geometry().size { - x if x.w >= x.h => Orientation::Vertical, - _ => Orientation::Horizontal, - }; - for node in src - .root_node_id() - .and_then(|root_id| src.traverse_pre_order(root_id).ok()) - .into_iter() - .flatten() - { - if let Data::Mapped { - mapped, - last_geometry: _, - } = node.data() - { - for (toplevel, _) in mapped.windows() { - toplevel_info.toplevel_leave_output(&toplevel, output); - toplevel_info.toplevel_enter_output(&toplevel, &new_output.output); - } - mapped.output_leave(output); - mapped.output_enter(&new_output.output, mapped.bbox()); - } - } - TilingLayout::merge_trees(src, &mut dst, orientation); - - let blocker = TilingLayout::update_positions(output, &mut dst, self.gaps); - dst_queue.push_tree(dst, ANIMATION_DURATION, blocker); } + + let blocker = TilingLayout::update_positions(output, &mut tree, self.gaps); + self.queue.push_tree(tree, None, blocker); + self.output = output.clone(); } pub fn map<'a>( &mut self, window: CosmicMapped, - seat: &Seat, focus_stack: impl Iterator + 'a, direction: Option, ) { - let output = seat.active_output(); - window.output_enter(&output, window.bbox()); - window.set_bounds(output.geometry().size); - self.map_internal(window, &output, Some(focus_stack), direction); + window.output_enter(&self.output, window.bbox()); + window.set_bounds(self.output.geometry().size.as_logical()); + self.map_internal(window, Some(focus_stack), direction); } fn map_internal<'a>( &mut self, window: impl Into, - output: &Output, focus_stack: Option + 'a>, direction: Option, ) { - let queue = self.queues.get_mut(output).expect("Output not mapped?"); - let mut tree = queue.trees.back().unwrap().0.copy_clone(); - - TilingLayout::map_to_tree(&mut tree, window, output, focus_stack, direction); - - let blocker = TilingLayout::update_positions(output, &mut tree, self.gaps); - queue.push_tree(tree, ANIMATION_DURATION, blocker); + let mut tree = self.queue.trees.back().unwrap().0.copy_clone(); + TilingLayout::map_to_tree(&mut tree, window, &self.output, focus_stack, direction); + let blocker = TilingLayout::update_positions(&self.output, &mut tree, self.gaps); + self.queue.push_tree(tree, ANIMATION_DURATION, blocker); } fn map_to_tree<'a>( @@ -561,48 +467,40 @@ impl TilingLayout { pub fn replace_window(&mut self, old: &CosmicMapped, new: &CosmicMapped) { let Some(old_id) = old.tiling_node_id.lock().unwrap().clone() else { return }; - let Some((output_data, queue)) = self.queues.iter_mut().find(|(_, queue)| queue.trees.back().unwrap().0.get(&old_id).is_ok()) else { return }; - let mut tree = queue.trees.back().unwrap().0.copy_clone(); + let mut tree = self.queue.trees.back().unwrap().0.copy_clone(); - let node = tree.get_mut(&old_id).unwrap(); - let data = node.data_mut(); - match data { - Data::Mapped { mapped, .. } => { - assert_eq!(mapped, old); - *mapped = new.clone(); - *new.tiling_node_id.lock().unwrap() = Some(old_id); - *old.tiling_node_id.lock().unwrap() = None; + if let Ok(node) = tree.get_mut(&old_id) { + let data = node.data_mut(); + match data { + Data::Mapped { mapped, .. } => { + assert_eq!(mapped, old); + *mapped = new.clone(); + *new.tiling_node_id.lock().unwrap() = Some(old_id); + *old.tiling_node_id.lock().unwrap() = None; + } + _ => unreachable!("Tiling id points to group"), } - _ => unreachable!("Tiling id points to group"), + + old.output_leave(&self.output); + new.output_enter(&self.output, new.bbox()); + + let blocker = TilingLayout::update_positions(&self.output, &mut tree, self.gaps); + self.queue.push_tree(tree, ANIMATION_DURATION, blocker); } - - old.output_leave(&output_data.output); - new.output_enter(&output_data.output, new.bbox()); - - let blocker = TilingLayout::update_positions(&output_data.output, &mut tree, self.gaps); - queue.push_tree(tree, ANIMATION_DURATION, blocker); } pub fn move_tree<'a>( this: &mut Self, other: &mut Self, - other_output: &Output, other_handle: &WorkspaceHandle, seat: &Seat, focus_stack: impl Iterator + 'a, desc: NodeDesc, toplevel_info_state: &mut ToplevelInfoState, ) -> Option { - let this_output = desc.output.upgrade()?; let this_handle = &desc.handle; - let mut this_tree = this - .queues - .get(&this_output) - .map(|queue| queue.trees.back().unwrap().0.copy_clone())?; - let mut other_tree = other - .queues - .get(other_output) - .map(|queue| queue.trees.back().unwrap().0.copy_clone())?; + let mut this_tree = this.queue.trees.back().unwrap().0.copy_clone(); + let mut other_tree = other.queue.trees.back().unwrap().0.copy_clone(); match desc.stack_window { Some(stack_surface) => { @@ -616,12 +514,12 @@ impl TilingLayout { let mapped: CosmicMapped = CosmicWindow::new(stack_surface, this_stack.loop_handle()).into(); - if &this_output != other_output { - mapped.output_leave(&this_output); - mapped.output_enter(other_output, mapped.bbox()); + if this.output != other.output { + mapped.output_leave(&this.output); + mapped.output_enter(&other.output, mapped.bbox()); for (ref surface, _) in mapped.windows() { - toplevel_info_state.toplevel_leave_output(surface, &this_output); - toplevel_info_state.toplevel_enter_output(surface, other_output); + toplevel_info_state.toplevel_leave_output(surface, &this.output); + toplevel_info_state.toplevel_enter_output(surface, &other.output); } } for (ref surface, _) in mapped.windows() { @@ -630,7 +528,7 @@ impl TilingLayout { } mapped.set_tiled(true); - other.map(mapped.clone(), seat, focus_stack, None); + other.map(mapped.clone(), focus_stack, None); return Some(KeyboardFocusTarget::Element(mapped)); } None => { @@ -651,11 +549,7 @@ impl TilingLayout { .unwrap() .current_focus() .and_then(|target| { - TilingLayout::currently_focused_node( - &other_tree, - &other_output, - target, - ) + TilingLayout::currently_focused_node(&other_tree, target) }) .map(|(id, _)| id) .unwrap_or(other_tree.root_node_id().unwrap().clone()); @@ -683,12 +577,12 @@ impl TilingLayout { *parent_id = id.clone(); } if let Data::Mapped { mapped, .. } = other_tree.get_mut(&id).unwrap().data_mut() { - if &this_output != other_output { - mapped.output_leave(&this_output); - mapped.output_enter(other_output, mapped.bbox()); + if this.output != other.output { + mapped.output_leave(&this.output); + mapped.output_enter(&other.output, mapped.bbox()); for (ref surface, _) in mapped.windows() { - toplevel_info_state.toplevel_leave_output(surface, &this_output); - toplevel_info_state.toplevel_enter_output(surface, other_output); + toplevel_info_state.toplevel_leave_output(surface, &this.output); + toplevel_info_state.toplevel_enter_output(surface, &other.output); } } for (ref surface, _) in mapped.windows() { @@ -717,14 +611,14 @@ impl TilingLayout { .insert(child_node, InsertBehavior::UnderNode(&parent_id)) .unwrap(); if let Some(mapped) = maybe_mapped { - if &this_output != other_output { - mapped.output_leave(&this_output); - mapped.output_enter(other_output, mapped.bbox()); + if this.output != other.output { + mapped.output_leave(&this.output); + mapped.output_enter(&other.output, mapped.bbox()); for (ref surface, _) in mapped.windows() { toplevel_info_state - .toplevel_leave_output(surface, &this_output); + .toplevel_leave_output(surface, &this.output); toplevel_info_state - .toplevel_enter_output(surface, other_output); + .toplevel_enter_output(surface, &other.output); } } for (ref surface, _) in mapped.windows() { @@ -742,19 +636,18 @@ impl TilingLayout { } } - let this_queue = this.queues.get_mut(&this_output).unwrap(); let blocker = - TilingLayout::update_positions(&this_output, &mut this_tree, this.gaps); - this_queue.push_tree(this_tree, ANIMATION_DURATION, blocker); + TilingLayout::update_positions(&this.output, &mut this_tree, this.gaps); + this.queue.push_tree(this_tree, ANIMATION_DURATION, blocker); - let other_queue = other.queues.get_mut(other_output).unwrap(); let blocker = - TilingLayout::update_positions(&other_output, &mut other_tree, other.gaps); - other_queue.push_tree(other_tree, ANIMATION_DURATION, blocker); + TilingLayout::update_positions(&other.output, &mut other_tree, other.gaps); + other + .queue + .push_tree(other_tree, ANIMATION_DURATION, blocker); other.node_desc_to_focus(&NodeDesc { handle: other_handle.clone(), - output: other_output.downgrade(), node: id, stack_window: None, }) @@ -769,10 +662,11 @@ impl TilingLayout { other_desc: &NodeDesc, toplevel_info_state: &mut ToplevelInfoState, ) -> Option { - let this_output = this_desc.output.upgrade()?; - let other_output = other_desc.output.upgrade()?; - - if this_output == other_output + let other_output = other + .as_ref() + .map(|other| other.output.clone()) + .unwrap_or(this.output.clone()); + if this.output == other_output && this_desc.handle == other_desc.handle && this_desc.node == other_desc.node && this_desc.stack_window.is_some() != other_desc.stack_window.is_some() @@ -780,24 +674,12 @@ impl TilingLayout { return None; } - let mut this_tree = this - .queues - .get(&this_output) - .map(|queue| queue.trees.back().unwrap().0.copy_clone())?; + let mut this_tree = this.queue.trees.back().unwrap().0.copy_clone(); let mut other_tree = match other.as_mut() { - Some(other) => Some( - other - .queues - .get(&other_output) - .map(|queue| queue.trees.back().unwrap().0.copy_clone())?, - ), + Some(other) => Some(other.queue.trees.back().unwrap().0.copy_clone()), None => { - if this_output != other_output { - Some( - this.queues - .get(&other_output) - .map(|queue| queue.trees.back().unwrap().0.copy_clone())?, - ) + if this.output != other_output { + Some(this.queue.trees.back().unwrap().0.copy_clone()) } else { None } @@ -807,42 +689,13 @@ impl TilingLayout { match (&this_desc.stack_window, &other_desc.stack_window) { (None, None) if other_tree.is_none() => { if this_desc.node != other_desc.node { - match this_tree.swap_nodes( - &this_desc.node, - &other_desc.node, - id_tree::SwapBehavior::TakeChildren, - ) { - Ok(_) => { - if this_output != other_output { - for node in this_tree.traverse_pre_order(&this_desc.node).unwrap() { - if let Data::Mapped { mapped, .. } = node.data() { - mapped.output_leave(&this_output); - mapped.output_enter(&other_output, mapped.bbox()); - for (ref surface, _) in mapped.windows() { - toplevel_info_state - .toplevel_leave_output(surface, &this_output); - toplevel_info_state - .toplevel_enter_output(surface, &other_output); - } - } - } - for node in this_tree.traverse_pre_order(&other_desc.node).unwrap() - { - if let Data::Mapped { mapped, .. } = node.data() { - mapped.output_leave(&other_output); - mapped.output_enter(&this_output, mapped.bbox()); - for (ref surface, _) in mapped.windows() { - toplevel_info_state - .toplevel_leave_output(surface, &other_output); - toplevel_info_state - .toplevel_enter_output(surface, &this_output); - } - } - } - } - } - Err(_) => return None, // Invalid node-descs, nothing to do here - } + this_tree + .swap_nodes( + &this_desc.node, + &other_desc.node, + id_tree::SwapBehavior::TakeChildren, + ) + .unwrap(); } } (None, None) => { @@ -855,12 +708,12 @@ impl TilingLayout { other_node.replace_data(this_node.replace_data(other_data)); if let Data::Mapped { mapped, .. } = this_node.data_mut() { - if this_output != other_output { + if this.output != other_output { mapped.output_leave(&other_output); - mapped.output_enter(&this_output, mapped.bbox()); + mapped.output_enter(&this.output, mapped.bbox()); for (ref surface, _) in mapped.windows() { toplevel_info_state.toplevel_leave_output(surface, &other_output); - toplevel_info_state.toplevel_enter_output(surface, &this_output); + toplevel_info_state.toplevel_enter_output(surface, &this.output); } } for (ref surface, _) in mapped.windows() { @@ -870,11 +723,11 @@ impl TilingLayout { *mapped.tiling_node_id.lock().unwrap() = Some(this_desc.node.clone()); } if let Data::Mapped { mapped, .. } = other_node.data_mut() { - if this_output != other_output { - mapped.output_leave(&this_output); + if this.output != other_output { + mapped.output_leave(&this.output); mapped.output_enter(&other_output, mapped.bbox()); for (ref surface, _) in mapped.windows() { - toplevel_info_state.toplevel_leave_output(surface, &this_output); + toplevel_info_state.toplevel_leave_output(surface, &this.output); toplevel_info_state.toplevel_enter_output(surface, &other_output); } } @@ -909,12 +762,12 @@ impl TilingLayout { .insert(child_node, InsertBehavior::UnderNode(&parent_id)) .unwrap(); if let Some(mapped) = maybe_mapped { - if this_output != other_output { - mapped.output_leave(&this_output); + if this.output != other_output { + mapped.output_leave(&this.output); mapped.output_enter(&other_output, mapped.bbox()); for (ref surface, _) in mapped.windows() { toplevel_info_state - .toplevel_leave_output(surface, &this_output); + .toplevel_leave_output(surface, &this.output); toplevel_info_state .toplevel_enter_output(surface, &other_output); } @@ -959,14 +812,14 @@ impl TilingLayout { .insert(child_node, InsertBehavior::UnderNode(&parent_id)) .unwrap(); if let Some(mapped) = maybe_mapped { - if this_output != other_output { + if this.output != other_output { mapped.output_leave(&other_output); - mapped.output_enter(&this_output, mapped.bbox()); + mapped.output_enter(&this.output, mapped.bbox()); for (ref surface, _) in mapped.windows() { toplevel_info_state .toplevel_leave_output(surface, &other_output); toplevel_info_state - .toplevel_enter_output(surface, &this_output); + .toplevel_enter_output(surface, &this.output); } } for (ref surface, _) in mapped.windows() { @@ -1024,11 +877,11 @@ impl TilingLayout { .position(|s| &s == this_surface) .unwrap(); for (i, surface) in surfaces.into_iter().enumerate() { - if this_output != other_output { + if this.output != other_output { surface.output_leave(&other_output); - surface.output_enter(&this_output, surface.bbox()); + surface.output_enter(&this.output, surface.bbox()); toplevel_info_state.toplevel_leave_output(&surface, &other_output); - toplevel_info_state.toplevel_enter_output(&surface, &this_output); + toplevel_info_state.toplevel_enter_output(&surface, &this.output); } if this_desc.handle != other_desc.handle { toplevel_info_state.toplevel_leave_workspace(&surface, &other_desc.handle); @@ -1036,9 +889,9 @@ impl TilingLayout { } this_stack.add_window(surface, Some(this_idx + i)); } - if this_output != other_output { - this_surface.output_leave(&this_output); - toplevel_info_state.toplevel_leave_output(&this_surface, &this_output); + if this.output != other_output { + this_surface.output_leave(&this.output); + toplevel_info_state.toplevel_leave_output(&this_surface, &this.output); toplevel_info_state.toplevel_enter_output(&this_surface, &other_output); } if this_desc.handle != other_desc.handle { @@ -1051,7 +904,7 @@ impl TilingLayout { CosmicWindow::new(this_surface.clone(), this_stack.loop_handle()).into(); mapped.set_tiled(true); mapped.refresh(); - if this_output != other_output { + if this.output != other_output { mapped.output_enter(&other_output, mapped.bbox()); } @@ -1100,10 +953,10 @@ impl TilingLayout { .position(|s| &s == other_surface) .unwrap(); for (i, surface) in surfaces.into_iter().enumerate() { - if this_output != other_output { - surface.output_leave(&this_output); + if this.output != other_output { + surface.output_leave(&this.output); surface.output_enter(&other_output, surface.bbox()); - toplevel_info_state.toplevel_leave_output(&surface, &this_output); + toplevel_info_state.toplevel_leave_output(&surface, &this.output); toplevel_info_state.toplevel_enter_output(&surface, &other_output); } if this_desc.handle != other_desc.handle { @@ -1112,10 +965,10 @@ impl TilingLayout { } other_stack.add_window(surface, Some(other_idx + i)); } - if this_output != other_output { + if this.output != other_output { other_surface.output_leave(&other_output); toplevel_info_state.toplevel_leave_output(&other_surface, &other_output); - toplevel_info_state.toplevel_enter_output(&other_surface, &this_output); + toplevel_info_state.toplevel_enter_output(&other_surface, &this.output); } if this_desc.handle != other_desc.handle { toplevel_info_state @@ -1128,8 +981,8 @@ impl TilingLayout { CosmicWindow::new(other_surface.clone(), other_stack.loop_handle()).into(); mapped.set_tiled(true); mapped.refresh(); - if this_output != other_output { - mapped.output_enter(&this_output, mapped.bbox()); + if this.output != other_output { + mapped.output_enter(&this.output, mapped.bbox()); } *mapped.tiling_node_id.lock().unwrap() = Some(this_desc.node.clone()); @@ -1181,11 +1034,11 @@ impl TilingLayout { this_stack.remove_window(&this_surface); other_stack.add_window(this_surface.clone(), Some(other_idx)); - if this_output != other_output { - toplevel_info_state.toplevel_leave_output(&this_surface, &this_output); + if this.output != other_output { + toplevel_info_state.toplevel_leave_output(&this_surface, &this.output); toplevel_info_state.toplevel_leave_output(&other_surface, &other_output); toplevel_info_state.toplevel_enter_output(&this_surface, &other_output); - toplevel_info_state.toplevel_enter_output(&other_surface, &this_output); + toplevel_info_state.toplevel_enter_output(&other_surface, &this.output); } if this_desc.handle != other_desc.handle { toplevel_info_state.toplevel_leave_workspace(&this_surface, &this_desc.handle); @@ -1210,16 +1063,15 @@ impl TilingLayout { } } - let this_queue = this.queues.get_mut(&this_output).unwrap(); - let blocker = TilingLayout::update_positions(&this_output, &mut this_tree, this.gaps); - this_queue.push_tree(this_tree, ANIMATION_DURATION, blocker); + let blocker = TilingLayout::update_positions(&this.output, &mut this_tree, this.gaps); + this.queue.push_tree(this_tree, ANIMATION_DURATION, blocker); let has_other_tree = other_tree.is_some(); if let Some(mut other_tree) = other_tree { let (other_queue, gaps) = if let Some(other) = other.as_mut() { - (other.queues.get_mut(&other_output).unwrap(), other.gaps) + (&mut other.queue, other.gaps) } else { - (this.queues.get_mut(&other_output).unwrap(), this.gaps) + (&mut this.queue, this.gaps) }; let blocker = TilingLayout::update_positions(&other_output, &mut other_tree, gaps); other_queue.push_tree(other_tree, ANIMATION_DURATION, blocker); @@ -1236,15 +1088,12 @@ impl TilingLayout { } pub fn node_desc_to_focus(&self, desc: &NodeDesc) -> Option { - let output = desc.output.upgrade()?; - let queue = self.queues.get(&output)?; - let tree = &queue.trees.back().unwrap().0; + let tree = &self.queue.trees.back().unwrap().0; let data = tree.get(&desc.node).ok()?.data(); match data { Data::Mapped { mapped, .. } => Some(KeyboardFocusTarget::Element(mapped.clone())), Data::Group { alive, .. } => Some(KeyboardFocusTarget::Group(WindowGroup { node: desc.node.clone(), - output: desc.output.clone(), alive: Arc::downgrade(alive), focus_stack: tree .children_ids(&desc.node) @@ -1256,60 +1105,26 @@ impl TilingLayout { } } - pub fn tree_for_output(&self, output: &Output) -> Option<&Tree> { - self.queues - .get(output) - .and_then(|queue| queue.trees.back()) - .map(|tree| &tree.0) + pub fn tree(&self) -> &Tree { + &self.queue.trees.back().unwrap().0 } - pub fn unmap(&mut self, window: &CosmicMapped) -> Option { - let output = { - let node_id = window.tiling_node_id.lock().unwrap().clone()?; - self.queues - .iter() - .find(|(_, queue)| { - queue - .trees - .back() - .unwrap() - .0 - .get(&node_id) - .map(|node| node.data().is_mapped(Some(window))) - .unwrap_or(false) - }) - .map(|(o, _)| o.output.clone())? - }; - - self.unmap_window_internal(window); - - window.output_leave(&output); - window.set_tiled(false); - Some(output) + pub fn unmap(&mut self, window: &CosmicMapped) -> bool { + if self.unmap_window_internal(window) { + window.output_leave(&self.output); + window.set_tiled(false); + *window.tiling_node_id.lock().unwrap() = None; + true + } else { + false + } } - pub fn unmap_as_placeholder(&mut self, window: &CosmicMapped) -> Option<(Output, NodeId)> { + pub fn unmap_as_placeholder(&mut self, window: &CosmicMapped) -> Option { let node_id = window.tiling_node_id.lock().unwrap().clone()?; - let output = { - self.queues - .iter() - .find(|(_, queue)| { - queue - .trees - .back() - .unwrap() - .0 - .get(&node_id) - .map(|node| node.data().is_mapped(Some(window))) - .unwrap_or(false) - }) - .map(|(o, _)| o.output.clone())? - }; let data = self - .queues - .get_mut(&output) - .unwrap() + .queue .trees .back_mut() .unwrap() @@ -1322,28 +1137,35 @@ impl TilingLayout { initial_placeholder: true, }; - window.output_leave(&output); + window.output_leave(&self.output); window.set_tiled(false); - Some((output, node_id)) + Some(node_id) } - fn unmap_window_internal(&mut self, mapped: &CosmicMapped) { + fn unmap_window_internal(&mut self, mapped: &CosmicMapped) -> bool { let tiling_node_id = mapped.tiling_node_id.lock().unwrap().as_ref().cloned(); if let Some(node_id) = tiling_node_id { - if let Some((output, queue)) = self.queues.iter_mut().find(|(_, queue)| { - let tree = &queue.trees.back().unwrap().0; - tree.get(&node_id) - .map(|node| node.data().is_mapped(Some(mapped))) - .unwrap_or(false) - }) { - let mut tree = queue.trees.back().unwrap().0.copy_clone(); + if self + .queue + .trees + .back() + .unwrap() + .0 + .get(&node_id) + .map(|node| node.data().is_mapped(Some(mapped))) + .unwrap_or(false) + { + let mut tree = self.queue.trees.back().unwrap().0.copy_clone(); TilingLayout::unmap_internal(&mut tree, &node_id); - let blocker = TilingLayout::update_positions(&output.output, &mut tree, self.gaps); - queue.push_tree(tree, ANIMATION_DURATION, blocker); + let blocker = TilingLayout::update_positions(&self.output, &mut tree, self.gaps); + self.queue.push_tree(tree, ANIMATION_DURATION, blocker); + + return true; } } + false } fn unmap_internal(tree: &mut Tree, node: &NodeId) { @@ -1402,19 +1224,16 @@ impl TilingLayout { } // TODO: Move would needs this to be accurate during animations - pub fn element_geometry(&self, elem: &CosmicMapped) -> Option> { + pub fn element_geometry(&self, elem: &CosmicMapped) -> Option> { if let Some(id) = elem.tiling_node_id.lock().unwrap().as_ref() { - if let Some(output) = self.output_for_element(elem) { - let (output_data, queue) = self.queues.get_key_value(output).unwrap(); - let node = queue.trees.back().unwrap().0.get(id).ok()?; - let data = node.data(); - assert!(data.is_mapped(Some(elem))); - let mut geo = *data.geometry(); - geo.loc += output_data.location; - return Some(geo); - } + let node = self.queue.trees.back().unwrap().0.get(id).ok()?; + let data = node.data(); + assert!(data.is_mapped(Some(elem))); + let geo = *data.geometry(); + Some(geo) + } else { + None } - None } pub fn move_current_node<'a>( @@ -1422,15 +1241,13 @@ impl TilingLayout { direction: Direction, seat: &Seat, ) -> MoveResult { - let output = seat.active_output(); - let queue = self.queues.get_mut(&output).unwrap(); - let mut tree = queue.trees.back().unwrap().0.copy_clone(); + let mut tree = self.queue.trees.back().unwrap().0.copy_clone(); let Some(target) = seat.get_keyboard().unwrap().current_focus() else { return MoveResult::None; }; let Some((node_id, data)) = - TilingLayout::currently_focused_node(&mut tree, &seat.active_output(), target) + TilingLayout::currently_focused_node(&mut tree, target) else { return MoveResult::None; }; @@ -1441,7 +1258,7 @@ impl TilingLayout { StackMoveResult::Handled => return MoveResult::Done, StackMoveResult::MoveOut(surface, loop_handle) => { let mapped: CosmicMapped = CosmicWindow::new(surface, loop_handle).into(); - mapped.output_enter(&output, mapped.bbox()); + mapped.output_enter(&self.output, mapped.bbox()); let orientation = match direction { Direction::Left | Direction::Right => Orientation::Vertical, Direction::Up | Direction::Down => Orientation::Horizontal, @@ -1463,8 +1280,9 @@ impl TilingLayout { .unwrap(); *mapped.tiling_node_id.lock().unwrap() = Some(new_id); - let blocker = TilingLayout::update_positions(&output, &mut tree, self.gaps); - queue.push_tree(tree, ANIMATION_DURATION, blocker); + let blocker = + TilingLayout::update_positions(&self.output, &mut tree, self.gaps); + self.queue.push_tree(tree, ANIMATION_DURATION, blocker); return MoveResult::ShiftFocus(mapped.into()); } StackMoveResult::Default => {} // continue normally @@ -1479,7 +1297,6 @@ impl TilingLayout { FocusedNodeData::Group(focus_stack, alive) => MoveResult::MoveFurther( WindowGroup { node: node_id, - output: output.downgrade(), alive, focus_stack, } @@ -1540,8 +1357,8 @@ impl TilingLayout { .data_mut() .remove_window(og_idx); - let blocker = TilingLayout::update_positions(&output, &mut tree, self.gaps); - queue.push_tree(tree, ANIMATION_DURATION, blocker); + let blocker = TilingLayout::update_positions(&self.output, &mut tree, self.gaps); + self.queue.push_tree(tree, ANIMATION_DURATION, blocker); return MoveResult::Done; } @@ -1566,8 +1383,8 @@ impl TilingLayout { .data_mut() .remove_window(og_idx); - let blocker = TilingLayout::update_positions(&output, &mut tree, self.gaps); - queue.push_tree(tree, ANIMATION_DURATION, blocker); + let blocker = TilingLayout::update_positions(&self.output, &mut tree, self.gaps); + self.queue.push_tree(tree, ANIMATION_DURATION, blocker); return MoveResult::Done; } @@ -1722,8 +1539,8 @@ impl TilingLayout { MoveResult::Done }; - let blocker = TilingLayout::update_positions(&output, &mut tree, self.gaps); - queue.push_tree(tree, ANIMATION_DURATION, blocker); + let blocker = TilingLayout::update_positions(&self.output, &mut tree, self.gaps); + self.queue.push_tree(tree, ANIMATION_DURATION, blocker); return result; } @@ -1737,7 +1554,6 @@ impl TilingLayout { FocusedNodeData::Group(focus_stack, alive) => MoveResult::MoveFurther( WindowGroup { node: node_id, - output: output.downgrade(), alive, focus_stack, } @@ -1753,14 +1569,13 @@ impl TilingLayout { focus_stack: impl Iterator + 'a, swap_desc: Option, ) -> FocusResult { - let output = seat.active_output(); - let tree = &self.queues.get(&output).unwrap().trees.back().unwrap().0; + let tree = &self.queue.trees.back().unwrap().0; let Some(target) = seat.get_keyboard().unwrap().current_focus() else { return FocusResult::None; }; let Some(focused) = - TilingLayout::currently_focused_node(tree, &seat.active_output(), target).or_else( + TilingLayout::currently_focused_node(tree, target).or_else( || { TilingLayout::last_active_window(tree, focus_stack) .map(|(id, mapped)| (id, FocusedNodeData::Window(mapped))) @@ -1814,7 +1629,6 @@ impl TilingLayout { Data::Group { alive, .. } => FocusResult::Some( WindowGroup { node: id, - output: output.downgrade(), alive: Arc::downgrade(alive), focus_stack: stack, } @@ -1849,7 +1663,6 @@ impl TilingLayout { return FocusResult::Some( WindowGroup { node: group.clone(), - output: output.downgrade(), alive: match group_data { &Data::Group { ref alive, .. } => Arc::downgrade(alive), _ => unreachable!(), @@ -1908,7 +1721,6 @@ impl TilingLayout { Data::Group { alive, .. } => { FocusResult::Some(KeyboardFocusTarget::Group(WindowGroup { node: replacement_id.clone(), - output: output.downgrade(), alive: Arc::downgrade(&alive), focus_stack: tree .children_ids(replacement_id) @@ -2024,18 +1836,12 @@ impl TilingLayout { new_orientation: Option, seat: &Seat, ) { - let output = seat.active_output(); - let Some(queue) = self.queues.get_mut(&output) else { - return; - }; - let mut tree = queue.trees.back().unwrap().0.copy_clone(); - let Some(target) = seat.get_keyboard().unwrap().current_focus() else { return; }; - if let Some((last_active, _)) = - TilingLayout::currently_focused_node(&tree, &seat.active_output(), target) - { + + let mut tree = self.queue.trees.back().unwrap().0.copy_clone(); + if let Some((last_active, _)) = TilingLayout::currently_focused_node(&tree, target) { if let Some(group) = tree.get(&last_active).unwrap().parent().cloned() { if let &mut Data::Group { ref mut orientation, @@ -2065,25 +1871,22 @@ impl TilingLayout { *orientation = new_orientation; - let blocker = TilingLayout::update_positions(&output, &mut tree, self.gaps); - queue.push_tree(tree, ANIMATION_DURATION, blocker); + let blocker = + TilingLayout::update_positions(&self.output, &mut tree, self.gaps); + self.queue.push_tree(tree, ANIMATION_DURATION, blocker); } } } } pub fn toggle_stacking<'a>(&mut self, seat: &Seat, mut focus_stack: FocusStackMut) { - let output = seat.active_output(); - let Some(queue) = self.queues.get_mut(&output) else { - return; - }; - let mut tree = queue.trees.back().unwrap().0.copy_clone(); - let Some(target) = seat.get_keyboard().unwrap().current_focus() else { return; }; + + let mut tree = self.queue.trees.back().unwrap().0.copy_clone(); if let Some((last_active, last_active_data)) = - TilingLayout::currently_focused_node(&tree, &seat.active_output(), target) + TilingLayout::currently_focused_node(&tree, target) { match last_active_data { FocusedNodeData::Window(mapped) => { @@ -2091,7 +1894,7 @@ impl TilingLayout { // if it is just a window match tree.get_mut(&last_active).unwrap().data_mut() { Data::Mapped { mapped, .. } => { - mapped.convert_to_stack(std::iter::once((&output, mapped.bbox()))); + mapped.convert_to_stack((&self.output, mapped.bbox())); focus_stack.append(&mapped); } _ => unreachable!(), @@ -2104,10 +1907,7 @@ impl TilingLayout { let handle = match tree.get_mut(&last_active).unwrap().data_mut() { Data::Mapped { mapped, .. } => { let handle = mapped.loop_handle(); - mapped.convert_to_surface( - first, - std::iter::once((&output, mapped.bbox())), - ); + mapped.convert_to_surface(first, (&self.output, mapped.bbox())); focus_stack.append(&mapped); handle } @@ -2120,13 +1920,17 @@ impl TilingLayout { other.set_tiled(false); let window = CosmicMapped::from(CosmicWindow::new(other, handle.clone())); - window.output_enter(&output, window.bbox()); - window.set_bounds(output.geometry().size); + window.output_enter(&self.output, window.bbox()); + + { + let layer_map = layer_map_for_output(&self.output); + window.set_bounds(layer_map.non_exclusive_zone().size); + } TilingLayout::map_to_tree( &mut tree, window, - &output, + &self.output, Some(focus_stack.iter()), None, ) @@ -2171,8 +1975,8 @@ impl TilingLayout { let data = tree.get_mut(&last_active).unwrap().data_mut(); let geo = *data.geometry(); - stack.set_geometry(geo); - stack.output_enter(&output, stack.bbox()); + //stack.set_geometry(geo.as_local()); + stack.output_enter(&self.output, stack.bbox()); stack.set_activate(true); stack.active().send_configure(); stack.refresh(); @@ -2188,18 +1992,15 @@ impl TilingLayout { } } - let blocker = TilingLayout::update_positions(&output, &mut tree, self.gaps); - queue.push_tree(tree, ANIMATION_DURATION, blocker); + let blocker = TilingLayout::update_positions(&self.output, &mut tree, self.gaps); + self.queue.push_tree(tree, ANIMATION_DURATION, blocker); } } - pub fn recalculate(&mut self, output: &Output) { - let Some(queue) = self.queues.get_mut(output) else { - return; - }; - let mut tree = queue.trees.back().unwrap().0.copy_clone(); - let blocker = TilingLayout::update_positions(&output, &mut tree, self.gaps); - queue.push_tree(tree, ANIMATION_DURATION, blocker); + pub fn recalculate(&mut self) { + let mut tree = self.queue.trees.back().unwrap().0.copy_clone(); + let blocker = TilingLayout::update_positions(&self.output, &mut tree, self.gaps); + self.queue.push_tree(tree, ANIMATION_DURATION, blocker); } pub fn refresh(&mut self) { @@ -2221,9 +2022,7 @@ impl TilingLayout { } pub fn animations_going(&self) -> bool { - self.queues - .values() - .any(|queue| queue.animation_start.is_some()) + self.queue.animation_start.is_some() } pub fn update_animation_state(&mut self) -> HashMap { @@ -2232,67 +2031,67 @@ impl TilingLayout { clients.extend(blocker.signal_ready()); } - for queue in self.queues.values_mut() { - if let Some(start) = queue.animation_start { - let duration_since_start = Instant::now().duration_since(start); - if duration_since_start - >= queue - .trees - .get(1) - .expect("Animation going without second tree?") - .1 - { - let _ = queue.animation_start.take(); - let _ = queue.trees.pop_front(); - let _ = queue.trees.front_mut().unwrap().2.take(); - } else { - continue; - } - } - - let ready_trees = queue - .trees - .iter() - .skip(1) - .take_while(|(_, _, blocker)| { - blocker - .as_ref() - .map(|blocker| blocker.is_ready() && blocker.is_signaled()) - .unwrap_or(true) - }) - .count(); - - // merge - let other_duration = if ready_trees > 1 { - queue + if let Some(start) = self.queue.animation_start { + let duration_since_start = Instant::now().duration_since(start); + if duration_since_start + >= self + .queue .trees - .drain(1..ready_trees) - .fold(None, |res, (_, duration, blocker)| { - if let Some(blocker) = blocker { - clients.extend(blocker.signal_ready()); - } - Some( - res.map(|old_duration: Duration| old_duration.max(duration)) - .unwrap_or(duration), - ) - }) + .get(1) + .expect("Animation going without second tree?") + .1 + { + let _ = self.queue.animation_start.take(); + let _ = self.queue.trees.pop_front(); + let _ = self.queue.trees.front_mut().unwrap().2.take(); } else { - None - }; - - // start - if ready_trees > 0 { - let (_, duration, blocker) = queue.trees.get_mut(1).unwrap(); - *duration = other_duration - .map(|other| other.max(*duration)) - .unwrap_or(*duration); - if let Some(blocker) = blocker { - clients.extend(blocker.signal_ready()); - } - queue.animation_start = Some(Instant::now()); + return clients; } } + let ready_trees = self + .queue + .trees + .iter() + .skip(1) + .take_while(|(_, _, blocker)| { + blocker + .as_ref() + .map(|blocker| blocker.is_ready() && blocker.is_signaled()) + .unwrap_or(true) + }) + .count(); + + // merge + let other_duration = if ready_trees > 1 { + self.queue + .trees + .drain(1..ready_trees) + .fold(None, |res, (_, duration, blocker)| { + if let Some(blocker) = blocker { + clients.extend(blocker.signal_ready()); + } + Some( + res.map(|old_duration: Duration| old_duration.max(duration)) + .unwrap_or(duration), + ) + }) + } else { + None + }; + + // start + if ready_trees > 0 { + let (_, duration, blocker) = self.queue.trees.get_mut(1).unwrap(); + *duration = other_duration + .map(|other| other.max(*duration)) + .unwrap_or(*duration); + if let Some(blocker) = blocker { + clients.extend(blocker.signal_ready()); + } + self.queue.animation_start = Some(Instant::now()); + } + clients } @@ -2337,28 +2136,22 @@ impl TilingLayout { edges: ResizeEdge, amount: i32, ) -> bool { - let Some((output, mut node_id)) = self.queues.iter().find_map(|(output, queue)| { - let tree = &queue.trees.back().unwrap().0; - let root_id = tree.root_node_id()?; - let id = - match TilingLayout::currently_focused_node(tree, &output.output, focused.clone()) { - Some((_id, FocusedNodeData::Window(mapped))) => - // we need to make sure the id belongs to this tree.. - { - tree.traverse_pre_order_ids(root_id) - .unwrap() - .find(|id| tree.get(id).unwrap().data().is_mapped(Some(&mapped))) - } - Some((id, FocusedNodeData::Group(_, _))) => Some(id), // in this case the output was already matched, so the id is to be trusted - _ => None, - }; - id.map(|id| (output.output.clone(), id)) - }) else { - return false; - }; - - let queue = self.queues.get_mut(&output).unwrap(); - let mut tree = queue.trees.back().unwrap().0.copy_clone(); + let mut tree = self.queue.trees.back().unwrap().0.copy_clone(); + let Some(root_id) = tree.root_node_id() else { return false }; + let Some(mut node_id) = + (match TilingLayout::currently_focused_node(&tree, focused.clone()) { + Some((_id, FocusedNodeData::Window(mapped))) => + // we need to make sure the id belongs to this tree.. + { + tree.traverse_pre_order_ids(root_id) + .unwrap() + .find(|id| tree.get(id).unwrap().data().is_mapped(Some(&mapped))) + } + Some((id, FocusedNodeData::Group(_, _))) => Some(id), // in this case the workspace handle was already matched, so the id is to be trusted + _ => None, + }) else { + return false + }; while let Some(group_id) = tree.get(&node_id).unwrap().parent().cloned() { let orientation = tree.get(&group_id).unwrap().data().orientation(); @@ -2421,8 +2214,8 @@ impl TilingLayout { } _ => unreachable!(), } - let blocker = TilingLayout::update_positions(&output, &mut tree, self.gaps); - queue.push_tree(tree, Duration::ZERO, blocker); + let blocker = TilingLayout::update_positions(&self.output, &mut tree, self.gaps); + self.queue.push_tree(tree, None, blocker); return true; } @@ -2430,7 +2223,7 @@ impl TilingLayout { true } - pub fn stacking_indicator(&self) -> Option> { + pub fn stacking_indicator(&self) -> Option> { if let Some(TargetZone::WindowStack(_, geo)) = self.last_overview_hover.as_ref().map(|(_, zone)| zone) { @@ -2440,16 +2233,8 @@ impl TilingLayout { } } - pub fn cleanup_drag(&mut self, output: &Output) { - let mut queue = self.queues.get_mut(output); - let mut owned_tree = None; - let mut tree = if let Some(queue) = queue.as_mut() { - owned_tree = queue.trees.back().map(|x| x.0.copy_clone()); - owned_tree.as_mut() - } else { - self.standby_tree.as_mut() - } - .unwrap(); + pub fn cleanup_drag(&mut self) { + let mut tree = self.queue.trees.back().unwrap().0.copy_clone(); if let Some(root) = tree.root_node_id() { for id in tree @@ -2467,35 +2252,19 @@ impl TilingLayout { } } - if let Some(mut tree) = owned_tree { - let blocker = TilingLayout::update_positions(output, &mut tree, self.gaps); - queue.unwrap().push_tree(tree, ANIMATION_DURATION, blocker); - } + let blocker = TilingLayout::update_positions(&self.output, &mut tree, self.gaps); + self.queue.push_tree(tree, ANIMATION_DURATION, blocker); } } - pub fn drop_window( - &mut self, - window: CosmicMapped, - output: &Output, - _cursor_pos: Point, - ) -> (CosmicMapped, Point) { - let mut queue = if self.queues.contains_key(output) { - self.queues.get_mut(output) - } else { - self.queues.values_mut().next() - }; - let mut owned_tree = None; - let mut tree = if let Some(queue) = queue.as_mut() { - owned_tree = queue.trees.back().map(|x| x.0.copy_clone()); - owned_tree.as_mut() - } else { - self.standby_tree.as_mut() - } - .unwrap(); + pub fn drop_window(&mut self, window: CosmicMapped) -> (CosmicMapped, Point) { + let mut tree = self.queue.trees.back().unwrap().0.copy_clone(); - window.output_enter(&output, window.bbox()); - window.set_bounds(output.geometry().size); + window.output_enter(&self.output, window.bbox()); + { + let layer_map = layer_map_for_output(&self.output); + window.set_bounds(layer_map.non_exclusive_zone().size); + } let mapped = match self.last_overview_hover.as_ref().map(|x| &x.1) { Some(TargetZone::GroupEdge(group_id, direction)) if tree.get(&group_id).is_ok() => { @@ -2589,7 +2358,7 @@ impl TilingLayout { Some(TargetZone::WindowStack(window_id, _)) if tree.get(&window_id).is_ok() => { match tree.get_mut(window_id).unwrap().data_mut() { Data::Mapped { mapped, .. } => { - mapped.convert_to_stack(std::iter::once((output, mapped.bbox()))); + mapped.convert_to_stack((&self.output, mapped.bbox())); let Some(stack) = mapped.stack_ref_mut() else { unreachable!() }; @@ -2605,7 +2374,7 @@ impl TilingLayout { TilingLayout::map_to_tree( &mut tree, window.clone(), - output, + &self.output, Option::>::None, None, ); @@ -2630,12 +2399,10 @@ impl TilingLayout { } } - if let Some(mut tree) = owned_tree { - let blocker = TilingLayout::update_positions(output, &mut tree, self.gaps); - queue.unwrap().push_tree(tree, ANIMATION_DURATION, blocker); - } + let blocker = TilingLayout::update_positions(&self.output, &mut tree, self.gaps); + self.queue.push_tree(tree, ANIMATION_DURATION, blocker); - let location = output.geometry().loc + self.element_geometry(&mapped).unwrap().loc; + let location = self.element_geometry(&mapped).unwrap().loc; (mapped, location) } @@ -2653,7 +2420,6 @@ impl TilingLayout { fn currently_focused_node( tree: &Tree, - output: &Output, mut target: KeyboardFocusTarget, ) -> Option<(NodeId, FocusedNodeData)> { // if the focus is currently on a popup, treat it's toplevel as the target @@ -2689,14 +2455,12 @@ impl TilingLayout { } } KeyboardFocusTarget::Group(window_group) => { - if window_group.output == *output { - let node = tree.get(&window_group.node).ok()?; - if node.data().is_group() { - return Some(( - window_group.node, - FocusedNodeData::Group(window_group.focus_stack, window_group.alive), - )); - } + let node = tree.get(&window_group.node).ok()?; + if node.data().is_group() { + return Some(( + window_group.node, + FocusedNodeData::Group(window_group.focus_stack, window_group.alive), + )); } } _ => {} @@ -2933,15 +2697,10 @@ impl TilingLayout { Data::Mapped { mapped, .. } => { if !(mapped.is_fullscreen(true) || mapped.is_maximized(true)) { mapped.set_tiled(true); - let internal_geometry = Rectangle::from_loc_and_size( - geo.loc + output.geometry().loc, - geo.size, - ); - if mapped.geometry() != internal_geometry { - mapped.set_geometry(internal_geometry); - if let Some(serial) = mapped.configure() { - configures.push((mapped.active_window(), serial)); - } + let internal_geometry = geo.to_global(&output); + mapped.set_geometry(internal_geometry); + if let Some(serial) = mapped.configure() { + configures.push((mapped.active_window(), serial)); } } } @@ -2966,606 +2725,605 @@ impl TilingLayout { pub fn element_under( &mut self, - location: Point, + location: Point, overview: OverviewMode, - ) -> Option<(PointerFocusTarget, Point)> { + ) -> Option<(PointerFocusTarget, Point)> { let last_overview_hover = &mut self.last_overview_hover; let placeholder_id = &self.placeholder_id; let gaps = &self.gaps; - self.queues.iter_mut().find_map(|(output_data, queue)| { - let tree = &queue.trees.back().unwrap().0; - let root = tree.root_node_id()?; - let location = (location - output_data.location.to_f64()).to_i32_round(); + let tree = &self.queue.trees.back().unwrap().0; + let root = tree.root_node_id()?; + let location = location.to_i32_round(); - { - let output_geo = - Rectangle::from_loc_and_size((0, 0), output_data.output.geometry().size); - if !output_geo.contains(location) { - return None; - } + { + let output_geo = + Rectangle::from_loc_and_size((0, 0), self.output.geometry().size.as_logical()) + .as_local(); + if !output_geo.contains(location) { + return None; } + } - if !matches!(overview, OverviewMode::Started(_, _)) { - last_overview_hover.take(); - } + if !matches!(overview, OverviewMode::Started(_, _)) { + last_overview_hover.take(); + } - if matches!(overview, OverviewMode::None) { - let mut result = None; - let mut lookup = Some(root.clone()); - while let Some(node) = lookup { - let data = tree.get(&node).unwrap().data(); - if data.geometry().contains(location) { - result = Some(node.clone()); - } - - lookup = None; - if result.is_some() && data.is_group() { - for child_id in tree.children_ids(&node).unwrap() { - if tree - .get(child_id) - .unwrap() - .data() - .geometry() - .contains(location) - { - lookup = Some(child_id.clone()); - break; - } - } - } + if matches!(overview, OverviewMode::None) { + let mut result = None; + let mut lookup = Some(root.clone()); + while let Some(node) = lookup { + let data = tree.get(&node).unwrap().data(); + if data.geometry().contains(location) { + result = Some(node.clone()); } - match result.map(|id| (id.clone(), tree.get(&id).unwrap().data().clone())) { - Some(( - _, - Data::Mapped { - mapped, - last_geometry, - }, - )) => { - let test_point = location.to_f64() - last_geometry.loc.to_f64() - + mapped.geometry().loc.to_f64(); - mapped.is_in_input_region(&test_point).then(|| { - ( - mapped.clone().into(), - last_geometry.loc + output_data.location - mapped.geometry().loc, - ) - }) - } - Some(( - id, - Data::Group { - orientation, - last_geometry, - .. - }, - )) => { - let idx = tree - .children(&id) + lookup = None; + if result.is_some() && data.is_group() { + for child_id in tree.children_ids(&node).unwrap() { + if tree + .get(child_id) .unwrap() - .position(|node| { - let data = node.data(); - match orientation { - Orientation::Vertical => location.x < data.geometry().loc.x, - Orientation::Horizontal => location.y < data.geometry().loc.y, - } - }) - .and_then(|x| x.checked_sub(1))?; - Some(( - ResizeForkTarget { - node: id.clone(), - output: output_data.output.downgrade(), - left_up_idx: idx, - orientation, - } - .into(), - last_geometry.loc - + output_data.location - + tree - .children(&id) - .unwrap() - .skip(idx) - .next() - .map(|node| { - let geo = node.data().geometry(); - geo.loc + geo.size - }) - .unwrap(), - )) - } - _ => None, - } - } else if matches!(overview, OverviewMode::Started(Trigger::Pointer(_), _)) { - let non_exclusive_zone = - layer_map_for_output(&output_data.output).non_exclusive_zone(); - let geometries = geometries_for_groupview( - tree, - Option::<&mut GlowRenderer>::None, - non_exclusive_zone, - None, - 1.0, - overview.alpha().unwrap(), - 1.0, - placeholder_id, - Some(None), - None, - None, - ) - .0; - - let mut result = None; - let mut lookup = Some(root.clone()); - while let Some(node) = lookup { - let data = tree.get(&node).unwrap().data(); - if geometries - .get(&node) - .map(|geo| geo.contains(location)) - .unwrap_or(false) - { - result = Some(node.clone()); - } - - lookup = None; - if result.is_some() && data.is_group() { - if tree.children(&node).unwrap().any(|child| { - matches!( - child.data(), - Data::Placeholder { - initial_placeholder: false, - .. - } - ) - }) { + .data() + .geometry() + .contains(location) + { + lookup = Some(child_id.clone()); break; } - for child_id in tree.children_ids(&node).unwrap() { - if geometries - .get(child_id) - .map(|geo| geo.contains(location)) - .unwrap_or(false) - { - lookup = Some(child_id.clone()); - break; + } + } + } + + match result.map(|id| (id.clone(), tree.get(&id).unwrap().data().clone())) { + Some(( + _, + Data::Mapped { + mapped, + last_geometry, + }, + )) => { + let test_point = (location.to_f64() - last_geometry.loc.to_f64() + + mapped.geometry().loc.to_f64().as_local()) + .as_logical(); + mapped.is_in_input_region(&test_point).then(|| { + ( + mapped.clone().into(), + last_geometry.loc - mapped.geometry().loc.as_local(), + ) + }) + } + Some(( + id, + Data::Group { + orientation, + last_geometry, + .. + }, + )) => { + let idx = tree + .children(&id) + .unwrap() + .position(|node| { + let data = node.data(); + match orientation { + Orientation::Vertical => location.x < data.geometry().loc.x, + Orientation::Horizontal => location.y < data.geometry().loc.y, } + }) + .and_then(|x| x.checked_sub(1))?; + Some(( + ResizeForkTarget { + node: id.clone(), + output: self.output.downgrade(), + left_up_idx: idx, + orientation, + } + .into(), + last_geometry.loc + + tree + .children(&id) + .unwrap() + .skip(idx) + .next() + .map(|node| { + let geo = node.data().geometry(); + geo.loc + geo.size + }) + .unwrap(), + )) + } + _ => None, + } + } else if matches!(overview, OverviewMode::Started(Trigger::Pointer(_), _)) { + let non_exclusive_zone = layer_map_for_output(&self.output) + .non_exclusive_zone() + .as_local(); + let geometries = geometries_for_groupview( + tree, + Option::<&mut GlowRenderer>::None, + non_exclusive_zone, + None, + 1.0, + overview.alpha().unwrap(), + 1.0, + placeholder_id, + Some(None), + None, + None, + ) + .0; + + let mut result = None; + let mut lookup = Some(root.clone()); + while let Some(node) = lookup { + let data = tree.get(&node).unwrap().data(); + if geometries + .get(&node) + .map(|geo| geo.contains(location)) + .unwrap_or(false) + { + result = Some(node.clone()); + } + + lookup = None; + if result.is_some() && data.is_group() { + if tree.children(&node).unwrap().any(|child| { + matches!( + child.data(), + Data::Placeholder { + initial_placeholder: false, + .. + } + ) + }) { + break; + } + for child_id in tree.children_ids(&node).unwrap() { + if geometries + .get(child_id) + .map(|geo| geo.contains(location)) + .unwrap_or(false) + { + lookup = Some(child_id.clone()); + break; } } } + } - if let Some(res_id) = result { - let mut last_geometry = *geometries.get(&res_id)?; - let node = tree.get(&res_id).unwrap(); - let data = node.data().clone(); + if let Some(res_id) = result { + let mut last_geometry = *geometries.get(&res_id)?; + let node = tree.get(&res_id).unwrap(); + let data = node.data().clone(); - let group_zone = if let Data::Group { orientation, .. } = &data { - if node.children().iter().any(|child_id| { - tree.get(child_id) - .ok() - .map(|child| { - matches!( - child.data(), - Data::Placeholder { - initial_placeholder: false, - .. - } - ) - }) - .unwrap_or(false) - }) { - None + let group_zone = if let Data::Group { orientation, .. } = &data { + if node.children().iter().any(|child_id| { + tree.get(child_id) + .ok() + .map(|child| { + matches!( + child.data(), + Data::Placeholder { + initial_placeholder: false, + .. + } + ) + }) + .unwrap_or(false) + }) { + None + } else { + let left_edge = match &*last_overview_hover { + Some((_, TargetZone::GroupEdge(id, Direction::Left))) + if *id == res_id => + { + let zone = Rectangle::from_loc_and_size( + last_geometry.loc, + (80, last_geometry.size.h), + ); + last_geometry.loc.x += 80; + last_geometry.size.w -= 80; + zone + } + _ => { + let zone = Rectangle::from_loc_and_size( + last_geometry.loc, + (32, last_geometry.size.h), + ); + last_geometry.loc.x += 32; + last_geometry.size.w -= 32; + zone + } + }; + let top_edge = match &*last_overview_hover { + Some((_, TargetZone::GroupEdge(id, Direction::Up))) + if *id == res_id => + { + let zone = Rectangle::from_loc_and_size( + last_geometry.loc, + (last_geometry.size.w, 80), + ); + last_geometry.loc.y += 80; + last_geometry.size.h -= 80; + zone + } + _ => { + let zone = Rectangle::from_loc_and_size( + last_geometry.loc, + (last_geometry.size.w, 32), + ); + last_geometry.loc.y += 32; + last_geometry.size.h -= 32; + zone + } + }; + let right_edge = match &*last_overview_hover { + Some((_, TargetZone::GroupEdge(id, Direction::Right))) + if *id == res_id => + { + let zone = Rectangle::from_loc_and_size( + ( + last_geometry.loc.x + last_geometry.size.w - 80, + last_geometry.loc.y, + ), + (80, last_geometry.size.h), + ); + last_geometry.size.w -= 80; + zone + } + _ => { + let zone = Rectangle::from_loc_and_size( + ( + last_geometry.loc.x + last_geometry.size.w - 32, + last_geometry.loc.y, + ), + (32, last_geometry.size.h), + ); + last_geometry.size.w -= 32; + zone + } + }; + let bottom_edge = match &*last_overview_hover { + Some((_, TargetZone::GroupEdge(id, Direction::Down))) + if *id == res_id => + { + let zone = Rectangle::from_loc_and_size( + ( + last_geometry.loc.x, + last_geometry.loc.y + last_geometry.size.h - 80, + ), + (last_geometry.size.w, 80), + ); + last_geometry.size.h -= 80; + zone + } + _ => { + let zone = Rectangle::from_loc_and_size( + ( + last_geometry.loc.x, + last_geometry.loc.y + last_geometry.size.h - 32, + ), + (last_geometry.size.w, 32), + ); + last_geometry.size.h -= 32; + zone + } + }; + + if left_edge.contains(location) { + Some(TargetZone::GroupEdge(res_id.clone(), Direction::Left)) + } else if right_edge.contains(location) { + Some(TargetZone::GroupEdge(res_id.clone(), Direction::Right)) + } else if top_edge.contains(location) { + Some(TargetZone::GroupEdge(res_id.clone(), Direction::Up)) + } else if bottom_edge.contains(location) { + Some(TargetZone::GroupEdge(res_id.clone(), Direction::Down)) } else { - let left_edge = match &*last_overview_hover { - Some((_, TargetZone::GroupEdge(id, Direction::Left))) - if *id == res_id => - { - let zone = Rectangle::from_loc_and_size( - last_geometry.loc, - (80, last_geometry.size.h), - ); - last_geometry.loc.x += 80; - last_geometry.size.w -= 80; - zone - } - _ => { - let zone = Rectangle::from_loc_and_size( - last_geometry.loc, - (32, last_geometry.size.h), - ); - last_geometry.loc.x += 32; - last_geometry.size.w -= 32; - zone - } - }; - let top_edge = match &*last_overview_hover { - Some((_, TargetZone::GroupEdge(id, Direction::Up))) - if *id == res_id => - { - let zone = Rectangle::from_loc_and_size( - last_geometry.loc, - (last_geometry.size.w, 80), - ); - last_geometry.loc.y += 80; - last_geometry.size.h -= 80; - zone - } - _ => { - let zone = Rectangle::from_loc_and_size( - last_geometry.loc, - (last_geometry.size.w, 32), - ); - last_geometry.loc.y += 32; - last_geometry.size.h -= 32; - zone - } - }; - let right_edge = match &*last_overview_hover { - Some((_, TargetZone::GroupEdge(id, Direction::Right))) - if *id == res_id => - { - let zone = Rectangle::from_loc_and_size( - ( - last_geometry.loc.x + last_geometry.size.w - 80, - last_geometry.loc.y, - ), - (80, last_geometry.size.h), - ); - last_geometry.size.w -= 80; - zone - } - _ => { - let zone = Rectangle::from_loc_and_size( - ( - last_geometry.loc.x + last_geometry.size.w - 32, - last_geometry.loc.y, - ), - (32, last_geometry.size.h), - ); - last_geometry.size.w -= 32; - zone - } - }; - let bottom_edge = match &*last_overview_hover { - Some((_, TargetZone::GroupEdge(id, Direction::Down))) - if *id == res_id => - { - let zone = Rectangle::from_loc_and_size( - ( - last_geometry.loc.x, - last_geometry.loc.y + last_geometry.size.h - 80, - ), - (last_geometry.size.w, 80), - ); - last_geometry.size.h -= 80; - zone - } - _ => { - let zone = Rectangle::from_loc_and_size( - ( - last_geometry.loc.x, - last_geometry.loc.y + last_geometry.size.h - 32, - ), - (last_geometry.size.w, 32), - ); - last_geometry.size.h -= 32; - zone - } - }; - - if left_edge.contains(location) { - Some(TargetZone::GroupEdge(res_id.clone(), Direction::Left)) - } else if right_edge.contains(location) { - Some(TargetZone::GroupEdge(res_id.clone(), Direction::Right)) - } else if top_edge.contains(location) { - Some(TargetZone::GroupEdge(res_id.clone(), Direction::Up)) - } else if bottom_edge.contains(location) { - Some(TargetZone::GroupEdge(res_id.clone(), Direction::Down)) - } else { - let idx = tree - .children_ids(&res_id) - .unwrap() - .position(|node| { - let Some(geo) = geometries.get(node) else { + let idx = tree + .children_ids(&res_id) + .unwrap() + .position(|node| { + let Some(geo) = geometries.get(node) else { return false; }; - match orientation { - Orientation::Vertical => location.x < geo.loc.x, - Orientation::Horizontal => location.y < geo.loc.y, - } - }) - .and_then(|x| x.checked_sub(1)) - .unwrap_or(0); - Some(TargetZone::GroupInterior(res_id.clone(), idx)) - } + match orientation { + Orientation::Vertical => location.x < geo.loc.x, + Orientation::Horizontal => location.y < geo.loc.y, + } + }) + .and_then(|x| x.checked_sub(1)) + .unwrap_or(0); + Some(TargetZone::GroupInterior(res_id.clone(), idx)) } - } else { - None - }; + } + } else { + None + }; - let target_zone = group_zone.unwrap_or_else(|| match &data { - Data::Placeholder { .. } => TargetZone::InitialPlaceholder(res_id), - Data::Group { .. } | Data::Mapped { .. } => { - let id = if data.is_group() { - tree.get(&res_id) - .unwrap() - .children() - .iter() - .find(|child_id| { - tree.get(child_id).unwrap().data().is_mapped(None) - }) - .expect("Placeholder group without real window?") - .clone() - } else { - res_id + let target_zone = group_zone.unwrap_or_else(|| match &data { + Data::Placeholder { .. } => TargetZone::InitialPlaceholder(res_id), + Data::Group { .. } | Data::Mapped { .. } => { + let id = if data.is_group() { + tree.get(&res_id) + .unwrap() + .children() + .iter() + .find(|child_id| tree.get(child_id).unwrap().data().is_mapped(None)) + .expect("Placeholder group without real window?") + .clone() + } else { + res_id + }; + + let third_width = (last_geometry.size.w as f64 / 3.0).round() as i32; + let third_height = (last_geometry.size.h as f64 / 3.0).round() as i32; + let stack_region = Rectangle::from_extemities( + ( + last_geometry.loc.x + third_width, + last_geometry.loc.y + third_height, + ), + ( + last_geometry.loc.x + 2 * third_width, + last_geometry.loc.y + 2 * third_height, + ), + ); + + if stack_region.contains(location) { + TargetZone::WindowStack(id, last_geometry) + } else { + let left_right = { + let relative_loc = (location.x - last_geometry.loc.x) as f64; + if relative_loc < last_geometry.size.w as f64 / 2.0 { + (Direction::Left, relative_loc / last_geometry.size.w as f64) + } else { + ( + Direction::Right, + 1.0 - (relative_loc / last_geometry.size.w as f64), + ) + } + }; + let up_down = { + let relative_loc = (location.y - last_geometry.loc.y) as f64; + if relative_loc < last_geometry.size.h as f64 / 2.0 { + (Direction::Up, relative_loc / last_geometry.size.h as f64) + } else { + ( + Direction::Down, + 1.0 - (relative_loc / last_geometry.size.h as f64), + ) + } }; - let third_width = (last_geometry.size.w as f64 / 3.0).round() as i32; - let third_height = (last_geometry.size.h as f64 / 3.0).round() as i32; - let stack_region = Rectangle::from_extemities( - ( - last_geometry.loc.x + third_width, - last_geometry.loc.y + third_height, - ), - ( - last_geometry.loc.x + 2 * third_width, - last_geometry.loc.y + 2 * third_height, - ), - ); - - if stack_region.contains(location) { - TargetZone::WindowStack(id, last_geometry) + let direction = if left_right.1 < up_down.1 { + left_right.0 } else { - let left_right = { - let relative_loc = (location.x - last_geometry.loc.x) as f64; - if relative_loc < last_geometry.size.w as f64 / 2.0 { - ( - Direction::Left, - relative_loc / last_geometry.size.w as f64, - ) - } else { - ( - Direction::Right, - 1.0 - (relative_loc / last_geometry.size.w as f64), - ) - } - }; - let up_down = { - let relative_loc = (location.y - last_geometry.loc.y) as f64; - if relative_loc < last_geometry.size.h as f64 / 2.0 { - (Direction::Up, relative_loc / last_geometry.size.h as f64) - } else { - ( - Direction::Down, - 1.0 - (relative_loc / last_geometry.size.h as f64), - ) - } - }; + up_down.0 + }; - let direction = if left_right.1 < up_down.1 { - left_right.0 + TargetZone::WindowSplit(id, direction) + } + } + }); + + match &mut *last_overview_hover { + last_overview_hover @ None => { + *last_overview_hover = Some(( + None, + tree.traverse_pre_order_ids(root) + .unwrap() + .find(|id| match tree.get(id).unwrap().data() { + Data::Placeholder { + initial_placeholder: true, + .. + } => true, + _ => false, + }) + .map(|node_id| TargetZone::InitialPlaceholder(node_id)) + .unwrap_or(TargetZone::Initial), + )); + } + Some((instant, old_target_zone)) => { + if *old_target_zone != target_zone { + let overdue = if let Some(instant) = instant { + match old_target_zone { + TargetZone::InitialPlaceholder(_) => { + Instant::now().duration_since(*instant) + > INITIAL_MOUSE_ANIMATION_DELAY + } + _ => { + Instant::now().duration_since(*instant) + > MOUSE_ANIMATION_DELAY + } + } + } else { + *instant = Some(Instant::now()); + false + }; + + if overdue { + let duration = if target_zone.is_window_zone() + && !old_target_zone.is_window_zone() + { + ANIMATION_DURATION * 2 } else { - up_down.0 + ANIMATION_DURATION }; - TargetZone::WindowSplit(id, direction) - } - } - }); + let mut tree = tree.copy_clone(); - match &mut *last_overview_hover { - last_overview_hover @ None => { - *last_overview_hover = Some(( - None, - tree.traverse_pre_order_ids(root) - .unwrap() - .find(|id| match tree.get(id).unwrap().data() { - Data::Placeholder { - initial_placeholder: true, - .. - } => true, - _ => false, - }) - .map(|node_id| TargetZone::InitialPlaceholder(node_id)) - .unwrap_or(TargetZone::Initial), - )); - } - Some((instant, old_target_zone)) => { - if *old_target_zone != target_zone { - let overdue = if let Some(instant) = instant { - match old_target_zone { - TargetZone::InitialPlaceholder(_) => { - Instant::now().duration_since(*instant) - > INITIAL_MOUSE_ANIMATION_DELAY - } - _ => { - Instant::now().duration_since(*instant) - > MOUSE_ANIMATION_DELAY + // remove old placeholders + let removed = if let TargetZone::InitialPlaceholder(node_id) = + old_target_zone + { + if tree.get(&node_id).is_ok() { + TilingLayout::unmap_internal(&mut tree, &node_id); + } + true + } else if let TargetZone::WindowSplit(node_id, _) = old_target_zone + { + if let Some(children) = tree + .get(&node_id) + .ok() + .and_then(|node| node.parent()) + .and_then(|parent_id| tree.get(parent_id).ok()) + .map(|node| node.children().clone()) + { + for id in children { + let matches = matches!( + tree.get(&id).unwrap().data(), + Data::Placeholder { + initial_placeholder: false, + .. + } + ); + + if matches { + TilingLayout::unmap_internal(&mut tree, &id); + break; + } } } + true + } else if let TargetZone::GroupEdge(node_id, _) = old_target_zone { + if let Ok(node) = tree.get_mut(&node_id) { + match node.data_mut() { + Data::Group { pill_indicator, .. } => { + *pill_indicator = None; + } + _ => unreachable!(), + } + } + true + } else if let TargetZone::GroupInterior(node_id, _) = + old_target_zone + { + if let Ok(node) = tree.get_mut(&node_id) { + match node.data_mut() { + Data::Group { pill_indicator, .. } => { + *pill_indicator = None; + } + _ => unreachable!(), + } + } + true } else { - *instant = Some(Instant::now()); false }; - if overdue { - let duration = if target_zone.is_window_zone() - && !old_target_zone.is_window_zone() - { - ANIMATION_DURATION * 2 - } else { - ANIMATION_DURATION - }; - - let mut tree = tree.copy_clone(); - - // remove old placeholders - let removed = if let TargetZone::InitialPlaceholder(node_id) = - old_target_zone - { - if tree.get(&node_id).is_ok() { - TilingLayout::unmap_internal(&mut tree, &node_id); - } - true - } else if let TargetZone::WindowSplit(node_id, _) = - old_target_zone - { - if let Some(children) = tree - .get(&node_id) - .ok() - .and_then(|node| node.parent()) - .and_then(|parent_id| tree.get(parent_id).ok()) - .map(|node| node.children().clone()) - { - for id in children { - let matches = matches!( - tree.get(&id).unwrap().data(), - Data::Placeholder { - initial_placeholder: false, - .. - } - ); - - if matches { - TilingLayout::unmap_internal(&mut tree, &id); - break; - } - } - } - true - } else if let TargetZone::GroupEdge(node_id, _) = - old_target_zone - { - if let Ok(node) = tree.get_mut(&node_id) { - match node.data_mut() { - Data::Group { pill_indicator, .. } => { - *pill_indicator = None; - } - _ => unreachable!(), - } - } - true - } else if let TargetZone::GroupInterior(node_id, _) = - old_target_zone - { - if let Ok(node) = tree.get_mut(&node_id) { - match node.data_mut() { - Data::Group { pill_indicator, .. } => { - *pill_indicator = None; - } - _ => unreachable!(), - } - } - true - } else { - false - }; - - // add placeholders - let added = if let TargetZone::WindowSplit(node_id, dir) = - &target_zone - { - let id = tree - .insert( - Node::new(Data::Placeholder { - last_geometry: Rectangle::from_loc_and_size( - (0, 0), - (100, 100), - ), - initial_placeholder: false, - }), - InsertBehavior::UnderNode(node_id), - ) - .unwrap(); - let orientation = - if matches!(dir, Direction::Left | Direction::Right) { - Orientation::Vertical - } else { - Orientation::Horizontal - }; - TilingLayout::new_group( - &mut tree, - &node_id, - &id, - orientation, + // add placeholders + let added = if let TargetZone::WindowSplit(node_id, dir) = + &target_zone + { + let id = tree + .insert( + Node::new(Data::Placeholder { + last_geometry: Rectangle::from_loc_and_size( + (0, 0), + (100, 100), + ), + initial_placeholder: false, + }), + InsertBehavior::UnderNode(node_id), ) .unwrap(); - if matches!(dir, Direction::Left | Direction::Up) { - tree.make_first_sibling(&id).unwrap(); - } - - true - } else if let TargetZone::GroupEdge(node_id, direction) = - &target_zone - { - if let Ok(node) = tree.get_mut(&node_id) { - match node.data_mut() { - Data::Group { pill_indicator, .. } => { - *pill_indicator = - Some(PillIndicator::Outer(*direction)); - } - _ => unreachable!(), - } - true + let orientation = + if matches!(dir, Direction::Left | Direction::Right) { + Orientation::Vertical } else { - false - } - } else if let TargetZone::GroupInterior(node_id, idx) = - &target_zone - { - if let Ok(node) = tree.get_mut(&node_id) { - match node.data_mut() { - Data::Group { pill_indicator, .. } => { - *pill_indicator = - Some(PillIndicator::Inner(*idx)); - } - _ => unreachable!(), - } - true - } else { - false - } - } else { - false - }; - - if removed || added { - let blocker = TilingLayout::update_positions( - &output_data.output, - &mut tree, - *gaps, - ); - queue.push_tree(tree, duration, blocker); + Orientation::Horizontal + }; + TilingLayout::new_group(&mut tree, &node_id, &id, orientation) + .unwrap(); + if matches!(dir, Direction::Left | Direction::Up) { + tree.make_first_sibling(&id).unwrap(); } - *instant = None; - *old_target_zone = target_zone; + true + } else if let TargetZone::GroupEdge(node_id, direction) = + &target_zone + { + if let Ok(node) = tree.get_mut(&node_id) { + match node.data_mut() { + Data::Group { pill_indicator, .. } => { + *pill_indicator = + Some(PillIndicator::Outer(*direction)); + } + _ => unreachable!(), + } + true + } else { + false + } + } else if let TargetZone::GroupInterior(node_id, idx) = &target_zone + { + if let Ok(node) = tree.get_mut(&node_id) { + match node.data_mut() { + Data::Group { pill_indicator, .. } => { + *pill_indicator = Some(PillIndicator::Inner(*idx)); + } + _ => unreachable!(), + } + true + } else { + false + } + } else { + false + }; + + if removed || added { + let blocker = TilingLayout::update_positions( + &self.output, + &mut tree, + *gaps, + ); + self.queue.push_tree(tree, duration, blocker); } - } else { + *instant = None; + *old_target_zone = target_zone; } + } else { + *instant = None; } } } - - None - } else { - None } - }) + + None + } else { + None + } } - pub fn mapped( - &self, - ) -> impl Iterator)> { - self.queues - .iter() - .flat_map(|(output_data, queue)| { - let tree = &queue.trees.back().unwrap().0; - if let Some(root) = tree.root_node_id() { - Some( + pub fn mapped(&self) -> impl Iterator)> { + let tree = &self.queue.trees.back().unwrap().0; + let iter = if let Some(root) = tree.root_node_id() { + Some( + tree.traverse_pre_order(root) + .unwrap() + .filter(|node| node.data().is_mapped(None)) + .filter(|node| match node.data() { + Data::Mapped { mapped, .. } => mapped.is_activated(false), + _ => unreachable!(), + }) + .map(|node| match node.data() { + Data::Mapped { + mapped, + last_geometry, + .. + } => (&self.output, mapped, { + let geo = last_geometry.clone(); + geo.to_global(&self.output) + }), + _ => unreachable!(), + }) + .chain( tree.traverse_pre_order(root) .unwrap() .filter(|node| node.data().is_mapped(None)) .filter(|node| match node.data() { - Data::Mapped { mapped, .. } => mapped.is_activated(false), + Data::Mapped { mapped, .. } => !mapped.is_activated(false), _ => unreachable!(), }) .map(|node| match node.data() { @@ -3573,72 +3331,58 @@ impl TilingLayout { mapped, last_geometry, .. - } => (&output_data.output, mapped, { - let mut geo = last_geometry.clone(); - geo.loc += output_data.location; - geo + } => (&self.output, mapped, { + let geo = last_geometry.clone(); + geo.to_global(&self.output) }), _ => unreachable!(), - }) - .chain( - tree.traverse_pre_order(root) - .unwrap() - .filter(|node| node.data().is_mapped(None)) - .filter(|node| match node.data() { - Data::Mapped { mapped, .. } => !mapped.is_activated(false), - _ => unreachable!(), - }) - .map(|node| match node.data() { - Data::Mapped { - mapped, - last_geometry, - .. - } => (&output_data.output, mapped, { - let mut geo = last_geometry.clone(); - geo.loc += output_data.location; - geo - }), - _ => unreachable!(), - }), - ), - ) - } else { - None - } - }) - .flatten() + }), + ), + ) + } else { + None + }; + iter.into_iter().flatten() } pub fn windows( &self, - ) -> impl Iterator)> + '_ { + ) -> impl Iterator)> + '_ { self.mapped().flat_map(|(output, mapped, geo)| { mapped.windows().map(move |(w, p)| { (output.clone(), w, { let mut geo = geo.clone(); - geo.loc += p; - geo.size -= p.to_size(); + geo.loc += p.as_global(); + geo.size -= p.to_size().as_global(); geo }) }) }) } - pub fn merge(&mut self, other: TilingLayout) { - for (output_data, mut src_queue) in other.queues { - let src = src_queue.trees.pop_back().unwrap().0; - let dst_queue = self.queues.entry(output_data.clone()).or_default(); - let mut dst = dst_queue.trees.back().unwrap().0.copy_clone(); + pub fn has_node(&self, node: &NodeId) -> bool { + let tree = &self.queue.trees.back().unwrap().0; + tree.root_node_id() + .map(|root| { + tree.traverse_pre_order_ids(root) + .unwrap() + .any(|id| &id == node) + }) + .unwrap_or(false) + } - let orientation = match output_data.output.geometry().size { - x if x.w >= x.h => Orientation::Vertical, - _ => Orientation::Horizontal, - }; - TilingLayout::merge_trees(src, &mut dst, orientation); + pub fn merge(&mut self, mut other: TilingLayout) { + let src = other.queue.trees.pop_back().unwrap().0; + let mut dst = self.queue.trees.back().unwrap().0.copy_clone(); - let blocker = TilingLayout::update_positions(&output_data.output, &mut dst, self.gaps); - dst_queue.push_tree(dst, ANIMATION_DURATION, blocker); - } + let orientation = match self.output.geometry().size { + x if x.w >= x.h => Orientation::Vertical, + _ => Orientation::Horizontal, + }; + TilingLayout::merge_trees(src, &mut dst, orientation); + + let blocker = TilingLayout::update_positions(&self.output, &mut dst, self.gaps); + self.queue.push_tree(dst, ANIMATION_DURATION, blocker); } fn merge_trees(src: Tree, dst: &mut Tree, orientation: Orientation) { @@ -3680,12 +3424,11 @@ impl TilingLayout { } } - pub fn render_output( + pub fn render( &self, renderer: &mut R, - output: &Output, seat: Option<&Seat>, - non_exclusive_zone: Rectangle, + non_exclusive_zone: Rectangle, overview: (OverviewMode, Option<(SwapIndicator, Option<&Tree>)>), resize_indicator: Option<(ResizeMode, ResizeIndicator)>, indicator_thickness: u8, @@ -3706,27 +3449,23 @@ impl TilingLayout { #[cfg(feature = "debug")] puffin::profile_function!(); - let output_scale = output.current_scale().fractional_scale(); + let output_scale = self.output.current_scale().fractional_scale(); - if !self.queues.contains_key(output) { - return Err(OutputNotMapped); - } - - let queue = self.queues.get(output).unwrap(); - let (target_tree, duration, _) = if queue.animation_start.is_some() { - queue + let (target_tree, duration, _) = if self.queue.animation_start.is_some() { + self.queue .trees .get(1) .expect("Animation ongoing, should have two trees") } else { - queue.trees.front().unwrap() + self.queue.trees.front().unwrap() }; - let reference_tree = queue + let reference_tree = self + .queue .animation_start .is_some() - .then(|| &queue.trees.front().unwrap().0); + .then(|| &self.queue.trees.front().unwrap().0); - let percentage = if let Some(animation_start) = queue.animation_start { + let percentage = if let Some(animation_start) = self.queue.animation_start { let percentage = Instant::now().duration_since(animation_start).as_millis() as f32 / duration.as_millis() as f32; ease(EaseInOutCubic, 0.0, 1.0, percentage) @@ -3816,7 +3555,7 @@ impl TilingLayout { old_geometries, is_overview, seat, - output, + &self.output, percentage, draw_groups, if let Some(transition) = draw_groups { @@ -3931,9 +3670,7 @@ where seat.get_keyboard() .unwrap() .current_focus() - .and_then(|target| { - TilingLayout::currently_focused_node(&tree, &seat.active_output(), target) - }) + .and_then(|target| TilingLayout::currently_focused_node(&tree, target)) }) .map(|(id, _)| id); let focused_geo = if let Some(focused_id) = focused.as_ref() { diff --git a/src/shell/mod.rs b/src/shell/mod.rs index e4a8263d..ca6c75cb 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -1,4 +1,5 @@ use calloop::LoopHandle; +use indexmap::IndexMap; use serde::{Deserialize, Serialize}; use std::{ cell::RefCell, @@ -36,15 +37,18 @@ use smithay::{ }; use crate::{ - config::{Config, KeyModifiers, KeyPattern, OutputConfig, WorkspaceMode as ConfigMode}, + config::{Config, KeyModifiers, KeyPattern, OutputConfig, WorkspaceMode}, state::client_has_security_context, utils::prelude::*, - wayland::protocols::{ + wayland::{ + handlers::output, + protocols::{ toplevel_info::ToplevelInfoState, toplevel_management::{ManagementCapabilities, ToplevelManagementState}, workspace::{ WorkspaceCapabilities, WorkspaceGroupHandle, WorkspaceHandle, WorkspaceState, WorkspaceUpdateGuard, + }, }, }, }; @@ -143,12 +147,18 @@ impl ResizeMode { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum MaximizeMode { + Floating, + OnTop, +} + #[derive(Debug)] pub struct Shell { + pub workspaces: Workspaces, + pub popups: PopupManager, - pub outputs: Vec, - pub workspaces: WorkspaceMode, - pub tiling_enabled: bool, + pub maximize_mode: MaximizeMode, pub pending_windows: Vec<(CosmicSurface, Seat)>, pub pending_layers: Vec<(LayerSurface, Output, Seat)>, pub override_redirect_windows: Vec, @@ -160,7 +170,6 @@ pub struct Shell { pub xdg_shell_state: XdgShellState, pub workspace_state: WorkspaceState, - gaps: (u8, u8), overview_mode: OverviewMode, swap_indicator: Option, resize_mode: ResizeMode, @@ -179,11 +188,11 @@ pub struct Shell { pub struct WorkspaceSet { previously_active: Option<(usize, Instant)>, active: usize, - amount: WorkspaceAmount, group: WorkspaceGroupHandle, idx: usize, tiling_enabled: bool, gaps: (u8, u8), + output: Output, pub(crate) workspaces: Vec, } @@ -195,6 +204,7 @@ pub enum WorkspaceAmount { fn create_workspace( state: &mut WorkspaceUpdateGuard<'_, State>, + output: &Output, group_handle: &WorkspaceGroupHandle, active: bool, tiling: bool, @@ -208,12 +218,13 @@ fn create_workspace( &workspace_handle, [WorkspaceCapabilities::Activate].into_iter(), ); - Workspace::new(workspace_handle, tiling, gaps) + Workspace::new(workspace_handle, output.clone(), tiling, gaps) } impl WorkspaceSet { fn new( state: &mut WorkspaceUpdateGuard<'_, State>, + output: &Output, amount: WorkspaceAmount, idx: usize, tiling_enabled: bool, @@ -223,7 +234,8 @@ impl WorkspaceSet { let workspaces = match amount { WorkspaceAmount::Dynamic => { - let workspace = create_workspace(state, &group_handle, true, tiling_enabled, gaps); + let workspace = + create_workspace(state, output, &group_handle, true, tiling_enabled, gaps); workspace_set_idx(state, 1, idx, &workspace.handle); state.set_workspace_capabilities( &workspace.handle, @@ -233,8 +245,14 @@ impl WorkspaceSet { } WorkspaceAmount::Static(len) => (0..len) .map(|i| { - let workspace = - create_workspace(state, &group_handle, i == 0, tiling_enabled, gaps); + let workspace = create_workspace( + state, + output, + &group_handle, + i == 0, + tiling_enabled, + gaps, + ); workspace_set_idx(state, i + 1, idx, &workspace.handle); state.set_workspace_capabilities( &workspace.handle, @@ -248,12 +266,12 @@ impl WorkspaceSet { WorkspaceSet { previously_active: None, active: 0, - amount, group: group_handle, idx, tiling_enabled, gaps, workspaces, + output: output.clone(), } } @@ -278,46 +296,38 @@ impl WorkspaceSet { } } - fn refresh<'a>( + fn set_output( &mut self, - state: &mut WorkspaceState, + new_output: &Output, toplevel_info: &mut ToplevelInfoState, - outputs: impl Iterator)>, ) { + for workspace in &mut self.workspaces { + workspace.set_output(new_output, toplevel_info); + } + self.output = new_output.clone(); + } + + fn refresh<'a>(&mut self) { if let Some((_, start)) = self.previously_active { if Instant::now().duration_since(start).as_millis() >= ANIMATION_DURATION.as_millis() { self.previously_active = None; } } else { - match self.amount { - WorkspaceAmount::Dynamic => self.ensure_last_empty(state, outputs), - WorkspaceAmount::Static(len) => { - self.ensure_static(len as usize, state, toplevel_info, outputs) - } - } - self.workspaces[self.active].refresh(); } } - fn ensure_last_empty<'a>( - &mut self, - state: &mut WorkspaceState, - outputs: impl Iterator)>, - ) { - let mut state = state.update(); - - // add empty at the end, if necessary - if self.workspaces.last().unwrap().windows().next().is_some() { + fn add_empty_workspace(&mut self, state: &mut WorkspaceUpdateGuard) { let mut workspace = create_workspace( - &mut state, + state, + &self.output, &self.group, false, self.tiling_enabled, self.gaps, ); workspace_set_idx( - &mut state, + state, self.workspaces.len() as u8 + 1, self.idx, &workspace.handle, @@ -326,15 +336,23 @@ impl WorkspaceSet { &workspace.handle, [WorkspaceCapabilities::Activate].into_iter(), ); - for (output, location) in outputs { - workspace.map_output(output, location); - } self.workspaces.push(workspace); } + fn ensure_last_empty<'a>(&mut self, state: &mut WorkspaceUpdateGuard) { + // add empty at the end, if necessary + if self + .workspaces + .last() + .map(|last| last.windows().next().is_some()) + .unwrap_or(true) + { + self.add_empty_workspace(state); + } + + // remove empty workspaces in between, if they are not active let len = self.workspaces.len(); let mut keep = vec![true; len]; - // remove empty workspaces in between, if they are not active for (i, workspace) in self.workspaces.iter().enumerate() { let has_windows = workspace.windows().next().is_some(); @@ -354,20 +372,18 @@ impl WorkspaceSet { if keep.iter().any(|val| *val == false) { for (i, workspace) in self.workspaces.iter().enumerate() { - workspace_set_idx(&mut state, i as u8 + 1, self.idx, &workspace.handle); + workspace_set_idx(state, i as u8 + 1, self.idx, &workspace.handle); } } } - fn ensure_static<'a>( + fn ensure_static( &mut self, amount: usize, - state: &mut WorkspaceState, + state: &mut WorkspaceUpdateGuard, toplevel_info: &mut ToplevelInfoState, - outputs: impl Iterator)>, ) { if amount < self.workspaces.len() { - let mut state = state.update(); // merge last ones let overflow = self.workspaces.split_off(amount); if self.active >= self.workspaces.len() { @@ -376,7 +392,11 @@ impl WorkspaceSet { } let last_space = self.workspaces.last_mut().unwrap(); - for workspace in overflow { + for mut workspace in overflow { + if last_space.fullscreen.is_some() { + workspace.remove_fullscreen(); + } + for element in workspace.mapped() { // fixup toplevel state for (toplevel, _) in element.windows() { @@ -386,27 +406,26 @@ impl WorkspaceSet { } last_space.tiling_layer.merge(workspace.tiling_layer); last_space.floating_layer.merge(workspace.floating_layer); - last_space - .fullscreen - .extend(workspace.fullscreen.into_iter()); + if workspace.fullscreen.is_some() { + last_space.fullscreen = workspace.fullscreen; + } state.remove_workspace(workspace.handle); } last_space.refresh(); } else if amount > self.workspaces.len() { - let mut state = state.update(); // add empty ones - let outputs = outputs.collect::>(); while amount > self.workspaces.len() { - let mut workspace = create_workspace( - &mut state, + let workspace = create_workspace( + state, + &self.output, &self.group, false, self.tiling_enabled, self.gaps, ); workspace_set_idx( - &mut state, + state, self.workspaces.len() as u8 + 1, self.idx, &workspace.handle, @@ -415,9 +434,6 @@ impl WorkspaceSet { &workspace.handle, [WorkspaceCapabilities::Activate].into_iter(), ); - for &(output, location) in outputs.iter() { - workspace.map_output(output, location); - } self.workspaces.push(workspace); } } @@ -441,138 +457,295 @@ impl WorkspaceSet { } #[derive(Debug)] -pub enum WorkspaceMode { - OutputBound(HashMap, WorkspaceAmount), - Global(WorkspaceSet), -} - -impl WorkspaceMode { - pub fn new( - config: crate::config::WorkspaceMode, +pub struct Workspaces { + sets: IndexMap, + backup_set: Option, amount: WorkspaceAmount, - state: &mut WorkspaceUpdateGuard<'_, State>, + mode: WorkspaceMode, tiling_enabled: bool, gaps: (u8, u8), - ) -> WorkspaceMode { - match config { - crate::config::WorkspaceMode::Global => { - WorkspaceMode::Global(WorkspaceSet::new(state, amount, 0, tiling_enabled, gaps)) +} + +impl Workspaces { + pub fn new(config: &Config) -> Workspaces { + Workspaces { + sets: IndexMap::new(), + backup_set: None, + amount: config.static_conf.workspace_amount, + mode: config.static_conf.workspace_mode, + tiling_enabled: config.static_conf.tiling_enabled, + gaps: config.static_conf.gaps, + } + } + + pub fn add_output( + &mut self, + output: &Output, + workspace_state: &mut WorkspaceUpdateGuard<'_, State>, + toplevel_info_state: &mut ToplevelInfoState, + ) { + if self.sets.contains_key(output) { + return; + } + + let set = self.backup_set.take().unwrap_or_else(|| { + WorkspaceSet::new( + workspace_state, + &output, + self.amount, + self.sets.len(), + self.tiling_enabled, + self.gaps, + ) + }); + workspace_state.add_group_output(&set.group, &output); + + self.sets.insert(output.clone(), set); + for workspace in &mut self.sets.get_mut(output).unwrap().workspaces { + workspace.set_output(output, toplevel_info_state); + } + } + + pub fn remove_output( + &mut self, + output: &Output, + seats: impl Iterator>, + workspace_state: &mut WorkspaceUpdateGuard<'_, State>, + toplevel_info_state: &mut ToplevelInfoState, + ) { + if !self.sets.contains_key(output) { + return; + } + + if let Some(set) = self.sets.remove(output) { + { + let map = layer_map_for_output(output); + for surface in map.layers() { + surface.layer_surface().send_close(); + } } - crate::config::WorkspaceMode::OutputBound => { - WorkspaceMode::OutputBound(HashMap::new(), amount) + + // TODO: Heuristic which output to move to. + // It is supposed to be the *most* internal, we just pick the first one for now + // and hope enumeration order works in our favor. + let new_output = self.sets.get_index(0).map(|(o, _)| o.clone()); + if let Some(new_output) = new_output { + for seat in seats { + if &seat.active_output() == output { + seat.set_active_output(&new_output); + } + } + + let new_set = self.sets.get_mut(&new_output).unwrap(); + let workspace_group = new_set.group; + for mut workspace in set.workspaces { + // update workspace protocol state + workspace_state.remove_workspace(workspace.handle); + let workspace_handle = + workspace_state.create_workspace(&workspace_group).unwrap(); + workspace_state.set_workspace_capabilities( + &workspace_handle, + [WorkspaceCapabilities::Activate].into_iter(), + ); + let old_workspace_handle = workspace.handle; + workspace.handle = workspace_handle; + + for window in workspace.mapped() { + for (surface, _) in window.windows() { + toplevel_info_state + .toplevel_leave_workspace(&surface, &old_workspace_handle); + toplevel_info_state + .toplevel_enter_workspace(&surface, &workspace.handle); + } + } + + // update mapping + workspace.set_output(&new_output, toplevel_info_state); + workspace.refresh(); + + // TODO: merge if mode = static + new_set.workspaces.push(workspace); + } + workspace_state.remove_workspace_group(set.group); + + for (i, set) in self.sets.values_mut().enumerate() { + set.update_idx(workspace_state, i); + } + } else { + workspace_state.remove_group_output(&set.group, output); + self.backup_set = Some(set); } } } + //pub fn update_mode + + pub fn refresh( + &mut self, + workspace_state: &mut WorkspaceUpdateGuard<'_, State>, + toplevel_info_state: &mut ToplevelInfoState, + ) { + match self.mode { + WorkspaceMode::Global => { + match self.amount { + WorkspaceAmount::Dynamic => { + // this should never happen + let max = self + .sets + .values() + .map(|set| set.workspaces.len()) + .max() + .unwrap_or_default(); + for set in self + .sets + .values_mut() + .filter(|set| set.workspaces.len() < max) + { + while set.workspaces.len() < max { + set.add_empty_workspace(workspace_state) + } + } + + // add empty at the end, if necessary + if self + .sets + .values() + .flat_map(|set| set.workspaces.last()) + .any(|w| w.mapped().next().is_some()) + { + for set in self.sets.values_mut() { + set.add_empty_workspace(workspace_state); + } + } + + // remove empty workspaces in between, if they are not active + let len = self.sets[0].workspaces.len(); + let mut active = self.sets[0].active; + let mut keep = vec![true; len]; + for i in 0..len { + let has_windows = self + .sets + .values() + .any(|s| s.workspaces[i].windows().next().is_some()); + + if !has_windows && i != active && i != len - 1 { + for workspace in self.sets.values().map(|s| &s.workspaces[i]) { + workspace_state.remove_workspace(workspace.handle); + } + keep[i] = false; + } + } + + self.sets.values_mut().for_each(|s| { + let mut iter = keep.iter(); + s.workspaces.retain(|_| *iter.next().unwrap()); + }); + active -= keep.iter().take(active + 1).filter(|keep| !**keep).count(); + self.sets.values_mut().for_each(|s| { + s.active = active; + }); + + if keep.iter().any(|val| *val == false) { + for set in self.sets.values_mut() { + for (i, workspace) in set.workspaces.iter().enumerate() { + workspace_set_idx( + workspace_state, + i as u8 + 1, + set.idx, + &workspace.handle, + ); + } + } + } + } + WorkspaceAmount::Static(amount) => { + for set in self.sets.values_mut() { + set.ensure_static(amount as usize, workspace_state, toplevel_info_state) + } + } + } + } + WorkspaceMode::OutputBound => match self.amount { + WorkspaceAmount::Dynamic => { + for set in self.sets.values_mut() { + set.ensure_last_empty(workspace_state); + } + } + WorkspaceAmount::Static(amount) => { + for set in self.sets.values_mut() { + set.ensure_static(amount as usize, workspace_state, toplevel_info_state) + } + } + }, + } + + for set in self.sets.values_mut() { + set.refresh() + } + } + pub fn get(&self, num: usize, output: &Output) -> Option<&Workspace> { - match self { - WorkspaceMode::Global(set) => set.workspaces.get(num), - WorkspaceMode::OutputBound(sets, _) => { - sets.get(output).and_then(|set| set.workspaces.get(num)) - } - } + self.sets + .get(output) + .and_then(|set| set.workspaces.get(num)) } pub fn get_mut(&mut self, num: usize, output: &Output) -> Option<&mut Workspace> { - match self { - WorkspaceMode::Global(set) => set.workspaces.get_mut(num), - WorkspaceMode::OutputBound(sets, _) => sets + self.sets .get_mut(output) - .and_then(|set| set.workspaces.get_mut(num)), - } + .and_then(|set| set.workspaces.get_mut(num)) } pub fn active(&self, output: &Output) -> (Option<(&Workspace, Instant)>, &Workspace) { - match self { - WorkspaceMode::Global(set) => ( - set.previously_active - .map(|(idx, start)| (&set.workspaces[idx], start)), - &set.workspaces[set.active], - ), - WorkspaceMode::OutputBound(sets, _) => { - let set = sets.get(output).unwrap(); + let set = self.sets.get(output).unwrap(); ( set.previously_active .map(|(idx, start)| (&set.workspaces[idx], start)), &set.workspaces[set.active], ) - } - } } pub fn active_mut(&mut self, output: &Output) -> &mut Workspace { - match self { - WorkspaceMode::Global(set) => &mut set.workspaces[set.active], - WorkspaceMode::OutputBound(sets, _) => { - let set = sets.get_mut(output).unwrap(); + let set = self.sets.get_mut(output).unwrap(); &mut set.workspaces[set.active] - } - } } pub fn active_num(&self, output: &Output) -> (Option, usize) { - match self { - WorkspaceMode::Global(set) => (set.previously_active.map(|(idx, _)| idx), set.active), - WorkspaceMode::OutputBound(sets, _) => { - let set = sets.get(output).unwrap(); + let set = self.sets.get(output).unwrap(); (set.previously_active.map(|(idx, _)| idx), set.active) - } - } } pub fn len(&self, output: &Output) -> usize { - match self { - WorkspaceMode::Global(set) => set.workspaces.len(), - WorkspaceMode::OutputBound(sets, _) => { - let set = sets.get(output).unwrap(); + let set = self.sets.get(output).unwrap(); set.workspaces.len() } - } + + pub fn iter(&self) -> impl Iterator { + self.sets.iter() } pub fn spaces(&self) -> impl Iterator { - match self { - WorkspaceMode::Global(set) => { - Box::new(set.workspaces.iter()) as Box> - } - WorkspaceMode::OutputBound(sets, _) => { - Box::new(sets.values().flat_map(|set| set.workspaces.iter())) - } - } + self.sets.values().flat_map(|set| set.workspaces.iter()) } pub fn spaces_for_output(&self, output: &Output) -> impl Iterator { - match self { - WorkspaceMode::Global(set) => { - Box::new(set.workspaces.iter()) as Box> - } - WorkspaceMode::OutputBound(sets, _) => Box::new( - sets.get(output) + self.sets + .get(output) .into_iter() - .flat_map(|set| set.workspaces.iter()), - ), - } + .flat_map(|set| set.workspaces.iter()) } pub fn spaces_mut(&mut self) -> impl Iterator { - match self { - WorkspaceMode::Global(set) => { - Box::new(set.workspaces.iter_mut()) as Box> - } - WorkspaceMode::OutputBound(sets, _) => { - Box::new(sets.values_mut().flat_map(|set| set.workspaces.iter_mut())) - } - } + Box::new( + self.sets + .values_mut() + .flat_map(|set| set.workspaces.iter_mut()), + ) } pub fn update_tiling_status(&mut self, seat: &Seat, tiling: bool) { - match self { - WorkspaceMode::Global(set) => set.update_tiling_status(seat, tiling), - WorkspaceMode::OutputBound(sets, _) => { - for set in sets.values_mut() { + for set in self.sets.values_mut() { set.update_tiling_status(seat, tiling) - } - } } } } @@ -598,26 +771,16 @@ impl Shell { //|client| client.get_data::().map_or(false, |s| s.privileged), client_has_security_context, ); - let mut workspace_state = WorkspaceState::new( + let workspace_state = WorkspaceState::new( dh, //|client| client.get_data::().map_or(false, |s| s.privileged), client_has_security_context, ); - let tiling_enabled = config.static_conf.tiling_enabled; - let mode = WorkspaceMode::new( - config.static_conf.workspace_mode, - config.static_conf.workspace_amount, - &mut workspace_state.update(), - tiling_enabled, - config.static_conf.gaps, - ); - Shell { popups: PopupManager::default(), - outputs: Vec::new(), - workspaces: mode, - tiling_enabled, + workspaces: Workspaces::new(config), + maximize_mode: MaximizeMode::Floating, pending_windows: Vec::new(), pending_layers: Vec::new(), @@ -629,7 +792,6 @@ impl Shell { xdg_shell_state, workspace_state, - gaps: config.static_conf.gaps, overview_mode: OverviewMode::None, swap_indicator: None, resize_mode: ResizeMode::None, @@ -639,148 +801,24 @@ impl Shell { } pub fn add_output(&mut self, output: &Output) { - if self.outputs.contains(output) { - return; - } - - self.outputs.push(output.clone()); - let mut state = self.workspace_state.update(); - - match &mut self.workspaces { - WorkspaceMode::OutputBound(sets, amount) => { - // TODO: Restore previously assigned workspaces, if possible! - if !sets.contains_key(output) { - let set = WorkspaceSet::new( - &mut state, - *amount, - sets.len(), - self.tiling_enabled, - self.gaps, - ); - state.add_group_output(&set.group, &output); - sets.insert(output.clone(), set); - } - for workspace in &mut sets.get_mut(output).unwrap().workspaces { - workspace.map_output(output, (0, 0).into()); - } - } - WorkspaceMode::Global(set) => { - // TODO: Restore any window positions from previous outputs ??? - state.add_group_output(&set.group, output); - for workspace in &mut set.workspaces { - workspace.map_output( + self.workspaces.add_output( output, - output - .user_data() - .get::>() - .unwrap() - .borrow() - .position - .into(), - ); - } - } - } + &mut self.workspace_state.update(), + &mut self.toplevel_info_state, + ); } pub fn remove_output(&mut self, output: &Output, seats: impl Iterator>) { - if let Some(first_output) = self.outputs.get(0) { - for seat in seats { - if &seat.active_output() == output { - seat.set_active_output(first_output); - } - } - } - - if !self.outputs.contains(output) { - return; - } - - { - let map = layer_map_for_output(output); - for surface in map.layers() { - surface.layer_surface().send_close(); - } - } - - let mut state = self.workspace_state.update(); - self.outputs.retain(|o| o != output); - - match &mut self.workspaces { - WorkspaceMode::OutputBound(sets, _) => { - // TODO: - // If amount::static merge them instead of appending - - if let Some(set) = sets.remove(output) { - // TODO: Heuristic which output to move to. - // It is supposed to be the *most* internal, we just pick the first one for now - // and hope enumeration order works in our favor. - if let Some(new_output) = self.outputs.get(0) { - let new_set = sets.get_mut(new_output).unwrap(); - let workspace_group = new_set.group; - for mut workspace in set.workspaces { - // update workspace protocol state - state.remove_workspace(workspace.handle); - let workspace_handle = - state.create_workspace(&workspace_group).unwrap(); - state.set_workspace_capabilities( - &workspace_handle, - [WorkspaceCapabilities::Activate].into_iter(), - ); - workspace.handle = workspace_handle; - for window in workspace.mapped() { - for (surface, _) in window.windows() { - self.toplevel_info_state - .toplevel_enter_workspace(&surface, &workspace.handle); - } - } - - // update mapping - workspace.map_output(new_output, (0, 0).into()); - workspace.unmap_output(output, &mut self.toplevel_info_state); - workspace.refresh(); - - new_set.workspaces.push(workspace); - } - state.remove_workspace_group(set.group); - } - // if there is no output, we are going to quit anyway, just drop the workspace set - } - for (i, set) in sets.values_mut().enumerate() { - set.update_idx(&mut state, i); - } - std::mem::drop(state); + self.workspaces.remove_output( + output, + seats, + &mut self.workspace_state.update(), + &mut self.toplevel_info_state, + ); self.refresh(); // cleans up excess of workspaces and empty workspaces } - WorkspaceMode::Global(set) => { - state.remove_group_output(&set.group, output); - for workspace in &mut set.workspaces { - workspace.unmap_output(output, &mut self.toplevel_info_state); - workspace.refresh(); - } - } - }; - } - - pub fn refresh_outputs(&mut self) { - if let WorkspaceMode::Global(set) = &mut self.workspaces { - for workspace in &mut set.workspaces { - for output in self.outputs.iter() { - workspace.map_output( - output, - output - .user_data() - .get::>() - .unwrap() - .borrow() - .position - .into(), - ); - } - } - } - } + /* pub fn set_mode(&mut self, mode: ConfigMode) { let mut state = self.workspace_state.update(); @@ -1005,34 +1043,39 @@ impl Shell { std::mem::drop(state); self.refresh(); // get rid of empty workspaces and enforce potential maximum } + */ pub fn activate( &mut self, output: &Output, idx: usize, - ) -> Result>, InvalidWorkspaceIndex> { - if match &mut self.workspaces { - WorkspaceMode::OutputBound(sets, _) => { - if let Some(set) = sets.get_mut(output) { + ) -> Result>, InvalidWorkspaceIndex> { + match &mut self.workspaces.mode { + WorkspaceMode::OutputBound => { + if let Some(set) = self.workspaces.sets.get_mut(output) { if matches!( self.overview_mode, OverviewMode::Started(Trigger::Pointer(_), _) ) { - set.workspaces[set.active].tiling_layer.cleanup_drag(output); + set.workspaces[set.active].tiling_layer.cleanup_drag(); } - set.activate(idx, &mut self.workspace_state.update())? - } else { - false - } - } - WorkspaceMode::Global(set) => set.activate(idx, &mut self.workspace_state.update())?, - } { + set.activate(idx, &mut self.workspace_state.update())?; + let output_geo = output.geometry(); Ok(Some( - output_geo.loc + Point::from((output_geo.size.w / 2, output_geo.size.h / 2)), + output_geo.loc + + Point::from((output_geo.size.w / 2, output_geo.size.h / 2)), )) } else { Ok(None) + } + } + WorkspaceMode::Global => { + for set in self.workspaces.sets.values_mut() { + set.activate(idx, &mut self.workspace_state.update())?; + } + Ok(None) + } } } @@ -1049,8 +1092,7 @@ impl Shell { surface: &'a WlSurface, ) -> impl Iterator + 'a { match self - .outputs - .iter() + .outputs() .find(|o| { let map = layer_map_for_output(o); map.layer_for_surface(surface, WindowSurfaceType::ALL) @@ -1086,8 +1128,8 @@ impl Shell { .chain(self.outputs().map(|o| self.active_space(o)).flat_map(|w| { w.mapped() .find(|e| e.has_surface(surface, WindowSurfaceType::ALL)) + .map(|_| w.output().clone()) .into_iter() - .flat_map(|e| w.outputs_for_element(e)) })), ), } @@ -1097,7 +1139,7 @@ impl Shell { &self, surface: &WlSurface, ) -> impl Iterator { - match self.outputs.iter().find(|o| { + match self.outputs().find(|o| { let map = layer_map_for_output(o); map.layer_for_surface(surface, WindowSurfaceType::ALL) .is_some() @@ -1105,31 +1147,16 @@ impl Shell { Some(output) => self .workspaces .spaces() - .filter(move |workspace| { - workspace - .floating_layer - .space - .outputs() - .any(|o| o == output) - }) - .map(|w| (w.handle.clone(), output.clone())) - .collect::>(), + .find(move |workspace| workspace.output() == output) + .map(|w| (w.handle.clone(), output.clone())), None => self .workspaces .spaces() - .filter_map(|w| { - if let Some(mapped) = w - .mapped() - .find(|e| e.has_surface(surface, WindowSurfaceType::ALL)) - { - let outputs = w.outputs_for_element(mapped); - Some(std::iter::repeat(w.handle.clone()).zip(outputs).fuse()) - } else { - None - } + .find(|w| { + w.mapped() + .any(|e| e.has_surface(surface, WindowSurfaceType::ALL)) }) - .flatten() - .collect::>(), + .map(|w| (w.handle.clone(), w.output().clone())), } .into_iter() } @@ -1166,44 +1193,34 @@ impl Shell { self.workspaces.spaces_mut().find(|w| &w.handle == handle) } - pub fn outputs(&self) -> impl Iterator { - self.outputs.iter() + pub fn outputs(&self) -> impl DoubleEndedIterator { + self.workspaces.sets.keys().chain( + self.workspaces + .backup_set + .as_ref() + .into_iter() + .map(|set| &set.output), + ) } - pub fn global_space(&self) -> Rectangle { - self.outputs - .iter() + pub fn global_space(&self) -> Rectangle { + self.outputs() .fold( - Option::>::None, + Option::>::None, |maybe_geo, output| match maybe_geo { Some(rect) => Some(rect.merge(output.geometry())), None => Some(output.geometry()), }, ) - .unwrap_or_else(|| Rectangle::from_loc_and_size((0, 0), (0, 0))) - } - - pub fn map_global_to_space( - &self, - global_loc: impl Into>, - output: &Output, - ) -> Point { - match self.workspaces { - WorkspaceMode::Global(_) => global_loc.into(), - WorkspaceMode::OutputBound(_, _) => { - let p = global_loc.into().to_f64() - output.current_location().to_f64(); - (C::from_f64(p.x), C::from_f64(p.y)).into() - } - } + .unwrap_or_else(Rectangle::default) } pub fn animations_going(&self) -> bool { - (match &self.workspaces { - WorkspaceMode::Global(set) => set.previously_active.is_some(), - WorkspaceMode::OutputBound(sets, _) => { - sets.values().any(|set| set.previously_active.is_some()) - } - }) || !matches!(self.overview_mode, OverviewMode::None) + self.workspaces + .sets + .values() + .any(|set| set.previously_active.is_some()) + || !matches!(self.overview_mode, OverviewMode::None) || !matches!(self.resize_mode, ResizeMode::None) || self .workspaces @@ -1301,24 +1318,12 @@ impl Shell { self.popups.cleanup(); - match &mut self.workspaces { - WorkspaceMode::OutputBound(sets, _) => { - for (output, set) in sets.iter_mut() { - set.refresh( - &mut self.workspace_state, + self.workspaces.refresh( + &mut self.workspace_state.update(), &mut self.toplevel_info_state, - std::iter::once((output, (0, 0).into())), - ); - } - } - WorkspaceMode::Global(set) => set.refresh( - &mut self.workspace_state, - &mut self.toplevel_info_state, - self.outputs.iter().map(|o| (o, o.current_location())), - ), - } + ); - for output in &self.outputs { + for output in self.outputs() { let mut map = layer_map_for_output(output); map.cleanup(); } @@ -1365,12 +1370,12 @@ impl Shell { mapped.set_debug(state.common.egui.active); } if layout::should_be_floating(&window) || !workspace.tiling_enabled { - workspace.floating_layer.map(mapped.clone(), &seat, None); + workspace.floating_layer.map(mapped.clone(), None); } else { let focus_stack = workspace.focus_stack.get(&seat); workspace .tiling_layer - .map(mapped.clone(), &seat, focus_stack.iter(), None); + .map(mapped.clone(), focus_stack.iter(), None); } if let CosmicSurface::X11(surface) = window { @@ -1431,7 +1436,7 @@ impl Shell { map.map_layer(&layer_surface).unwrap(); } for workspace in state.common.shell.workspaces.spaces_mut() { - workspace.tiling_layer.recalculate(&output); + workspace.tiling_layer.recalculate(); } if wants_focus { diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 369f1a4b..4daee1f8 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -76,6 +76,7 @@ const FULLSCREEN_ANIMATION_DURATION: Duration = Duration::from_millis(200); #[derive(Debug)] pub struct Workspace { + pub output: Output, pub tiling_layer: TilingLayout, pub floating_layer: FloatingLayout, pub tiling_enabled: bool, @@ -84,6 +85,7 @@ pub struct Workspace { pub focus_stack: FocusStacks, pub pending_buffers: Vec<(ScreencopySession, BufferParams)>, pub screencopy_sessions: Vec, + pub output_stack: VecDeque, pub(super) backdrop_id: Id, pub dirty: AtomicBool, } @@ -198,16 +200,26 @@ impl MoveResult { } impl Workspace { - pub fn new(handle: WorkspaceHandle, tiling_enabled: bool, gaps: (u8, u8)) -> Workspace { + pub fn new( + handle: WorkspaceHandle, + output: Output, + tiling_enabled: bool, + gaps: (u8, u8), + ) -> Workspace { + let tiling_layer = TilingLayout::new(gaps, &output); + let floating_layer = FloatingLayout::new(&output); + Workspace { - tiling_layer: TilingLayout::new(gaps), - floating_layer: FloatingLayout::new(), + output, + tiling_layer, + floating_layer, tiling_enabled, fullscreen: HashMap::new(), handle, focus_stack: FocusStacks::default(), pending_buffers: Vec::new(), screencopy_sessions: Vec::new(), + output_stack: VecDeque::new(), backdrop_id: Id::new(), dirty: AtomicBool::new(false), } @@ -299,27 +311,29 @@ impl Workspace { } } - pub fn map_output(&mut self, output: &Output, position: Point) { - self.tiling_layer.map_output(output, position); - self.floating_layer.map_output(output, position); + pub fn output(&self) -> &Output { + &self.output } - pub fn unmap_output( + pub fn set_output( &mut self, output: &Output, toplevel_info: &mut ToplevelInfoState, ) { - if let Some(dead_output_fullscreen) = self.fullscreen.remove(output) { - self.unfullscreen_request(&dead_output_fullscreen.window.surface()); + self.tiling_layer.set_output(output); + self.floating_layer.set_output(output); + for mapped in self.mapped() { + for (surface, _) in mapped.windows() { + toplevel_info.toplevel_leave_output(&surface, &self.output); + toplevel_info.toplevel_enter_output(&surface, output); + } } - self.tiling_layer.unmap_output(output, toplevel_info); - self.floating_layer.unmap_output(output, toplevel_info); - self.refresh(); + self.output = output.clone(); } pub fn unmap(&mut self, mapped: &CosmicMapped) -> Option { let was_floating = self.floating_layer.unmap(&mapped); - let was_tiling = self.tiling_layer.unmap(&mapped).is_some(); + let was_tiling = self.tiling_layer.unmap(&mapped); if was_floating || was_tiling { assert!(was_floating != was_tiling); } @@ -363,67 +377,32 @@ impl Workspace { .find(|e| { e.windows() .any(|(w, _)| w.wl_surface().as_ref() == Some(surface)) - }) - } - - pub fn outputs_for_element(&self, elem: &CosmicMapped) -> impl Iterator { - self.floating_layer - .space - .outputs_for_element(elem) - .into_iter() - .chain(self.tiling_layer.output_for_element(elem).cloned()) - } - - pub fn output_under(&self, point: Point) -> Option<&Output> { - let space = &self.floating_layer.space; - space.outputs().find(|o| { - let internal_output_geo = space.output_geometry(o).unwrap(); - let external_output_geo = o.geometry(); - internal_output_geo.contains(point - external_output_geo.loc + internal_output_geo.loc) }) } pub fn element_under( &mut self, - location: Point, + location: Point, overview: OverviewMode, - ) -> Option<(PointerFocusTarget, Point)> { + ) -> Option<(PointerFocusTarget, Point)> { + let location = location.to_local(&self.output); self.floating_layer .space - .element_under(location) - .map(|(mapped, p)| (mapped.clone().into(), p)) + .element_under(location.as_logical()) + .map(|(mapped, p)| (mapped.clone().into(), p.as_local())) .or_else(|| self.tiling_layer.element_under(location, overview)) + .map(|(m, p)| (m, p.to_global(&self.output))) } - pub fn element_geometry(&self, elem: &CosmicMapped) -> Option> { - let space = &self.floating_layer.space; - let outputs = space.outputs().collect::>(); - let offset = if outputs.len() == 1 - && space.output_geometry(&outputs[0]).unwrap().loc == Point::from((0, 0)) - { - outputs[0].geometry().loc - } else { - (0, 0).into() - }; - + pub fn element_geometry(&self, elem: &CosmicMapped) -> Option> { self.floating_layer - .space .element_geometry(elem) .or_else(|| self.tiling_layer.element_geometry(elem)) - .map(|mut geo| { - geo.loc += offset; - geo - }) } - pub fn recalculate(&mut self, output: &Output) { - if let Some(f) = self.fullscreen.get(output) { - if !f.exclusive { - f.window - .set_geometry(layer_map_for_output(output).non_exclusive_zone()); - } - } - self.tiling_layer.recalculate(output); + pub fn recalculate(&mut self) { + self.tiling_layer.recalculate(); + self.floating_layer.refresh(); } pub fn maximize_request( @@ -574,7 +553,7 @@ impl Workspace { } } - result + Some(f.original_geometry.size.as_logical()) } else { None } @@ -730,7 +709,7 @@ impl Workspace { } let was_floating = self.floating_layer.unmap(&mapped); - let was_tiled = self.tiling_layer.unmap_as_placeholder(&mapped); + let was_tiled = dbg!(self.tiling_layer.unmap_as_placeholder(&mapped)); assert!(was_floating != was_tiled.is_some()); Some(MoveGrab::new( @@ -754,7 +733,7 @@ impl Workspace { .into_iter() { self.tiling_layer.unmap(&window); - self.floating_layer.map(window, seat, None); + self.floating_layer.map(window, None); } self.tiling_enabled = false; } else { @@ -767,8 +746,7 @@ impl Workspace { .into_iter() { self.floating_layer.unmap(&window); - self.tiling_layer - .map(window, seat, focus_stack.iter(), None) + self.tiling_layer.map(window, focus_stack.iter(), None) } self.tiling_enabled = true; } @@ -779,12 +757,11 @@ impl Workspace { if let Some(window) = self.focus_stack.get(seat).iter().next().cloned() { if self.tiling_layer.mapped().any(|(_, m, _)| m == &window) { self.tiling_layer.unmap(&window); - self.floating_layer.map(window, seat, None); + self.floating_layer.map(window, None); } else if self.floating_layer.mapped().any(|w| w == &window) { let focus_stack = self.focus_stack.get(seat); self.floating_layer.unmap(&window); - self.tiling_layer - .map(window, seat, focus_stack.iter(), None) + self.tiling_layer.map(window, focus_stack.iter(), None) } } } @@ -837,8 +814,8 @@ impl Workspace { pub fn node_desc(&self, focus: KeyboardFocusTarget) -> Option { match focus { KeyboardFocusTarget::Element(mapped) => { - self.tiling_layer.mapped().find_map(|(output, m, _)| { - (m == &mapped).then_some(output.clone()).and_then(|output| { + self.tiling_layer.mapped().find_map(|(_, m, _)| { + if m == &mapped { mapped .tiling_node_id .lock() @@ -846,7 +823,6 @@ impl Workspace { .clone() .map(|node_id| NodeDesc { handle: self.handle.clone(), - output: output.downgrade(), node: node_id, stack_window: if mapped .stack_ref() @@ -858,12 +834,13 @@ impl Workspace { None }, }) - }) + } else { + None + } }) } - KeyboardFocusTarget::Group(WindowGroup { output, node, .. }) => Some(NodeDesc { + KeyboardFocusTarget::Group(WindowGroup { node, .. }) => Some(NodeDesc { handle: self.handle.clone(), - output, node, stack_window: None, }), @@ -904,10 +881,9 @@ impl Workspace { } } - pub fn render_output<'a, R>( + pub fn render<'a, R>( &self, renderer: &mut R, - output: &Output, override_redirect_windows: &[X11Surface], xwm_state: Option<&'a mut XWaylandState>, draw_focus_indicator: Option<&Seat>, @@ -1089,9 +1065,8 @@ impl Workspace { OverviewMode::None => 1.0, }; - let (w_elements, p_elements) = self.floating_layer.render_output::( + let (w_elements, p_elements) = self.floating_layer.render::( renderer, - output, focused.as_ref(), resize_indicator.clone(), indicator_thickness, @@ -1113,11 +1088,10 @@ impl Workspace { }; //tiling surfaces - let (w_elements, p_elements) = self.tiling_layer.render_output::( + let (w_elements, p_elements) = self.tiling_layer.render::( renderer, - output, draw_focus_indicator, - layer_map.non_exclusive_zone(), + zone, overview, resize_indicator, indicator_thickness, diff --git a/src/state.rs b/src/state.rs index 8d4feeba..9149684d 100644 --- a/src/state.rs +++ b/src/state.rs @@ -511,50 +511,34 @@ impl Common { let active = self.shell.active_space(output); active.mapped().for_each(|mapped| { - let outputs_for_element: Vec<_> = active.outputs_for_element(mapped).collect(); - if outputs_for_element.contains(&output) { - let window = mapped.active_window(); - window.with_surfaces(|surface, states| { - let primary_scanout_output = update_surface_primary_scanout_output( - surface, - output, - states, - render_element_states, - |current_output, current_state, next_output, next_state| { - if outputs_for_element.contains(current_output) { - default_primary_scanout_output_compare( - current_output, - current_state, - next_output, - next_state, - ) - } else { - next_output - } - }, - ); - if let Some(output) = primary_scanout_output { - with_fractional_scale(states, |fraction_scale| { - fraction_scale - .set_preferred_scale(output.current_scale().fractional_scale()); - }); - } - }); - window.send_frame(output, time, throttle, surface_primary_scanout_output); - if let Some(feedback) = window - .wl_surface() - .and_then(|wl_surface| { - source_node_for_surface(&wl_surface, &self.display_handle) - }) - .and_then(|source| dmabuf_feedback(source)) - { - window.send_dmabuf_feedback( - output, - &feedback, - render_element_states, - surface_primary_scanout_output, - ); + let window = mapped.active_window(); + window.with_surfaces(|surface, states| { + let primary_scanout_output = update_surface_primary_scanout_output( + surface, + output, + states, + render_element_states, + |_current_output, _current_state, next_output, _next_state| next_output, + ); + if let Some(output) = primary_scanout_output { + with_fractional_scale(states, |fraction_scale| { + fraction_scale + .set_preferred_scale(output.current_scale().fractional_scale()); + }); } + }); + window.send_frame(output, time, throttle, surface_primary_scanout_output); + if let Some(feedback) = window + .wl_surface() + .and_then(|wl_surface| source_node_for_surface(&wl_surface, &self.display_handle)) + .and_then(|source| dmabuf_feedback(source)) + { + window.send_dmabuf_feedback( + output, + &feedback, + render_element_states, + surface_primary_scanout_output, + ); } }); @@ -565,10 +549,8 @@ impl Common { .filter(|w| w.handle != active.handle) { space.mapped().for_each(|mapped| { - if space.outputs_for_element(mapped).any(|o| &o == output) { - let window = mapped.active_window(); - window.send_frame(output, time, throttle, |_, _| None); - } + let window = mapped.active_window(); + window.send_frame(space.output(), time, throttle, |_, _| None); }); } diff --git a/src/wayland/handlers/compositor.rs b/src/wayland/handlers/compositor.rs index 3746d57e..0380e8e0 100644 --- a/src/wayland/handlers/compositor.rs +++ b/src/wayland/handlers/compositor.rs @@ -254,7 +254,7 @@ impl CompositorHandler for State { let changed = layer_map_for_output(&output).arrange(); if changed { for workspace in self.common.shell.workspaces.spaces_mut() { - workspace.recalculate(&output); + workspace.recalculate(); } } } diff --git a/src/wayland/handlers/layer_shell.rs b/src/wayland/handlers/layer_shell.rs index f2726921..70acd77a 100644 --- a/src/wayland/handlers/layer_shell.rs +++ b/src/wayland/handlers/layer_shell.rs @@ -41,8 +41,7 @@ impl WlrLayerShellHandler for State { } fn new_popup(&mut self, _parent: WlrLayerSurface, popup: PopupSurface) { - let positioner = popup.with_pending_state(|state| state.positioner); - self.common.shell.unconstrain_popup(&popup, &positioner); + self.common.shell.unconstrain_popup(&popup); if popup.send_configure().is_ok() { self.common @@ -76,7 +75,7 @@ impl WlrLayerShellHandler for State { } for workspace in self.common.shell.workspaces.spaces_mut() { - workspace.recalculate(&output); + workspace.recalculate(); } // collect screencopy sessions needing an update diff --git a/src/wayland/handlers/screencopy.rs b/src/wayland/handlers/screencopy.rs index f8a8cfdb..1aeffcb7 100644 --- a/src/wayland/handlers/screencopy.rs +++ b/src/wayland/handlers/screencopy.rs @@ -1296,7 +1296,7 @@ pub fn schedule_offscreen_workspace_session( if !session.alive() { return; } - if !state.common.shell.outputs.contains(&output) { + if !state.common.shell.outputs().any(|o| o == &output) { return; } match render_workspace_to_buffer( diff --git a/src/wayland/handlers/workspace.rs b/src/wayland/handlers/workspace.rs index 56b268b7..dee16c74 100644 --- a/src/wayland/handlers/workspace.rs +++ b/src/wayland/handlers/workspace.rs @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only use crate::{ - shell::WorkspaceMode, state::ClientState, utils::prelude::*, wayland::protocols::workspace::{ @@ -30,19 +29,12 @@ impl WorkspaceHandler for State { for request in requests.into_iter() { match request { Request::Activate(handle) => { - let maybe = match &self.common.shell.workspaces { - WorkspaceMode::Global(set) => set - .workspaces + let maybe = self.common.shell.workspaces.iter().find_map(|(o, set)| { + set.workspaces .iter() .position(|w| w.handle == handle) - .map(|i| (self.common.last_active_seat().active_output(), i)), - WorkspaceMode::OutputBound(sets, _) => sets.iter().find_map(|(o, set)| { - set.workspaces - .iter() - .position(|w| w.handle == handle) - .map(|i| (o.clone(), i)) - }), - }; + .map(|i| (o.clone(), i)) + }); if let Some((output, idx)) = maybe { let _ = self.common.shell.activate(&output, idx); // TODO: move cursor? diff --git a/src/wayland/handlers/xdg_shell/mod.rs b/src/wayland/handlers/xdg_shell/mod.rs index ec0ec87f..84c4d4a2 100644 --- a/src/wayland/handlers/xdg_shell/mod.rs +++ b/src/wayland/handlers/xdg_shell/mod.rs @@ -50,7 +50,7 @@ impl XdgShellHandler for State { if surface.get_parent_surface().is_some() { // let other shells deal with their popups - self.common.shell.unconstrain_popup(&surface, &positioner); + self.common.shell.unconstrain_popup(&surface); if surface.send_configure().is_ok() { self.common @@ -123,7 +123,7 @@ impl XdgShellHandler for State { state.positioner = positioner; }); - self.common.shell.unconstrain_popup(&surface, &positioner); + self.common.shell.unconstrain_popup(&surface); surface.send_repositioned(token); if let Err(err) = surface.send_configure() { warn!( @@ -150,9 +150,6 @@ impl XdgShellHandler for State { } fn maximize_request(&mut self, surface: ToplevelSurface) { - let seat = self.common.last_active_seat(); - let output = seat.active_output(); - if let Some(mapped) = self .common .shell @@ -164,7 +161,7 @@ impl XdgShellHandler for State { .windows() .find(|(w, _)| w.wl_surface().as_ref() == Some(surface.wl_surface())) .unwrap(); - workspace.maximize_request(&window, &output, self.common.event_loop_handle.clone()) + workspace.maximize_request(&window) } } } @@ -194,6 +191,7 @@ impl XdgShellHandler for State { let seat = self.common.last_active_seat(); seat.active_output() }); + // TODO: If this is not the output? Do we move it? if let Some(mapped) = self .common @@ -206,11 +204,7 @@ impl XdgShellHandler for State { .windows() .find(|(w, _)| w.wl_surface().as_ref() == Some(surface.wl_surface())) .unwrap(); - workspace.fullscreen_request( - &window, - &output, - self.common.event_loop_handle.clone(), - ) + workspace.fullscreen_request(&window) } } } diff --git a/src/xwayland.rs b/src/xwayland.rs index b5dce66b..0d5cdc33 100644 --- a/src/xwayland.rs +++ b/src/xwayland.rs @@ -363,14 +363,11 @@ impl XwmHandler for State { } fn maximize_request(&mut self, _xwm: XwmId, window: X11Surface) { - let seat = self.common.last_active_seat(); - let output = seat.active_output(); let surface = CosmicSurface::X11(window); - if let Some(mapped) = self.common.shell.element_for_surface(&surface).cloned() { if let Some(workspace) = self.common.shell.space_for_mut(&mapped) { let (window, _) = mapped.windows().find(|(w, _)| w == &surface).unwrap(); - workspace.maximize_request(&window, &output, self.common.event_loop_handle.clone()) + workspace.maximize_request(&window); } } } @@ -386,18 +383,10 @@ impl XwmHandler for State { } fn fullscreen_request(&mut self, _xwm: XwmId, window: X11Surface) { - let seat = self.common.last_active_seat(); - let output = seat.active_output(); let surface = CosmicSurface::X11(window); - if let Some(mapped) = self.common.shell.element_for_surface(&surface).cloned() { if let Some(workspace) = self.common.shell.space_for_mut(&mapped) { - let (window, _) = mapped.windows().find(|(w, _)| w == &surface).unwrap(); - workspace.fullscreen_request( - &window, - &output, - self.common.event_loop_handle.clone(), - ) + workspace.fullscreen_request(&surface) } } }