From cf7980fe6ce49db37f25951283e8db0008a791c3 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Fri, 29 Sep 2023 19:19:58 +0200 Subject: [PATCH] WIP --- src/backend/kms/mod.rs | 5 +- src/backend/render/mod.rs | 50 +- src/backend/winit.rs | 1 - src/backend/x11.rs | 1 - src/input/mod.rs | 284 ++- src/shell/element/mod.rs | 39 +- src/shell/element/stack.rs | 10 +- src/shell/element/surface.rs | 7 +- src/shell/element/window.rs | 37 +- src/shell/focus/mod.rs | 42 +- src/shell/focus/target.rs | 23 +- src/shell/grabs/moving.rs | 101 +- src/shell/layout/floating/grabs/resize.rs | 22 +- src/shell/layout/floating/mod.rs | 347 ++-- src/shell/layout/tiling/grabs/resize.rs | 109 +- src/shell/layout/tiling/grabs/swap.rs | 3 +- src/shell/layout/tiling/mod.rs | 2140 +++++++++------------ src/shell/mod.rs | 903 +++++---- src/shell/workspace.rs | 538 +++--- src/state.rs | 76 +- src/utils/geometry.rs | 149 ++ src/utils/mod.rs | 1 + src/utils/prelude.rs | 8 +- src/wayland/handlers/compositor.rs | 2 +- src/wayland/handlers/layer_shell.rs | 5 +- src/wayland/handlers/screencopy.rs | 6 +- src/wayland/handlers/workspace.rs | 16 +- src/wayland/handlers/xdg_shell/mod.rs | 16 +- src/wayland/handlers/xdg_shell/popup.rs | 37 +- src/xwayland.rs | 36 +- 30 files changed, 2332 insertions(+), 2682 deletions(-) create mode 100644 src/utils/geometry.rs diff --git a/src/backend/kms/mod.rs b/src/backend/kms/mod.rs index 6f8f68b8..a5997ee2 100644 --- a/src/backend/kms/mod.rs +++ b/src/backend/kms/mod.rs @@ -965,8 +965,8 @@ fn render_node_for_output( ) -> DrmNode { let workspace = shell.active_space(output); let nodes = workspace - .get_fullscreen(output) - .map(|w| vec![w.surface()]) + .get_fullscreen() + .map(|w| vec![w.clone()]) .unwrap_or_else(|| workspace.windows().collect::>()) .into_iter() .flat_map(|w| w.wl_surface().and_then(|s| source_node_for_surface(&s, dh))) @@ -1379,7 +1379,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/render/mod.rs b/src/backend/render/mod.rs index 75cead8a..725c6b9e 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -17,7 +17,7 @@ use crate::{ CosmicMapped, CosmicMappedRenderElement, OverviewMode, Trigger, WorkspaceRenderElement, }, state::{Common, Fps}, - utils::prelude::{OutputExt, SeatExt}, + utils::prelude::*, wayland::{ handlers::{ data_device::get_dnd_icon, @@ -160,7 +160,7 @@ impl IndicatorShader { pub fn focus_element( renderer: &R, key: impl Into, - mut element_geo: Rectangle, + mut element_geo: Rectangle, thickness: u8, scale: f64, alpha: f32, @@ -184,7 +184,7 @@ impl IndicatorShader { pub fn element( renderer: &R, key: impl Into, - geo: Rectangle, + geo: Rectangle, thickness: u8, radius: u8, alpha: f32, @@ -223,7 +223,7 @@ impl IndicatorShader { let elem = PixelShaderElement::new( shader, - geo, + geo.as_logical(), None, //TODO alpha, vec![ @@ -240,8 +240,8 @@ impl IndicatorShader { } let elem = &mut cache.get_mut(&key).unwrap().1; - if elem.geometry(1.0.into()).to_logical(1) != geo { - elem.resize(geo, None); + if elem.geometry(1.0.into()).to_logical(1) != geo.as_logical() { + elem.resize(geo.as_logical(), None); } elem.clone() } @@ -271,7 +271,7 @@ impl BackdropShader { pub fn element( renderer: &R, key: impl Into, - geo: Rectangle, + geo: Rectangle, radius: f32, alpha: f32, color: [f32; 3], @@ -304,7 +304,7 @@ impl BackdropShader { let elem = PixelShaderElement::new( shader, - geo, + geo.as_logical(), None, // TODO alpha, vec![ @@ -320,8 +320,8 @@ impl BackdropShader { } let elem = &mut cache.get_mut(&key).unwrap().1; - if elem.geometry(1.0.into()).to_logical(1) != geo { - elem.resize(geo, None); + if elem.geometry(1.0.into()).to_logical(1) != geo.as_logical() { + elem.resize(geo.as_logical(), None); } elem.clone() } @@ -492,15 +492,11 @@ where let (resize_mode, resize_indicator) = state.shell.resize_mode(); let resize_indicator = resize_indicator.map(|indicator| (resize_mode, indicator)); let swap_tree = if let OverviewMode::Started(Trigger::KeyboardSwap(_, desc), _) = &overview.0 { - if let Some(desc_output) = desc.output.upgrade() { - if output != &desc_output || current.0 != desc.handle { - state - .shell - .space_for_handle(&desc.handle) - .and_then(|w| w.tiling_layer.tree_for_output(&desc_output)) - } else { - None - } + if current.0 != desc.handle { + state + .shell + .space_for_handle(&desc.handle) + .map(|w| w.tiling_layer.tree()) } else { None } @@ -530,9 +526,9 @@ where let has_fullscreen = workspace .fullscreen - .get(output) + .as_ref() .filter(|f| !f.is_animating()) - .map(|f| f.exclusive); + .is_some(); let (overlay_elements, overlay_popups) = split_layer_elements(renderer, output, Layer::Overlay, exclude_workspace_overview); @@ -540,7 +536,7 @@ where elements.extend(overlay_popups.into_iter().map(Into::into)); elements.extend(overlay_elements.into_iter().map(Into::into)); - let mut window_elements = if !has_fullscreen.unwrap_or(false) { + let mut window_elements = if !has_fullscreen { let (top_elements, top_popups) = split_layer_elements(renderer, output, Layer::Top, exclude_workspace_overview); elements.extend(top_popups.into_iter().map(Into::into)); @@ -557,7 +553,7 @@ where .shell .space_for_handle(&previous) .ok_or(OutputNoMode)?; - let has_fullscreen = workspace.fullscreen.contains_key(output); + let has_fullscreen = workspace.fullscreen.is_some(); let is_active_space = workspace.outputs().any(|o| o == &active_output); let percentage = { @@ -581,9 +577,8 @@ where }); let (w_elements, p_elements) = workspace - .render_output::( + .render::( renderer, - output, &state.shell.override_redirect_windows, state.xwayland_state.as_mut(), (!move_active && is_active_space).then_some(&last_active_seat), @@ -639,9 +634,8 @@ where let is_active_space = workspace.outputs().any(|o| o == &active_output); let (w_elements, p_elements) = workspace - .render_output::( + .render::( renderer, - output, &state.shell.override_redirect_windows, state.xwayland_state.as_mut(), (!move_active && is_active_space).then_some(&last_active_seat), @@ -665,7 +659,7 @@ where )) })); - if has_fullscreen.is_none() { + if !has_fullscreen { let (w_elements, p_elements) = background_layer_elements(renderer, output, exclude_workspace_overview); 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 80764218..97198ffe 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -39,7 +39,7 @@ use smithay::{ input::event::pointer::PointerAxisEvent as LibinputPointerAxisEvent, wayland_server::DisplayHandle, }, - utils::{Logical, Point, Rectangle, Serial, SERIAL_COUNTER}, + utils::{Point, Serial, SERIAL_COUNTER}, wayland::{ keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitorSeat, pointer_constraints::{with_pointer_constraint, PointerConstraint}, @@ -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); @@ -553,24 +553,18 @@ impl State { if let Some(seat) = self.common.seat_with_device(&event.device()).cloned() { let current_output = seat.active_output(); - let mut position = seat.get_pointer().unwrap().current_location(); + let mut position = seat.get_pointer().unwrap().current_location().as_global(); - let relative_pos = self - .common - .shell - .map_global_to_space(position, ¤t_output); let overview = self.common.shell.overview_mode(); - let output_geometry = current_output.geometry(); let workspace = self.common.shell.workspaces.active_mut(¤t_output); let under = State::surface_under( position, - relative_pos, ¤t_output, - output_geometry, &self.common.shell.override_redirect_windows, overview.0.clone(), workspace, - ); + ) + .map(|(target, pos)| (target, pos.as_logical())); let ptr = seat.get_pointer().unwrap(); @@ -618,7 +612,7 @@ impl State { return; } - position += event.delta(); + position += event.delta().as_global(); let output = self .common @@ -627,18 +621,17 @@ impl State { .find(|output| output.geometry().to_f64().contains(position)) .cloned() .unwrap_or(current_output.clone()); + let output_geometry = output.geometry(); let workspace = self.common.shell.workspaces.active_mut(&output); - let output_geometry = output.geometry(); let new_under = State::surface_under( position, - relative_pos, &output, - output_geometry, &self.common.shell.override_redirect_windows, overview.0, workspace, - ); + ) + .map(|(target, pos)| (target, pos.as_logical())); position.x = position.x.clamp( output_geometry.loc.x as f64, @@ -660,13 +653,17 @@ impl State { } if let PointerFocusTarget::Element(element) = surface { //if !element.is_in_input_region(&(position.to_i32_round() - *surface_loc).to_f64()) { - if !element.is_in_input_region(&(position - surface_loc.to_f64())) { + if !element.is_in_input_region( + &(position.as_logical() - surface_loc.to_f64()), + ) { ptr.frame(self); return; } } if let Some(region) = confine_region { - if !region.contains(position.to_i32_round() - *surface_loc) { + if !region + .contains(position.as_logical().to_i32_round() - *surface_loc) + { ptr.frame(self); return; } @@ -679,7 +676,7 @@ impl State { self, under, &MotionEvent { - location: position, + location: position.as_logical(), serial, time: event.time_msec(), }, @@ -720,17 +717,16 @@ impl State { for session in sessions_for_output(&self.common, &output) { if let Some((geometry, offset)) = seat.cursor_geometry( - position.to_buffer( + position.as_logical().to_buffer( output.current_scale().fractional_scale(), output.current_transform(), - &output.geometry().size.to_f64(), + &output_geometry.size.to_f64().as_logical(), ), self.common.clock.now(), ) { session.cursor_info(&seat, InputType::Pointer, geometry, offset); } } - #[cfg(feature = "debug")] if self.common.seats().position(|x| x == &seat).unwrap() == 0 { let location = if let Some(output) = self.common.shell.outputs.first() { @@ -752,28 +748,27 @@ impl State { let position = geometry.loc.to_f64() + smithay::backend::input::AbsolutePositionEvent::position_transformed( &event, - geometry.size, - ); - let relative_pos = self.common.shell.map_global_to_space(position, &output); + geometry.size.as_logical(), + ) + .as_global(); let overview = self.common.shell.overview_mode(); let workspace = self.common.shell.workspaces.active_mut(&output); let serial = SERIAL_COUNTER.next_serial(); let under = State::surface_under( position, - relative_pos, &output, - geometry, &self.common.shell.override_redirect_windows, overview.0, workspace, - ); + ) + .map(|(target, pos)| (target, pos.as_logical())); for session in sessions_for_output(&self.common, &output) { if let Some((geometry, offset)) = seat.cursor_geometry( - position.to_buffer( + position.as_logical().to_buffer( output.current_scale().fractional_scale(), output.current_transform(), - &output.geometry().size.to_f64(), + &geometry.size.to_f64().as_logical(), ), self.common.clock.now(), ) { @@ -785,7 +780,7 @@ impl State { self, under, &MotionEvent { - location: position, + location: position.as_logical(), serial, time: event.time_msec(), }, @@ -835,22 +830,22 @@ impl State { && !seat.get_keyboard().map(|k| k.is_grabbed()).unwrap_or(false) { let output = seat.active_output(); - let pos = seat.get_pointer().unwrap().current_location(); - let relative_pos = self.common.shell.map_global_to_space(pos, &output); + let pos = seat.get_pointer().unwrap().current_location().as_global(); + let relative_pos = pos.to_local(&output); let overview = self.common.shell.overview_mode(); let workspace = self.common.shell.active_space_mut(&output); let mut under = None; - if let Some(window) = workspace.get_fullscreen(&output) { + if let Some(window) = workspace.get_fullscreen() { let layers = layer_map_for_output(&output); if let Some(layer) = - layers.layer_under(WlrLayer::Overlay, relative_pos) + layers.layer_under(WlrLayer::Overlay, relative_pos.as_logical()) { let layer_loc = layers.layer_geometry(layer).unwrap().loc; if layer.can_receive_keyboard_focus() && layer .surface_under( - relative_pos - layer_loc.to_f64(), + relative_pos.as_logical() - layer_loc.to_f64(), WindowSurfaceType::ALL, ) .is_some() @@ -864,14 +859,19 @@ impl State { let done = { let layers = layer_map_for_output(&output); if let Some(layer) = layers - .layer_under(WlrLayer::Overlay, relative_pos) - .or_else(|| layers.layer_under(WlrLayer::Top, relative_pos)) + .layer_under(WlrLayer::Overlay, relative_pos.as_logical()) + .or_else(|| { + layers.layer_under( + WlrLayer::Top, + relative_pos.as_logical(), + ) + }) { let layer_loc = layers.layer_geometry(layer).unwrap().loc; if layer.can_receive_keyboard_focus() && layer .surface_under( - relative_pos - layer_loc.to_f64(), + relative_pos.as_logical() - layer_loc.to_f64(), WindowSurfaceType::ALL, ) .is_some() @@ -884,35 +884,38 @@ impl State { } }; if !done { - if let Some(surface) = workspace.get_maximized(&output) { - under = Some(surface.clone().into()); + if let Some((target, _)) = + workspace.element_under(pos, overview.0) + { + under = Some(target); } else { - if let Some((target, _)) = - workspace.element_under(relative_pos, overview.0) + let layers = layer_map_for_output(&output); + if let Some(layer) = layers + .layer_under( + WlrLayer::Bottom, + relative_pos.as_logical(), + ) + .or_else(|| { + layers.layer_under( + WlrLayer::Background, + relative_pos.as_logical(), + ) + }) { - under = Some(target); - } else { - let layers = layer_map_for_output(&output); - if let Some(layer) = layers - .layer_under(WlrLayer::Bottom, pos) - .or_else(|| { - layers.layer_under(WlrLayer::Background, pos) - }) + let layer_loc = + layers.layer_geometry(layer).unwrap().loc; + if layer.can_receive_keyboard_focus() + && layer + .surface_under( + relative_pos.as_logical() + - layer_loc.to_f64(), + WindowSurfaceType::ALL, + ) + .is_some() { - let layer_loc = - layers.layer_geometry(layer).unwrap().loc; - if layer.can_receive_keyboard_focus() - && layer - .surface_under( - relative_pos - layer_loc.to_f64(), - WindowSurfaceType::ALL, - ) - .is_some() - { - under = Some(layer.clone().into()); - } - }; - } + under = Some(layer.clone().into()); + } + }; } } } @@ -1330,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)) => { @@ -1349,7 +1351,7 @@ impl State { self, None, &MotionEvent { - location: new_pos.to_f64(), + location: new_pos.to_f64().as_logical(), serial, time, }, @@ -1366,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)) => { @@ -1386,7 +1387,7 @@ impl State { self, None, &MotionEvent { - location: new_pos.to_f64(), + location: new_pos.to_f64().as_logical(), serial, time, }, @@ -1403,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, @@ -1426,7 +1426,7 @@ impl State { self, None, &MotionEvent { - location: new_pos.to_f64(), + location: new_pos.to_f64().as_logical(), serial, time, }, @@ -1438,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, @@ -1462,7 +1461,7 @@ impl State { self, None, &MotionEvent { - location: new_pos.to_f64(), + location: new_pos.to_f64().as_logical(), serial, time, }, @@ -1540,9 +1539,6 @@ impl State { Action::Move(direction) => { let current_output = seat.active_output(); let workspace = self.common.shell.active_space_mut(¤t_output); - if workspace.get_fullscreen(¤t_output).is_some() { - return; // TODO, is this what we want? How do we indicate the switch? - } match workspace.move_current_element(direction, seat) { MoveResult::MoveFurther(_move_further) => { @@ -1603,7 +1599,7 @@ impl State { Action::SwapWindow => { let current_output = seat.active_output(); let workspace = self.common.shell.active_space_mut(¤t_output); - if workspace.get_fullscreen(¤t_output).is_some() { + if workspace.get_fullscreen().is_some() { return; // TODO, is this what we want? Maybe disengage fullscreen instead? } @@ -1625,11 +1621,7 @@ impl State { let focus_stack = workspace.focus_stack.get(seat); let focused_window = focus_stack.last(); if let Some(window) = focused_window.map(|f| f.active_window()) { - workspace.maximize_toggle( - &window, - ¤t_output, - self.common.event_loop_handle.clone(), - ); + workspace.maximize_toggle(&window); } } Action::Resizing(direction) => self.common.shell.set_resize_mode( @@ -1705,77 +1697,83 @@ impl State { } pub fn surface_under( - global_pos: Point, - relative_pos: Point, + global_pos: Point, output: &Output, - output_geo: Rectangle, override_redirect_windows: &[X11Surface], overview: OverviewMode, workspace: &mut Workspace, - ) -> Option<(PointerFocusTarget, Point)> { - if let Some(window) = workspace.get_fullscreen(output) { + ) -> Option<(PointerFocusTarget, Point)> { + let relative_pos = global_pos.to_local(output); + let output_geo = output.geometry(); + + if let Some(window) = workspace.get_fullscreen() { let layers = layer_map_for_output(output); - if let Some(layer) = layers.layer_under(WlrLayer::Overlay, relative_pos) { + if let Some(layer) = layers.layer_under(WlrLayer::Overlay, relative_pos.as_logical()) { let layer_loc = layers.layer_geometry(layer).unwrap().loc; if layer - .surface_under(relative_pos - layer_loc.to_f64(), WindowSurfaceType::ALL) + .surface_under( + relative_pos.as_logical() - layer_loc.to_f64(), + WindowSurfaceType::ALL, + ) .is_some() { - return Some((layer.clone().into(), output_geo.loc + layer_loc)); + return Some((layer.clone().into(), output_geo.loc + layer_loc.as_global())); } } - if let Some(or) = override_redirect_windows - .iter() - .find(|or| or.is_in_input_region(&(global_pos - or.geometry().loc.to_f64()))) - { - return Some((or.clone().into(), or.geometry().loc)); + if let Some(or) = override_redirect_windows.iter().find(|or| { + or.is_in_input_region(&(global_pos.as_logical() - or.geometry().loc.to_f64())) + }) { + return Some((or.clone().into(), or.geometry().loc.as_global())); } Some((window.clone().into(), output_geo.loc)) } else { { let layers = layer_map_for_output(output); if let Some(layer) = layers - .layer_under(WlrLayer::Overlay, relative_pos) - .or_else(|| layers.layer_under(WlrLayer::Top, relative_pos)) + .layer_under(WlrLayer::Overlay, relative_pos.as_logical()) + .or_else(|| layers.layer_under(WlrLayer::Top, relative_pos.as_logical())) { let layer_loc = layers.layer_geometry(layer).unwrap().loc; if layer - .surface_under(relative_pos - layer_loc.to_f64(), WindowSurfaceType::ALL) + .surface_under( + relative_pos.as_logical() - layer_loc.to_f64(), + WindowSurfaceType::ALL, + ) .is_some() { - return Some((layer.clone().into(), output_geo.loc + layer_loc)); + return Some(( + layer.clone().into(), + output_geo.loc + layer_loc.as_global(), + )); } } } - if let Some(or) = override_redirect_windows - .iter() - .find(|or| or.is_in_input_region(&(global_pos - or.geometry().loc.to_f64()))) - { - return Some((or.clone().into(), or.geometry().loc)); + if let Some(or) = override_redirect_windows.iter().find(|or| { + or.is_in_input_region(&(global_pos.as_logical() - or.geometry().loc.to_f64())) + }) { + return Some((or.clone().into(), or.geometry().loc.as_global())); } - if let Some(surface) = workspace.get_maximized(output) { - let offset = layer_map_for_output(output).non_exclusive_zone().loc; - return Some((surface.clone().into(), output_geo.loc + offset)); - } else { - if let Some((target, loc)) = workspace.element_under(relative_pos, overview) { - return Some((target, loc + (global_pos - relative_pos).to_i32_round())); - } + if let Some((target, loc)) = workspace.element_under(global_pos, overview) { + return Some((target, loc)); + } + { + let layers = layer_map_for_output(output); + if let Some(layer) = layers + .layer_under(WlrLayer::Bottom, relative_pos.as_logical()) + .or_else(|| layers.layer_under(WlrLayer::Background, relative_pos.as_logical())) { - let layers = layer_map_for_output(output); - if let Some(layer) = layers - .layer_under(WlrLayer::Bottom, relative_pos) - .or_else(|| layers.layer_under(WlrLayer::Background, relative_pos)) + let layer_loc = layers.layer_geometry(layer).unwrap().loc; + if layer + .surface_under( + relative_pos.as_logical() - layer_loc.to_f64(), + WindowSurfaceType::ALL, + ) + .is_some() { - let layer_loc = layers.layer_geometry(layer).unwrap().loc; - if layer - .surface_under( - relative_pos - layer_loc.to_f64(), - WindowSurfaceType::ALL, - ) - .is_some() - { - return Some((layer.clone().into(), output_geo.loc + layer_loc)); - } + return Some(( + layer.clone().into(), + output_geo.loc + layer_loc.as_global(), + )); } } } @@ -1786,7 +1784,7 @@ impl State { fn sessions_for_output(state: &Common, output: &Output) -> impl Iterator { let workspace = state.shell.active_space(&output); - let maybe_fullscreen = workspace.get_fullscreen(&output); + let maybe_fullscreen = workspace.get_fullscreen(); workspace .screencopy_sessions .iter() @@ -1795,7 +1793,7 @@ fn sessions_for_output(state: &Common, output: &Output) -> impl Iterator() { + if let Some(sessions) = w.user_data().get::() { Some( sessions .0 diff --git a/src/shell/element/mod.rs b/src/shell/element/mod.rs index 12157b2a..78df78ba 100644 --- a/src/shell/element/mod.rs +++ b/src/shell/element/mod.rs @@ -4,7 +4,7 @@ use crate::{ GlMultiError, GlMultiFrame, GlMultiRenderer, }, state::State, - utils::prelude::SeatExt, + utils::prelude::*, }; use calloop::LoopHandle; use id_tree::NodeId; @@ -74,7 +74,7 @@ use tracing::debug; use super::{ focus::FocusDirection, layout::{floating::ResizeState, tiling::NodeDesc}, - Direction, + Direction, ManagedLayer, }; space_elements! { @@ -84,18 +84,25 @@ space_elements! { Stack=CosmicStack, } +#[derive(Debug, Clone)] +pub struct MaximizedState { + pub original_geometry: Rectangle, + pub original_layer: ManagedLayer, +} + #[derive(Clone)] pub struct CosmicMapped { element: CosmicMappedInternal, // associated data last_cursor_position: Arc>>>, + pub maximized_state: Arc>>, //tiling pub tiling_node_id: Arc>>, //floating - pub(super) last_geometry: Arc>>>, pub(super) resize_state: Arc>>, + pub last_geometry: Arc>>>, #[cfg(feature = "debug")] debug: Arc>>, @@ -106,8 +113,10 @@ impl fmt::Debug for CosmicMapped { f.debug_struct("CosmicMapped") .field("element", &self.element) .field("last_cursor_position", &self.last_cursor_position) + .field("maximized_state", &self.maximized_state) .field("tiling_node_id", &self.tiling_node_id) .field("resize_state", &self.resize_state) + .field("last_geometry", &self.last_geometry) .finish() } } @@ -361,7 +370,7 @@ impl CosmicMapped { window.is_activated(pending) } - pub fn set_geometry(&self, geo: Rectangle) { + pub fn set_geometry(&self, geo: Rectangle) { match &self.element { CosmicMappedInternal::Stack(s) => s.set_geometry(geo), CosmicMappedInternal::Window(w) => w.set_geometry(geo), @@ -491,7 +500,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) => { @@ -501,11 +510,9 @@ impl CosmicMapped { let stack = CosmicStack::new(std::iter::once(surface), handle); if let Some(geo) = self.last_geometry.lock().unwrap().clone() { - stack.set_geometry(geo); - } - for (output, overlap) in outputs { - stack.output_enter(output, overlap); + stack.set_geometry(geo.to_global(&output)); } + stack.output_enter(output, overlap); stack.set_activate(activated); stack.active().send_configure(); stack.refresh(); @@ -519,7 +526,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); @@ -527,11 +534,9 @@ impl CosmicMapped { let window = CosmicWindow::new(surface, handle); if let Some(geo) = self.last_geometry.lock().unwrap().clone() { - window.set_geometry(geo); - } - for (output, overlap) in outputs { - window.output_enter(output, overlap); + window.set_geometry(geo.to_global(&output)); } + window.output_enter(output, overlap); window.set_activate(self.is_activated(true)); window.surface().send_configure(); window.refresh(); @@ -1067,9 +1072,10 @@ impl From for CosmicMapped { CosmicMapped { element: CosmicMappedInternal::Window(w), last_cursor_position: Arc::new(Mutex::new(HashMap::new())), + maximized_state: Arc::new(Mutex::new(None)), tiling_node_id: Arc::new(Mutex::new(None)), - last_geometry: Arc::new(Mutex::new(None)), resize_state: Arc::new(Mutex::new(None)), + last_geometry: Arc::new(Mutex::new(None)), #[cfg(feature = "debug")] debug: Arc::new(Mutex::new(None)), } @@ -1081,9 +1087,10 @@ impl From for CosmicMapped { CosmicMapped { element: CosmicMappedInternal::Stack(s), last_cursor_position: Arc::new(Mutex::new(HashMap::new())), + maximized_state: Arc::new(Mutex::new(None)), tiling_node_id: Arc::new(Mutex::new(None)), - last_geometry: Arc::new(Mutex::new(None)), resize_state: Arc::new(Mutex::new(None)), + last_geometry: Arc::new(Mutex::new(None)), #[cfg(feature = "debug")] debug: Arc::new(Mutex::new(None)), } diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index 04e333ec..54478b3a 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -5,7 +5,7 @@ use crate::{ }, state::State, utils::iced::{IcedElement, Program}, - utils::prelude::SeatExt, + utils::prelude::*, wayland::handlers::screencopy::ScreencopySessions, }; use calloop::LoopHandle; @@ -91,7 +91,7 @@ pub struct CosmicStackInternal { override_alive: Arc, last_seat: Arc, Serial)>>>, last_location: Arc, Serial, u32)>>>, - geometry: Arc>>>, + geometry: Arc>>>, mask: Arc>>, } @@ -406,7 +406,7 @@ impl CosmicStack { Point::from((0, TAB_HEIGHT)) } - pub fn set_geometry(&self, geo: Rectangle) { + pub fn set_geometry(&self, geo: Rectangle) { self.0.with_program(|p| { let loc = (geo.loc.x, geo.loc.y + TAB_HEIGHT); let size = (geo.size.w, geo.size.h - TAB_HEIGHT); @@ -1047,11 +1047,11 @@ impl PointerTarget for CosmicStack { if let Some(workspace) = data.common.shell.space_for(stack_mapped) { // TODO: Unify this somehow with Shell::move_request/Workspace::move_request let button = 0x110; // BTN_LEFT - let pos = event.location; + let pos = event.location.as_global(); let start_data = PointerGrabStartData { focus: None, button, - location: pos, + location: pos.as_logical(), }; let mapped = CosmicMapped::from(CosmicWindow::new( surface, diff --git a/src/shell/element/surface.rs b/src/shell/element/surface.rs index 67b88b1b..c427f1b9 100644 --- a/src/shell/element/surface.rs +++ b/src/shell/element/surface.rs @@ -51,6 +51,7 @@ use smithay::{ use crate::{ state::{State, SurfaceDmabufFeedback}, + utils::prelude::*, wayland::handlers::decoration::PreferredDecorationMode, }; @@ -122,13 +123,13 @@ impl CosmicSurface { } } - pub fn set_geometry(&self, geo: Rectangle) { + pub fn set_geometry(&self, geo: Rectangle) { match self { CosmicSurface::Wayland(window) => window .toplevel() - .with_pending_state(|state| state.size = Some(geo.size)), + .with_pending_state(|state| state.size = Some(geo.size.as_logical())), CosmicSurface::X11(surface) => { - let _ = surface.configure(geo); + let _ = surface.configure(geo.as_logical()); } _ => {} } diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index 67856dd1..1e45ed0b 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -3,7 +3,7 @@ use crate::{ state::State, utils::{ iced::{IcedElement, Program}, - prelude::SeatExt, + prelude::*, }, wayland::handlers::screencopy::ScreencopySessions, }; @@ -132,7 +132,7 @@ impl CosmicWindow { )) } - pub fn set_geometry(&self, geo: Rectangle) { + pub fn set_geometry(&self, geo: Rectangle) { self.0.with_program(|p| { let loc = ( geo.loc.x, @@ -241,27 +241,20 @@ impl Program for CosmicWindowInternal { } } Message::Maximize => { - if let Some((seat, _serial)) = self.last_seat.lock().unwrap().clone() { - if let Some(surface) = self.window.wl_surface() { - loop_handle.insert_idle(move |state| { - if let Some(mapped) = - state.common.shell.element_for_wl_surface(&surface).cloned() - { - if let Some(workspace) = state.common.shell.space_for_mut(&mapped) { - let output = seat.active_output(); - let (window, _) = mapped - .windows() - .find(|(w, _)| w.wl_surface().as_ref() == Some(&surface)) - .unwrap(); - workspace.maximize_toggle( - &window, - &output, - state.common.event_loop_handle.clone(), - ) - } + if let Some(surface) = self.window.wl_surface() { + loop_handle.insert_idle(move |state| { + if let Some(mapped) = + state.common.shell.element_for_wl_surface(&surface).cloned() + { + if let Some(workspace) = state.common.shell.space_for_mut(&mapped) { + let (window, _) = mapped + .windows() + .find(|(w, _)| w.wl_surface().as_ref() == Some(&surface)) + .unwrap(); + workspace.maximize_toggle(&window) } - }); - } + } + }); } } Message::Close => self.window.close(), diff --git a/src/shell/focus/mod.rs b/src/shell/focus/mod.rs index 07f24deb..1e4b588d 100644 --- a/src/shell/focus/mod.rs +++ b/src/shell/focus/mod.rs @@ -100,11 +100,9 @@ impl Shell { // update FocusStack and notify layouts about new focus (if any window) let element = match target { Some(KeyboardFocusTarget::Element(mapped)) => Some(mapped.clone()), - Some(KeyboardFocusTarget::Fullscreen(window)) => state - .common - .shell - .element_for_surface(&window.surface()) - .cloned(), + Some(KeyboardFocusTarget::Fullscreen(window)) => { + state.common.shell.element_for_surface(window).cloned() + } _ => None, }; @@ -154,7 +152,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 +161,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 +222,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); @@ -236,7 +235,7 @@ impl Common { let workspace = state.common.shell.active_space(&output); let focus_stack = workspace.focus_stack.get(&seat); if focus_stack.last().map(|m| m == &mapped).unwrap_or(false) - && workspace.get_fullscreen(&output).is_none() + && workspace.get_fullscreen().is_none() { continue; // Focus is valid } else { @@ -248,11 +247,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, } } @@ -262,9 +266,9 @@ impl Common { if focus_stack .last() - .map(|m| m.has_active_window(&window.surface())) + .map(|m| m.has_active_window(&window)) .unwrap_or(false) - && workspace.get_fullscreen(&output).is_some() + && workspace.get_fullscreen().is_some() { continue; // Focus is valid } else { @@ -307,7 +311,7 @@ impl Common { .common .shell .active_space(&output) - .get_fullscreen(&output) + .get_fullscreen() .cloned() .map(KeyboardFocusTarget::Fullscreen) .or_else(|| { diff --git a/src/shell/focus/target.rs b/src/shell/focus/target.rs index 72d41ae7..15a69a7a 100644 --- a/src/shell/focus/target.rs +++ b/src/shell/focus/target.rs @@ -1,10 +1,7 @@ use std::sync::Weak; use crate::{ - shell::{ - element::{CosmicMapped, CosmicWindow}, - layout::tiling::ResizeForkTarget, - }, + shell::{element::CosmicMapped, layout::tiling::ResizeForkTarget, CosmicSurface}, utils::prelude::*, wayland::handlers::xdg_shell::popup::get_popup_toplevel, }; @@ -22,7 +19,6 @@ use smithay::{ }, Seat, }, - output::WeakOutput, reexports::wayland_server::{backend::ObjectId, protocol::wl_surface::WlSurface, Resource}, utils::{IsAlive, Serial}, wayland::seat::WaylandFocus, @@ -32,7 +28,7 @@ use smithay::{ #[derive(Debug, Clone, PartialEq)] pub enum PointerFocusTarget { Element(CosmicMapped), - Fullscreen(CosmicWindow), + Fullscreen(CosmicSurface), LayerSurface(LayerSurface), Popup(PopupKind), OverrideRedirect(X11Surface), @@ -42,7 +38,7 @@ pub enum PointerFocusTarget { #[derive(Debug, Clone, PartialEq)] pub enum KeyboardFocusTarget { Element(CosmicMapped), - Fullscreen(CosmicWindow), + Fullscreen(CosmicSurface), Group(WindowGroup), LayerSurface(LayerSurface), Popup(PopupKind), @@ -87,16 +83,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) } } @@ -564,8 +557,8 @@ impl From for PointerFocusTarget { } } -impl From for PointerFocusTarget { - fn from(s: CosmicWindow) -> Self { +impl From for PointerFocusTarget { + fn from(s: CosmicSurface) -> Self { PointerFocusTarget::Fullscreen(s) } } @@ -600,8 +593,8 @@ impl From for KeyboardFocusTarget { } } -impl From for KeyboardFocusTarget { - fn from(s: CosmicWindow) -> Self { +impl From for KeyboardFocusTarget { + fn from(s: CosmicSurface) -> Self { KeyboardFocusTarget::Fullscreen(s) } } diff --git a/src/shell/grabs/moving.rs b/src/shell/grabs/moving.rs index e8233b55..6af828c9 100644 --- a/src/shell/grabs/moving.rs +++ b/src/shell/grabs/moving.rs @@ -82,15 +82,21 @@ impl MoveGrabState { let mut window_geo = self.window.geometry(); window_geo.loc += cursor_at.to_i32_round() + self.window_offset; - if !output.geometry().intersection(window_geo).is_some() { + if !output + .geometry() + .as_logical() + .intersection(window_geo) + .is_some() + { return Vec::new(); } let output_scale: Scale = output.current_scale().fractional_scale().into(); let scaling_offset = self.window_offset - self.window_offset.to_f64().upscale(scale).to_i32_round(); - let render_location = - cursor_at.to_i32_round() - output.geometry().loc + self.window_offset - scaling_offset; + let render_location = cursor_at.to_i32_round() - output.geometry().loc.as_logical() + + self.window_offset + - scaling_offset; let focus_element = if self.indicator_thickness > 0 { Some( @@ -105,7 +111,8 @@ impl MoveGrabState { .to_f64() .upscale(scale) .to_i32_round(), - ), + ) + .as_local(), self.indicator_thickness, output_scale.x, 1.0, @@ -208,6 +215,7 @@ impl PointerGrab for MoveGrab { .find(|output| { output .geometry() + .as_logical() .contains(handle.current_location().to_i32_round()) }) .cloned() @@ -221,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(); } @@ -233,8 +241,8 @@ impl PointerGrab for MoveGrab { if let Some(grab_state) = borrow.as_mut().and_then(|s| s.as_mut()) { let mut window_geo = self.window.geometry(); window_geo.loc += event.location.to_i32_round() + grab_state.window_offset; - for output in &state.common.shell.outputs { - if let Some(overlap) = output.geometry().intersection(window_geo) { + for output in state.common.shell.outputs() { + if let Some(overlap) = output.geometry().as_logical().intersection(window_geo) { if self.window_outputs.insert(output.clone()) { self.window.output_enter(output, overlap); if let Some(indicator) = @@ -261,11 +269,20 @@ impl PointerGrab for MoveGrab { if indicator_location.is_some() != grab_state.stacking_indicator.is_some() { grab_state.stacking_indicator = indicator_location.map(|geo| { - let element = stack_hover(state.common.event_loop_handle.clone(), geo.size); + let element = stack_hover( + state.common.event_loop_handle.clone(), + geo.size.as_logical(), + ); for output in &self.window_outputs { - element.output_enter(output, output.geometry()); + element.output_enter( + output, + Rectangle::from_loc_and_size( + (0, 0), + output.geometry().size.as_logical(), + ), + ); } - (element, geo.loc) + (element, geo.loc.as_logical()) }); } } @@ -397,8 +414,8 @@ impl MoveGrab { start_data: PointerGrabStartData, window: CosmicMapped, seat: &Seat, - initial_cursor_location: Point, - initial_window_location: Point, + initial_cursor_location: Point, + initial_window_location: Point, indicator_thickness: u8, was_tiled: bool, ) -> MoveGrab { @@ -409,7 +426,8 @@ impl MoveGrab { let grab_state = MoveGrabState { window: window.clone(), - window_offset: initial_window_location - initial_cursor_location.to_i32_round(), + window_offset: (initial_window_location - initial_cursor_location.to_i32_round()) + .as_logical(), indicator_thickness, start: Instant::now(), stacking_indicator: None, @@ -451,16 +469,16 @@ impl MoveGrab { // No more buttons are pressed, release the grab. let output = self.seat.active_output(); - let position = if let Some(grab_state) = self + let position: Option<(CosmicMapped, Point)> = if let Some(grab_state) = self .seat .user_data() .get::() .and_then(|s| s.borrow_mut().take()) { if grab_state.window.alive() { - let window_location = handle.current_location().to_i32_round() - - output.geometry().loc - + grab_state.window_offset; + let window_location = (handle.current_location().to_i32_round() + + grab_state.window_offset) + .as_global(); let workspace_handle = state.common.shell.active_space(&output).handle; for old_output in self.window_outputs.iter().filter(|o| *o != &output) { @@ -480,41 +498,26 @@ impl MoveGrab { } if self.tiling { - Some( - state - .common - .shell - .active_space_mut(&output) - .tiling_layer - .drop_window(grab_state.window, &output, handle.current_location()), - ) - } else { - let offset = state + let (window, location) = state .common .shell - .active_space(&output) - .floating_layer - .space - .output_geometry(&output) - .unwrap() - .loc; + .active_space_mut(&output) + .tiling_layer + .drop_window(grab_state.window); + Some((window, location.to_global(&output))) + } else { grab_state.window.set_geometry(Rectangle::from_loc_and_size( - window_location + offset, - grab_state.window.geometry().size, + window_location, + grab_state.window.geometry().size.as_global(), )); - state - .common - .shell - .active_space_mut(&output) - .floating_layer - .map_internal(grab_state.window, &output, Some(window_location + offset)); - - let pointer_pos = handle.current_location(); - let relative_pos = state.common.shell.map_global_to_space(pointer_pos, &output); - Some(( - self.window.clone(), - window_location + offset + (pointer_pos - relative_pos).to_i32_round(), - )) + let workspace = state.common.shell.active_space_mut(&output); + workspace.floating_layer.map_internal( + grab_state.window, + Some(window_location.to_local(&workspace.output)), + None, + ); + + Some((self.window.clone(), window_location)) } } else { None @@ -535,7 +538,7 @@ impl MoveGrab { state, Some(( PointerFocusTarget::from(mapped.clone()), - position - self.window.geometry().loc, + position.as_logical() - self.window.geometry().loc, )), &MotionEvent { location: handle.current_location(), diff --git a/src/shell/layout/floating/grabs/resize.rs b/src/shell/layout/floating/grabs/resize.rs index 530c9a86..89b9ef54 100644 --- a/src/shell/layout/floating/grabs/resize.rs +++ b/src/shell/layout/floating/grabs/resize.rs @@ -101,10 +101,10 @@ impl PointerGrab for ResizeSurfaceGrab { self.window.set_resizing(true); self.window.set_geometry(Rectangle::from_loc_and_size( match self.window.active_window() { - CosmicSurface::X11(s) => s.geometry().loc, + CosmicSurface::X11(s) => s.geometry().loc.as_global(), _ => (0, 0).into(), }, - self.last_window_size, + self.last_window_size.as_global(), )); self.window.configure(); } @@ -139,10 +139,10 @@ impl PointerGrab for ResizeSurfaceGrab { self.window.set_resizing(false); self.window.set_geometry(Rectangle::from_loc_and_size( match self.window.active_window() { - CosmicSurface::X11(s) => s.geometry().loc, + CosmicSurface::X11(s) => s.geometry().loc.as_global(), _ => (0, 0).into(), }, - self.last_window_size, + self.last_window_size.as_global(), )); self.window.configure(); @@ -271,7 +271,13 @@ impl ResizeSurfaceGrab { } pub fn apply_resize_to_location(window: CosmicMapped, space: &mut Workspace) { - if let Some(location) = space.floating_layer.space.element_location(&window) { + if let Some(location) = space + .floating_layer + .space + .element_location(&window) + .map(PointExt::as_local) + .map(|p| p.to_global(space.output())) + { let mut new_location = None; let mut resize_state = window.resize_state.lock().unwrap(); @@ -316,13 +322,13 @@ impl ResizeSurfaceGrab { CosmicSurface::Wayland(window) => { update_reactive_popups( &window, - new_location + offset, + new_location + offset.as_global(), space.floating_layer.space.outputs(), ); } CosmicSurface::X11(surface) => { let mut geometry = surface.geometry(); - geometry.loc += location - new_location; + geometry.loc += (location - new_location).as_logical(); let _ = surface.configure(geometry); } _ => unreachable!(), @@ -331,7 +337,7 @@ impl ResizeSurfaceGrab { space .floating_layer .space - .map_element(window, new_location, false); + .map_element(window, new_location.as_logical(), false); } } } diff --git a/src/shell/layout/floating/mod.rs b/src/shell/layout/floating/mod.rs index 28f9a40b..c2531e08 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,68 @@ 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 fn map_maximized(&mut self, mapped: CosmicMapped) { + let output = self.space.outputs().next().unwrap().clone(); + let layers = layer_map_for_output(&output); + let geometry = layers.non_exclusive_zone().as_local(); + + mapped.set_bounds(geometry.size.as_logical()); + mapped.set_tiled(true); + mapped.set_maximized(true); + mapped.set_geometry(geometry.to_global(&output)); + mapped.configure(); + + self.space + .map_element(mapped, geometry.loc.as_logical(), true); } 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; } { @@ -151,30 +149,17 @@ 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 { - #[allow(irrefutable_let_patterns)] - let is_maximized = window.is_maximized(true); - - if !is_maximized { + if !window.is_maximized(true) || !window.is_fullscreen(true) { 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()); } } @@ -183,56 +168,8 @@ impl FloatingLayout { was_unmaped } - pub fn element_geometry(&self, elem: &CosmicMapped) -> Option> { - self.space.element_geometry(elem) - } - - pub fn maximize_request(&mut self, window: &CosmicSurface) { - if let Some(mapped) = self - .space - .elements() - .find(|m| m.windows().any(|(w, _)| &w == window)) - { - if let Some(location) = self.space.element_location(mapped) { - *mapped.last_geometry.lock().unwrap() = Some(Rectangle::from_loc_and_size( - location, - mapped.geometry().size, - )); - } - } - } - - pub fn unmaximize_request(&mut self, window: &CosmicSurface) -> Option> { - let maybe_mapped = self - .space - .elements() - .find(|m| m.windows().any(|(w, _)| &w == window)) - .cloned(); - - if let Some(mapped) = maybe_mapped { - let last_geometry = mapped.last_geometry.lock().unwrap().clone(); - let last_size = last_geometry.map(|g| g.size).expect("No previous size?"); - let last_location = last_geometry.map(|g| g.loc).expect("No previous location?"); - let output = self - .space - .output_under(last_location.to_f64()) - .next() - .unwrap_or(self.space.outputs().next().unwrap()); - 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( - last_location + offset, - last_size, - )); - self.space.map_element(mapped, last_location, true); - Some(last_size) - } else { - None - } + pub fn element_geometry(&self, elem: &CosmicMapped) -> Option> { + self.space.element_geometry(elem).map(RectExt::as_local) } pub fn resize_request( @@ -275,6 +212,9 @@ impl FloatingLayout { else { return false; }; + if mapped.is_maximized(true) { + return false; + } let Some(original_geo) = self.space.element_geometry(mapped) else { return false; // we don't have that window @@ -340,13 +280,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 @@ -472,7 +409,7 @@ impl FloatingLayout { }) .then_some(pos); - self.map_internal(mapped.clone(), &output, position); + self.map_internal(mapped.clone(), position.map(PointExt::as_local), None); return MoveResult::ShiftFocus(KeyboardFocusTarget::Element(mapped)); } StackMoveResult::Default => {} @@ -504,83 +441,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, @@ -599,68 +479,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, - ); - - 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, + 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(), + 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()); - } + 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.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; + 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); - }; + // 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; - }; + 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!(), + 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; } - - 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); + _ => 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 7e4c4d78..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, } @@ -116,7 +81,7 @@ pub struct NodeDesc { enum TargetZone { Initial, InitialPlaceholder(NodeId), - WindowStack(NodeId, Rectangle), + WindowStack(NodeId, Rectangle), WindowSplit(NodeId, Direction), GroupEdge(NodeId, Direction), GroupInterior(NodeId, usize), @@ -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, @@ -171,22 +136,22 @@ pub enum Data { Group { orientation: Orientation, sizes: Vec, - last_geometry: Rectangle, + last_geometry: Rectangle, alive: Arc<()>, pill_indicator: Option, }, Mapped { mapped: CosmicMapped, - last_geometry: Rectangle, + last_geometry: Rectangle, }, Placeholder { - last_geometry: Rectangle, + last_geometry: Rectangle, initial_placeholder: bool, }, } impl Data { - fn new_group(orientation: Orientation, geo: Rectangle) -> Data { + fn new_group(orientation: Orientation, geo: Rectangle) -> Data { Data::Group { orientation, sizes: vec![ @@ -296,7 +261,7 @@ impl Data { } } - fn geometry(&self) -> &Rectangle { + fn geometry(&self) -> &Rectangle { match self { Data::Group { last_geometry, .. } => last_geometry, Data::Mapped { last_geometry, .. } => last_geometry, @@ -304,7 +269,7 @@ impl Data { } } - fn update_geometry(&mut self, geo: Rectangle) { + fn update_geometry(&mut self, geo: Rectangle) { match self { Data::Group { orientation, @@ -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 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()) - { - self.pending_blockers.push(blocker); - } - 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; - }; + pub fn set_output(&mut self, output: &Output) { + let mut tree = self.queue.trees.back().unwrap().0.copy_clone(); - 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() + 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() { - 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()); - } + mapped.output_leave(&self.output); + mapped.output_enter(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(&output_data.output); - new.output_enter(&output_data.output, new.bbox()); + old.output_leave(&self.output); + new.output_enter(&self.output, new.bbox()); - let blocker = TilingLayout::update_positions(&output_data.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 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,65 +2031,65 @@ 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; - } + if let Some(start) = self.queue.animation_start { + let duration_since_start = Instant::now().duration_since(start); + if duration_since_start + >= self + .queue + .trees + .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 { + 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(); - let ready_trees = queue + // merge + let other_duration = if ready_trees > 1 { + self.queue .trees - .iter() - .skip(1) - .take_while(|(_, _, blocker)| { - blocker - .as_ref() - .map(|blocker| blocker.is_ready() && blocker.is_signaled()) - .unwrap_or(true) + .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), + ) }) - .count(); - - // merge - let other_duration = if ready_trees > 1 { - 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 - }; + } 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()); + // 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), + )); } } _ => {} @@ -2816,7 +2580,9 @@ impl TilingLayout { let mut configures = Vec::new(); let (outer, inner) = gaps; - let mut geo = layer_map_for_output(&output).non_exclusive_zone(); + let mut geo = layer_map_for_output(&output) + .non_exclusive_zone() + .as_local(); geo.loc.x += outer; geo.loc.y += outer; geo.size.w -= outer * 2; @@ -2931,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)); } } } @@ -2964,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()); - } + 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; - } + 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; } } } + } - 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, - ) + 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(( - id, - Data::Group { + ResizeForkTarget { + node: id.clone(), + output: self.output.downgrade(), + left_up_idx: idx, 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: 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, + } + .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()); } - } 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, - .. - } - ) - }) { + 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; } - for child_id in tree.children_ids(&node).unwrap() { - if geometries - .get(child_id) - .map(|geo| geo.contains(location)) - .unwrap_or(false) + } + } + } + + 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 + } else { + let left_edge = match &*last_overview_hover { + Some((_, TargetZone::GroupEdge(id, Direction::Left))) + if *id == res_id => { - lookup = Some(child_id.clone()); - break; + 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 { + 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)) } } - } + } 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 + }; - 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 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, + ), + ); - 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 + if stack_region.contains(location) { + TargetZone::WindowStack(id, last_geometry) } 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 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 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 + 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), + ) } }; - 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 { - 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)) - } - } - } 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() + let direction = if left_right.1 < up_down.1 { + left_right.0 } else { - res_id + up_down.0 }; - 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, - ), - ); + TargetZone::WindowSplit(id, direction) + } + } + }); - 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), - ) + 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 } - }; - 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), - ) + _ => { + Instant::now().duration_since(*instant) + > MOUSE_ANIMATION_DELAY } - }; + } + } else { + *instant = Some(Instant::now()); + false + }; - let direction = if left_right.1 < up_down.1 { - left_right.0 + 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); } - } 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 + 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()) { - 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; + 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::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 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 - }; + } + 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(); - } + let orientation = + if matches!(dir, Direction::Left | Direction::Right) { + Orientation::Vertical + } else { + 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(); + } - 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 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)); } - true - } else { - false + _ => unreachable!(), } - } 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 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)); } - true - } else { - false + _ => unreachable!(), } + true } else { false - }; - - if removed || added { - let blocker = TilingLayout::update_positions( - &output_data.output, - &mut tree, - *gaps, - ); - queue.push_tree(tree, duration, blocker); } + } else { + false + }; - *instant = None; - *old_target_zone = target_zone; + 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() { @@ -3571,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) + } + + 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 orientation = match output_data.output.geometry().size { - x if x.w >= x.h => Orientation::Vertical, - _ => Orientation::Horizontal, - }; - TilingLayout::merge_trees(src, &mut dst, orientation); + 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(&output_data.output, &mut dst, self.gaps); - dst_queue.push_tree(dst, ANIMATION_DURATION, blocker); - } + 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) { @@ -3678,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, @@ -3704,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) @@ -3814,7 +3555,7 @@ impl TilingLayout { old_geometries, is_overview, seat, - output, + &self.output, percentage, draw_groups, if let Some(transition) = draw_groups { @@ -3861,8 +3602,8 @@ fn swap_factor(size: Size) -> f64 { fn swap_geometry( size: Size, - relative_to: Rectangle, -) -> Rectangle { + relative_to: Rectangle, +) -> Rectangle { let factor = swap_factor(size); let new_size = Size::from(( @@ -3881,7 +3622,7 @@ fn swap_geometry( fn geometries_for_groupview<'a, R>( tree: &Tree, renderer: impl Into>, - non_exclusive_zone: Rectangle, + non_exclusive_zone: Rectangle, seat: Option<&Seat>, alpha: f32, transition: f32, @@ -3891,7 +3632,7 @@ fn geometries_for_groupview<'a, R>( swap_desc: Option, swap_tree: Option<&Tree>, ) -> ( - HashMap>, + HashMap>, Vec>, ) where @@ -3921,7 +3662,7 @@ where } let mut elements = Vec::new(); - let mut geometries: HashMap> = HashMap::new(); + let mut geometries: HashMap> = HashMap::new(); let alpha = alpha * transition; let focused = seat @@ -3929,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() { @@ -4268,7 +4007,7 @@ where ); } let swap_geo = swap_geometry( - geo.size, + geo.size.as_logical(), focused_geo.unwrap_or({ let mut geo = non_exclusive_zone; geo.loc += (WINDOW_BACKDROP_BORDER, WINDOW_BACKDROP_BORDER).into(); @@ -4469,7 +4208,7 @@ where if matches!(swap_desc, Some(ref desc) if &desc.node == &node_id && desc.stack_window.is_none()) { let swap_geo = swap_geometry( - geo.size, + geo.size.as_logical(), focused_geo.unwrap_or({ let mut geo = non_exclusive_zone; geo.loc += (WINDOW_BACKDROP_BORDER, WINDOW_BACKDROP_BORDER).into(); @@ -4523,7 +4262,7 @@ fn render_old_tree( reference_tree: &Tree, target_tree: &Tree, renderer: &mut R, - geometries: Option>>, + geometries: Option>>, output_scale: f64, percentage: f32, is_swap_mode: bool, @@ -4585,7 +4324,10 @@ where .unwrap_or(*original_geo); let crop_rect = geo.clone(); - let original_location = original_geo.loc.to_physical_precise_round(output_scale) + let original_location = original_geo + .loc + .as_logical() + .to_physical_precise_round(output_scale) - mapped .geometry() .loc @@ -4605,7 +4347,9 @@ where let cropped = CropRenderElement::from_element( elem, output_scale, - crop_rect.to_physical_precise_round(output_scale), + crop_rect + .as_logical() + .to_physical_precise_round(output_scale), )?; let rescaled = RescaleRenderElement::from_element( cropped, @@ -4615,6 +4359,7 @@ where let relocated = RelocateRenderElement::from_element( rescaled, (geo.loc - original_geo.loc) + .as_logical() .to_physical_precise_round(output_scale), Relocate::Relative, ); @@ -4626,7 +4371,9 @@ where let cropped = CropRenderElement::from_element( elem, output_scale, - crop_rect.to_physical_precise_round(output_scale), + crop_rect + .as_logical() + .to_physical_precise_round(output_scale), )?; let rescaled = RescaleRenderElement::from_element( cropped, @@ -4636,6 +4383,7 @@ where let relocated = RelocateRenderElement::from_element( rescaled, (geo.loc - original_geo.loc) + .as_logical() .to_physical_precise_round(output_scale), Relocate::Relative, ); @@ -4655,9 +4403,9 @@ fn render_new_tree( target_tree: &Tree, reference_tree: Option<&Tree>, renderer: &mut R, - non_exclusive_zone: Rectangle, - geometries: Option>>, - old_geometries: Option>>, + non_exclusive_zone: Rectangle, + geometries: Option>>, + old_geometries: Option>>, is_overview: bool, seat: Option<&Seat>, output: &Output, @@ -4685,13 +4433,7 @@ where seat.get_keyboard() .unwrap() .current_focus() - .and_then(|target| { - TilingLayout::currently_focused_node( - &target_tree, - &seat.active_output(), - target, - ) - }) + .and_then(|target| TilingLayout::currently_focused_node(&target_tree, target)) }) .map(|(id, _)| id); let focused_geo = if let Some(focused) = focused.as_ref() { @@ -4778,7 +4520,8 @@ where transition.unwrap_or(1.0), )); - let render_loc = (swap_geo.loc - window_geo.loc).to_physical_precise_round(output_scale); + let render_loc = + (swap_geo.loc.as_logical() - window_geo.loc).to_physical_precise_round(output_scale); swap_elements.extend( window @@ -4792,7 +4535,10 @@ where .map(|window| { CosmicMappedRenderElement::GrabbedWindow(RescaleRenderElement::from_element( window, - swap_geo.loc.to_physical_precise_round(output_scale), + swap_geo + .loc + .as_logical() + .to_physical_precise_round(output_scale), ease( Linear, 1.0, @@ -4985,12 +4731,12 @@ where .unwrap_or(false)) { if let Some(swap) = swap_indicator.as_ref() { - swap.resize(geo.size); - swap.output_enter(output, output_geo); + swap.resize(geo.size.as_logical()); + swap.output_enter(output, output_geo.as_logical()); swap_elements.extend( swap.render_elements::>( renderer, - geo.loc.to_physical_precise_round(output_scale), + geo.loc.as_logical().to_physical_precise_round(output_scale), output_scale.into(), alpha * overview.0.alpha().unwrap_or(1.0), ) @@ -5006,8 +4752,8 @@ where geo.loc -= (18, 18).into(); geo.size += (36, 36).into(); - resize.resize(geo.size); - resize.output_enter(output, output_geo); + resize.resize(geo.size.as_logical()); + resize.output_enter(output, output_geo.as_logical()); let possible_edges = TilingLayout::possible_resizes(target_tree, node_id.clone()); if !possible_edges.is_empty() { @@ -5026,7 +4772,7 @@ where resize .render_elements::>( renderer, - geo.loc.to_physical_precise_round(output_scale), + geo.loc.as_logical().to_physical_precise_round(output_scale), output_scale.into(), alpha * mode.alpha().unwrap_or(1.0), ) @@ -5039,7 +4785,7 @@ where } if let Data::Mapped { mapped, .. } = data { - let original_location = (original_geo.loc - mapped.geometry().loc) + let original_location = (original_geo.loc.as_logical() - mapped.geometry().loc) .to_physical_precise_round(output_scale); let (mut w_elements, p_elements) = mapped @@ -5069,7 +4815,7 @@ where }) .unwrap_or(false) { - let mut geo = mapped.active_window_geometry(); + let mut geo = mapped.active_window_geometry().as_local(); geo.loc += original_geo.loc; w_elements.insert( 0, @@ -5090,16 +4836,22 @@ where let cropped = CropRenderElement::from_element( elem, output_scale, - crop_rect.to_physical_precise_round(output_scale), + crop_rect + .as_logical() + .to_physical_precise_round(output_scale), )?; let rescaled = RescaleRenderElement::from_element( cropped, - original_geo.loc.to_physical_precise_round(output_scale), + original_geo + .loc + .as_logical() + .to_physical_precise_round(output_scale), scale, ); let relocated = RelocateRenderElement::from_element( rescaled, (geo.loc - original_geo.loc) + .as_logical() .to_physical_precise_round(output_scale), Relocate::Relative, ); @@ -5111,16 +4863,22 @@ where let cropped = CropRenderElement::from_element( elem, output_scale, - crop_rect.to_physical_precise_round(output_scale), + crop_rect + .as_logical() + .to_physical_precise_round(output_scale), )?; let rescaled = RescaleRenderElement::from_element( cropped, - original_geo.loc.to_physical_precise_round(output_scale), + original_geo + .loc + .as_logical() + .to_physical_precise_round(output_scale), scale, ); let relocated = RelocateRenderElement::from_element( rescaled, (geo.loc - original_geo.loc) + .as_logical() .to_physical_precise_round(output_scale), Relocate::Relative, ); @@ -5132,16 +4890,22 @@ where let cropped = CropRenderElement::from_element( elem, output_scale, - crop_rect.to_physical_precise_round(output_scale), + crop_rect + .as_logical() + .to_physical_precise_round(output_scale), )?; let rescaled = RescaleRenderElement::from_element( cropped, - original_geo.loc.to_physical_precise_round(output_scale), + original_geo + .loc + .as_logical() + .to_physical_precise_round(output_scale), scale, ); let relocated = RelocateRenderElement::from_element( rescaled, (geo.loc - original_geo.loc) + .as_logical() .to_physical_precise_round(output_scale), Relocate::Relative, ); @@ -5187,10 +4951,10 @@ where (window_elements, popup_elements) } -fn scale_to_center( - old_geo: &Rectangle, - new_geo: &Rectangle, -) -> (f64, Point) { +fn scale_to_center( + old_geo: &Rectangle, + new_geo: &Rectangle, +) -> (f64, Point) { let scale_w = new_geo.size.w as f64 / old_geo.size.w as f64; let scale_h = new_geo.size.h as f64 / old_geo.size.h as f64; diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 3640353f..6d47efec 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::{ - toplevel_info::ToplevelInfoState, - toplevel_management::{ManagementCapabilities, ToplevelManagementState}, - workspace::{ - WorkspaceCapabilities, WorkspaceGroupHandle, WorkspaceHandle, WorkspaceState, - WorkspaceUpdateGuard, + 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,63 +296,63 @@ 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(); + fn add_empty_workspace(&mut self, state: &mut WorkspaceUpdateGuard) { + let mut workspace = create_workspace( + state, + &self.output, + &self.group, + false, + self.tiling_enabled, + self.gaps, + ); + workspace_set_idx( + state, + self.workspaces.len() as u8 + 1, + self.idx, + &workspace.handle, + ); + state.set_workspace_capabilities( + &workspace.handle, + [WorkspaceCapabilities::Activate].into_iter(), + ); + 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().unwrap().windows().next().is_some() { - let mut workspace = create_workspace( - &mut state, - &self.group, - false, - self.tiling_enabled, - self.gaps, - ); - workspace_set_idx( - &mut state, - self.workspaces.len() as u8 + 1, - self.idx, - &workspace.handle, - ); - state.set_workspace_capabilities( - &workspace.handle, - [WorkspaceCapabilities::Activate].into_iter(), - ); - for (output, location) in outputs { - workspace.map_output(output, location); - } - self.workspaces.push(workspace); + 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), +pub struct Workspaces { + sets: IndexMap, + backup_set: Option, + amount: WorkspaceAmount, + mode: WorkspaceMode, + tiling_enabled: bool, + gaps: (u8, u8), } -impl WorkspaceMode { - pub fn new( - config: crate::config::WorkspaceMode, - amount: WorkspaceAmount, - state: &mut WorkspaceUpdateGuard<'_, State>, - 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 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)) + //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> { + 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 - .get_mut(output) - .and_then(|set| set.workspaces.get_mut(num)), - } + self.sets + .get_mut(output) + .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(); - ( - set.previously_active - .map(|(idx, start)| (&set.workspaces[idx], start)), - &set.workspaces[set.active], - ) - } - } + 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(); - &mut set.workspaces[set.active] - } - } + 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(); - (set.previously_active.map(|(idx, _)| idx), set.active) - } - } + 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(); - set.workspaces.len() - } - } + 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) - .into_iter() - .flat_map(|set| set.workspaces.iter()), - ), - } + self.sets + .get(output) + .into_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() { - set.update_tiling_status(seat, tiling) - } - } + 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( - output, - output - .user_data() - .get::>() - .unwrap() - .borrow() - .position - .into(), - ); - } - } - } + self.workspaces.add_output( + output, + &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.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(), - ); - } - } - } + 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 } + /* 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())? + 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)), + )) } else { - false + Ok(None) } } - WorkspaceMode::Global(set) => 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)), - )) - } 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) @@ -1073,7 +1115,10 @@ impl Shell { .filter(|o| { self.override_redirect_windows.iter().any(|or| { if or.wl_surface().as_ref() == Some(surface) { - or.geometry().intersection(o.geometry()).is_some() + or.geometry() + .as_global() + .intersection(o.geometry()) + .is_some() } else { false } @@ -1083,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)) })), ), } @@ -1094,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() @@ -1102,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() } @@ -1163,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 @@ -1298,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, - &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())), - ), - } + self.workspaces.refresh( + &mut self.workspace_state.update(), + &mut self.toplevel_info_state, + ); - for output in &self.outputs { + for output in self.outputs() { let mut map = layer_map_for_output(output); map.cleanup(); } @@ -1340,7 +1348,7 @@ impl Shell { let (window, seat) = state.common.shell.pending_windows.remove(pos); let workspace = state.common.shell.workspaces.active_mut(output); - workspace.remove_fullscreen(output); + workspace.remove_fullscreen(); state.common.shell.toplevel_info_state.new_toplevel(&window); state .common @@ -1362,12 +1370,21 @@ 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 { + for mapped in workspace + .mapped() + .filter(|m| m.maximized_state.lock().unwrap().is_some()) + .cloned() + .collect::>() + .into_iter() + { + workspace.unmaximize_request(&mapped.active_window()); + } 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 { @@ -1393,13 +1410,12 @@ impl Shell { pub fn map_override_redirect(state: &mut State, window: X11Surface) { let geo = window.geometry(); - for (output, overlap) in state - .common - .shell - .outputs() - .cloned() - .filter_map(|o| o.geometry().intersection(geo).map(|overlap| (o, overlap))) - { + for (output, overlap) in state.common.shell.outputs().cloned().filter_map(|o| { + o.geometry() + .as_logical() + .intersection(geo) + .map(|overlap| (o, overlap)) + }) { window.output_enter(&output, overlap); } @@ -1429,7 +1445,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 { @@ -1444,7 +1460,7 @@ impl Shell { to: (&Output, Option), follow: bool, direction: Option, - ) -> Result>, InvalidWorkspaceIndex> { + ) -> Result>, InvalidWorkspaceIndex> { let (to_output, to_idx) = to; let to_idx = to_idx.unwrap_or(state.common.shell.workspaces.active_num(to_output).1); if state @@ -1507,44 +1523,17 @@ impl Shell { .unwrap(); // checked above let focus_stack = to_workspace.focus_stack.get(&seat); if window_state.layer == ManagedLayer::Floating { - to_workspace.floating_layer.map(mapped.clone(), &seat, None); + to_workspace.floating_layer.map(mapped.clone(), None); } else { to_workspace .tiling_layer - .map(mapped.clone(), &seat, focus_stack.iter(), direction); + .map(mapped.clone(), focus_stack.iter(), direction); } - let focus_target = match window_state.was_fullscreen { - Some(true) => { - to_workspace.fullscreen_request( - &mapped.active_window(), - &to_output, - state.common.event_loop_handle.clone(), - ); - KeyboardFocusTarget::from( - to_workspace - .fullscreen - .get(to_output) - .unwrap() - .window - .clone(), - ) - } - Some(false) => { - to_workspace.maximize_request( - &mapped.active_window(), - &to_output, - state.common.event_loop_handle.clone(), - ); - KeyboardFocusTarget::from( - to_workspace - .fullscreen - .get(to_output) - .unwrap() - .window - .clone(), - ) - } - None => KeyboardFocusTarget::from(mapped.clone()), + let focus_target = if window_state.was_fullscreen.is_some() { + to_workspace.fullscreen_request(&mapped.active_window()); + KeyboardFocusTarget::from(to_workspace.get_fullscreen().unwrap().clone()) + } else { + KeyboardFocusTarget::from(mapped.clone()) }; for (toplevel, _) in mapped.windows() { @@ -1578,14 +1567,18 @@ impl Shell { pub fn update_reactive_popups(&self, mapped: &CosmicMapped) { if let Some(workspace) = self.space_for(mapped) { - let element_loc = workspace.element_geometry(mapped).unwrap().loc; + let element_loc = workspace + .element_geometry(mapped) + .unwrap() + .loc + .to_global(&workspace.output); for (toplevel, offset) in mapped.windows() { if let CosmicSurface::Wayland(toplevel) = toplevel { - let window_geo_offset = toplevel.geometry().loc; + let window_geo_offset = toplevel.geometry().loc.as_global(); update_reactive_popups( &toplevel, - element_loc + offset + window_geo_offset, - self.outputs.iter(), + element_loc + offset.as_global() + window_geo_offset, + self.outputs(), ); } } diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 39486bae..444708ab 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -21,7 +21,6 @@ use crate::{ xwayland::XWaylandState, }; -use calloop::LoopHandle; use id_tree::Tree; use indexmap::IndexSet; use keyframe::{ease, functions::EaseInOutCubic}; @@ -47,7 +46,7 @@ use smithay::{ xwayland::X11Surface, }; use std::{ - collections::HashMap, + collections::{HashMap, VecDeque}, sync::{ atomic::{AtomicBool, Ordering}, Arc, @@ -61,7 +60,7 @@ use super::{ element::{ resize_indicator::ResizeIndicator, stack::CosmicStackRenderElement, swap_indicator::SwapIndicator, window::CosmicWindowRenderElement, CosmicMapped, - CosmicWindow, + MaximizedState, }, focus::{ target::{KeyboardFocusTarget, PointerFocusTarget, WindowGroup}, @@ -76,28 +75,36 @@ 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, - pub fullscreen: HashMap, + pub fullscreen: Option, + pub handle: WorkspaceHandle, 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, } #[derive(Debug, Clone)] pub struct FullscreenSurface { - pub window: CosmicWindow, - pub exclusive: bool, - original_size: Size, + pub surface: CosmicSurface, + original_geometry: Rectangle, start_at: Option, ended_at: Option, animation_signal: Option>, } +impl PartialEq for FullscreenSurface { + fn eq(&self, other: &Self) -> bool { + self.surface == other.surface + } +} + struct FullscreenBlocker { signal: Arc, } @@ -120,17 +127,17 @@ impl FullscreenSurface { impl IsAlive for FullscreenSurface { fn alive(&self) -> bool { - self.window.alive() + self.surface.alive() } } #[derive(Debug, Default)] pub struct FocusStacks(HashMap, IndexSet>); -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq)] pub struct ManagedState { pub layer: ManagedLayer, - pub was_fullscreen: Option, + pub was_fullscreen: Option, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum ManagedLayer { @@ -198,16 +205,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(), + fullscreen: None, 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), } @@ -217,7 +234,11 @@ impl Workspace { #[cfg(feature = "debug")] puffin::profile_function!(); - self.fullscreen.retain(|_, w| w.alive()); + // TODO: `Option::take_if` once stabilitized + if self.fullscreen.as_ref().is_some_and(|w| !w.alive()) { + let _ = self.fullscreen.take(); + }; + self.floating_layer.refresh(); self.tiling_layer.refresh(); } @@ -233,15 +254,15 @@ impl Workspace { self.tiling_layer.animations_going() || self .fullscreen - .values() - .any(|f| f.start_at.is_some() || f.ended_at.is_some()) + .as_ref() + .is_some_and(|f| f.start_at.is_some() || f.ended_at.is_some()) || self.dirty.swap(false, Ordering::SeqCst) } pub fn update_animations(&mut self) -> HashMap { let mut clients = HashMap::new(); - for f in self.fullscreen.values_mut() { + if let Some(f) = self.fullscreen.as_mut() { if let Some(start) = f.start_at.as_ref() { let duration_since = Instant::now().duration_since(*start); if duration_since > FULLSCREEN_ANIMATION_DURATION { @@ -252,36 +273,32 @@ impl Workspace { if let Some(signal) = f.animation_signal.take() { signal.store(true, Ordering::SeqCst); if let Some(client) = - f.window.wl_surface().as_ref().and_then(Resource::client) + f.surface.wl_surface().as_ref().and_then(Resource::client) { clients.insert(client.id(), client); } } } } - } - let len = self.fullscreen.len(); - self.fullscreen.retain(|_, f| match f.ended_at { - None => true, - Some(instant) => { - let duration_since = Instant::now().duration_since(instant); + if let Some(end) = f.ended_at { + let duration_since = Instant::now().duration_since(end); if duration_since * 2 > FULLSCREEN_ANIMATION_DURATION { if let Some(signal) = f.animation_signal.take() { signal.store(true, Ordering::SeqCst); if let Some(client) = - f.window.wl_surface().as_ref().and_then(Resource::client) + f.surface.wl_surface().as_ref().and_then(Resource::client) { clients.insert(client.id(), client); } } } - duration_since < FULLSCREEN_ANIMATION_DURATION + if duration_since >= FULLSCREEN_ANIMATION_DURATION { + let _ = self.fullscreen.take(); + self.dirty.store(true, Ordering::SeqCst); + } } - }); - if len != self.fullscreen.len() { - self.dirty.store(true, Ordering::SeqCst); } clients.extend(self.tiling_layer.update_animation_state()); @@ -299,37 +316,46 @@ 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_fullscreen = self + .fullscreen + .as_ref() + .filter(|f| f.ended_at.is_none()) + .map(|f| mapped.windows().any(|(w, _)| w == f.surface)) + .unwrap_or(false) + .then(|| self.fullscreen.take().unwrap()); + + if mapped.is_maximized(true) { + // If surface is maximized then unmaximize it, so it is assigned to only one layer + let _ = self.unmaximize_request(&mapped.active_window()); + } + 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); } - let was_maximized = mapped.is_maximized(true); - let was_fullscreen = mapped.is_fullscreen(true); - if was_maximized || was_fullscreen { - self.unmaximize_request(&mapped.active_window()); - } - self.focus_stack .0 .values_mut() @@ -337,12 +363,12 @@ impl Workspace { if was_floating { Some(ManagedState { layer: ManagedLayer::Floating, - was_fullscreen: (was_fullscreen || was_maximized).then_some(was_fullscreen), + was_fullscreen, }) } else if was_tiling { Some(ManagedState { layer: ManagedLayer::Tiling, - was_fullscreen: (was_fullscreen || was_maximized).then_some(was_fullscreen), + was_fullscreen, }) } else { None @@ -366,140 +392,94 @@ impl Workspace { }) } - 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( - &mut self, - window: &CosmicSurface, - output: &Output, - evlh: LoopHandle<'static, crate::state::State>, - ) { - if self.fullscreen.contains_key(output) { + pub fn maximize_request(&mut self, window: &CosmicSurface) { + if self.fullscreen.is_some() { return; } - self.floating_layer.maximize_request(window); - - window.set_fullscreen(false); - window.set_maximized(true); - self.set_fullscreen(window, output, false, evlh) + if let Some(elem) = self.element_for_surface(window).cloned() { + let mut state = elem.maximized_state.lock().unwrap(); + if state.is_none() { + *state = Some(MaximizedState { + original_geometry: self.element_geometry(&elem).unwrap(), + original_layer: if self.is_floating(&elem) { + ManagedLayer::Floating + } else { + ManagedLayer::Tiling + }, + }); + std::mem::drop(state); + self.floating_layer.map_maximized(elem); + } + } } pub fn unmaximize_request(&mut self, window: &CosmicSurface) -> Option> { - if self - .fullscreen - .values() - .any(|f| &f.window.surface() == window) - { - self.unfullscreen_request(window) - } else { - None + if let Some(elem) = self.element_for_surface(window).cloned() { + let mut state = elem.maximized_state.lock().unwrap(); + if let Some(state) = state.take() { + match state.original_layer { + ManagedLayer::Tiling => { + // should still be mapped in tiling + self.floating_layer.unmap(&elem); + elem.output_enter(&self.output, elem.bbox()); + elem.set_maximized(false); + elem.set_geometry(state.original_geometry.to_global(&self.output)); + elem.configure(); + self.tiling_layer.recalculate(); + return self + .tiling_layer + .element_geometry(&elem) + .map(|geo| geo.size.as_logical()); + } + ManagedLayer::Floating => { + elem.set_maximized(false); + self.floating_layer.map_internal( + elem.clone(), + Some(state.original_geometry.loc), + Some(state.original_geometry.size.as_logical()), + ); + return Some(state.original_geometry.size.as_logical()); + } + } + } } + None } - pub fn fullscreen_request( - &mut self, - window: &CosmicSurface, - output: &Output, - evlh: LoopHandle<'static, crate::state::State>, - ) { - if self - .fullscreen - .get(output) - .map(|f| &f.window.surface() != window) - .unwrap_or(false) - { + pub fn fullscreen_request(&mut self, window: &CosmicSurface) { + if self.fullscreen.is_some() { return; } - if !window.is_maximized(true) { - self.floating_layer.maximize_request(window); - } - - window.set_maximized(false); window.set_fullscreen(true); - self.set_fullscreen(window, output, true, evlh) - } - - fn set_fullscreen<'a>( - &mut self, - window: &'a CosmicSurface, - output: &Output, - exclusive: bool, - evlh: LoopHandle<'static, crate::state::State>, - ) { - if let Some(mapped) = self - .mapped() - .find(|m| m.windows().any(|(w, _)| &w == window)) - { - mapped.set_active(window); - } - - let window = CosmicWindow::new(window.clone(), evlh); - let geo = if exclusive { - output.geometry() - } else { - layer_map_for_output(output).non_exclusive_zone() - }; - let original_size = window.geometry().size; + let geo = self.output.geometry(); + let original_geometry = window.geometry().as_global(); let signal = if let Some(surface) = window.wl_surface() { let signal = Arc::new(AtomicBool::new(false)); add_blocker( @@ -513,35 +493,26 @@ impl Workspace { None }; window.set_geometry(geo); - window.output_enter(output, Rectangle::from_loc_and_size((0, 0), geo.size)); - window.surface().send_configure(); - - self.fullscreen.insert( - output.clone(), - FullscreenSurface { - window: window.clone(), - exclusive, - original_size, - start_at: Some(Instant::now()), - ended_at: None, - animation_signal: signal, - }, - ); + window.send_configure(); + + self.fullscreen = Some(FullscreenSurface { + surface: window.clone(), + original_geometry, + start_at: Some(Instant::now()), + ended_at: None, + animation_signal: signal, + }); } pub fn unfullscreen_request(&mut self, window: &CosmicSurface) -> Option> { - if let Some((output, f)) = self - .fullscreen - .iter_mut() - .find(|(_, f)| &f.window.surface() == window) - { - f.window.output_leave(output); - window.set_maximized(false); + if let Some(f) = self.fullscreen.as_mut().filter(|f| &f.surface == window) { window.set_fullscreen(false); - let result = self.floating_layer.unmaximize_request(window); + window.set_geometry(f.original_geometry); + self.floating_layer.refresh(); - self.tiling_layer.recalculate(output); + self.tiling_layer.recalculate(); self.tiling_layer.refresh(); + let signal = if let Some(surface) = window.wl_surface() { let signal = Arc::new(AtomicBool::new(false)); add_blocker( @@ -574,90 +545,32 @@ impl Workspace { } } - result + Some(f.original_geometry.size.as_logical()) } else { None } } - pub fn remove_fullscreen(&mut self, output: &Output) { - if let Some(FullscreenSurface { - window, - ended_at, - start_at, - animation_signal, - .. - }) = self.fullscreen.get_mut(output) - { - window.output_leave(output); - let surface = window.surface(); - surface.set_maximized(false); - surface.set_fullscreen(false); - self.floating_layer.unmaximize_request(&surface); - self.floating_layer.refresh(); - self.tiling_layer.recalculate(output); - self.tiling_layer.refresh(); - let signal = if let Some(surface) = surface.wl_surface() { - let signal = Arc::new(AtomicBool::new(false)); - add_blocker( - &surface, - FullscreenBlocker { - signal: signal.clone(), - }, - ); - Some(signal) - } else { - None - }; - surface.send_configure(); - - *ended_at = Some( - Instant::now() - - (FULLSCREEN_ANIMATION_DURATION - - start_at - .take() - .map(|earlier| { - Instant::now() - .duration_since(earlier) - .min(FULLSCREEN_ANIMATION_DURATION) - }) - .unwrap_or(FULLSCREEN_ANIMATION_DURATION)), - ); - if let Some(new_signal) = signal { - if let Some(old_signal) = animation_signal.replace(new_signal) { - old_signal.store(true, Ordering::SeqCst); - } - } + pub fn remove_fullscreen(&mut self) { + if let Some(surface) = self.fullscreen.as_ref().map(|f| f.surface.clone()) { + self.unfullscreen_request(&surface); } } - pub fn maximize_toggle( - &mut self, - window: &CosmicSurface, - output: &Output, - evlh: LoopHandle<'static, crate::state::State>, - ) { - if self.fullscreen.contains_key(output) { + pub fn maximize_toggle(&mut self, window: &CosmicSurface) { + if window.is_maximized(true) { self.unmaximize_request(window); } else { - self.maximize_request(window, output, evlh); + self.maximize_request(window); } } - pub fn get_fullscreen(&self, output: &Output) -> Option<&CosmicWindow> { - self.fullscreen - .get(output) - .filter(|f| f.alive() && f.exclusive) - .filter(|f| f.ended_at.is_none() && f.start_at.is_none()) - .map(|f| &f.window) - } - - pub fn get_maximized(&self, output: &Output) -> Option<&CosmicWindow> { + pub fn get_fullscreen(&self) -> Option<&CosmicSurface> { self.fullscreen - .get(output) - .filter(|f| f.alive() && !f.exclusive) + .as_ref() + .filter(|f| f.alive()) .filter(|f| f.ended_at.is_none() && f.start_at.is_none()) - .map(|f| &f.window) + .map(|f| &f.surface) } pub fn resize_request( @@ -690,8 +603,8 @@ impl Workspace { if let Some(toplevel) = focused.toplevel() { if self .fullscreen - .values() - .any(|f| f.window.surface().wl_surface().as_ref() == Some(&toplevel)) + .as_ref() + .is_some_and(|f| f.surface.wl_surface().as_ref() == Some(&toplevel)) { return false; } @@ -713,12 +626,24 @@ impl Workspace { indicator_thickness: u8, ) -> Option { let pointer = seat.get_pointer().unwrap(); - let pos = pointer.current_location(); + let pos = pointer.current_location().as_global(); + + if self + .fullscreen + .as_ref() + .is_some_and(|f| &f.surface == window) + { + self.remove_fullscreen(); + } let mapped = self.element_for_surface(&window)?.clone(); - let mut initial_window_location = self.element_geometry(&mapped).unwrap().loc; + let mut initial_window_location = self + .element_geometry(&mapped) + .unwrap() + .loc + .to_global(&self.output); - if mapped.is_fullscreen(true) || mapped.is_maximized(true) { + if mapped.is_maximized(true) { // If surface is maximized then unmaximize it let new_size = self.unmaximize_request(window); let ratio = pos.x / output.geometry().size.w as f64; @@ -730,7 +655,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 +679,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 +692,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 +703,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) } } } @@ -808,37 +731,23 @@ impl Workspace { pub fn is_fullscreen(&self, mapped: &CosmicMapped) -> bool { self.fullscreen - .values() - .any(|f| f.exclusive && f.window.surface() == mapped.active_window()) - } - - pub fn is_maximized(&self, mapped: &CosmicMapped) -> bool { - self.fullscreen - .values() - .any(|f| !f.exclusive && f.window.surface() == mapped.active_window()) + .as_ref() + .is_some_and(|f| f.surface == mapped.active_window()) } pub fn is_floating(&self, mapped: &CosmicMapped) -> bool { - !self - .fullscreen - .values() - .any(|f| f.window.surface() == mapped.active_window()) - && self.floating_layer.mapped().any(|m| m == mapped) + !self.is_fullscreen(mapped) && self.floating_layer.mapped().any(|m| m == mapped) } pub fn is_tiled(&self, mapped: &CosmicMapped) -> bool { - !self - .fullscreen - .values() - .any(|f| f.window.surface() == mapped.active_window()) - && self.tiling_layer.mapped().any(|(_, m, _)| m == mapped) + !self.is_fullscreen(mapped) && self.tiling_layer.mapped().any(|(_, m, _)| m == mapped) } 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 +755,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 +766,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, }), @@ -877,7 +786,7 @@ impl Workspace { seat: &Seat, swap_desc: Option, ) -> FocusResult { - if self.fullscreen.contains_key(&seat.active_output()) { + if self.fullscreen.is_some() { return FocusResult::None; } @@ -895,8 +804,8 @@ impl Workspace { direction: Direction, seat: &Seat, ) -> MoveResult { - if let Some(f) = self.fullscreen.get(&seat.active_output()) { - MoveResult::MoveFurther(KeyboardFocusTarget::Fullscreen(f.window.clone())) + if let Some(f) = self.fullscreen.as_ref() { + MoveResult::MoveFurther(KeyboardFocusTarget::Fullscreen(f.surface.clone())) } else { self.floating_layer .move_current_element(direction, seat) @@ -904,10 +813,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>, @@ -935,20 +843,28 @@ impl Workspace { let mut window_elements = Vec::new(); let mut popup_elements = Vec::new(); - let output_scale = output.current_scale().fractional_scale(); - let layer_map = layer_map_for_output(output); - let zone = layer_map.non_exclusive_zone(); + let output_scale = self.output.current_scale().fractional_scale(); + let zone = { + let layer_map = layer_map_for_output(&self.output); + layer_map.non_exclusive_zone().as_local() + }; // OR windows above all popup_elements.extend( override_redirect_windows .iter() - .filter(|or| (*or).geometry().intersection(output.geometry()).is_some()) + .filter(|or| { + (*or) + .geometry() + .as_global() + .intersection(self.output.geometry()) + .is_some() + }) .flat_map(|or| { AsRenderElements::::render_elements::>( or, renderer, - (or.geometry().loc - output.geometry().loc) + (or.geometry().loc - self.output.geometry().loc.as_logical()) .to_physical_precise_round(output_scale), Scale::from(output_scale), 1.0, @@ -956,32 +872,27 @@ impl Workspace { }), ); - if let Some(fullscreen) = self.fullscreen.get(output) { + if let Some(fullscreen) = self.fullscreen.as_ref() { // fullscreen window - let bbox = fullscreen.window.bbox(); + let bbox = fullscreen.surface.bbox().as_local(); let element_geo = Rectangle::from_loc_and_size( - self.element_for_surface(&fullscreen.window.surface()) + self.element_for_surface(&fullscreen.surface) .and_then(|elem| { self.floating_layer - .space .element_geometry(elem) .or_else(|| self.tiling_layer.element_geometry(elem)) .map(|mut geo| { - geo.loc -= elem.geometry().loc; + geo.loc -= elem.geometry().loc.as_local(); geo }) }) .unwrap_or(bbox) .loc, - fullscreen.original_size, + fullscreen.original_geometry.size.as_local(), ); - let mut full_geo = if fullscreen.exclusive { - Rectangle::from_loc_and_size((0, 0), output.geometry().size) - } else { - layer_map.non_exclusive_zone() - }; - + let mut full_geo = + Rectangle::from_loc_and_size((0, 0), self.output.geometry().size.as_local()); if fullscreen.start_at.is_none() { if bbox != full_geo { if bbox.size.w < full_geo.size.w { @@ -1027,14 +938,17 @@ impl Workspace { (None, None) => (full_geo, 1.0), }; - let render_loc = target_geo.loc.to_physical_precise_round(output_scale); + let render_loc = target_geo + .loc + .as_logical() + .to_physical_precise_round(output_scale); let scale = Scale { x: target_geo.size.w as f64 / bbox.size.w as f64, y: target_geo.size.h as f64 / bbox.size.h as f64, }; let (w_elements, p_elements) = fullscreen - .window + .surface .split_render_elements::>( renderer, render_loc, @@ -1052,12 +966,12 @@ impl Workspace { if self .fullscreen - .get(output) + .as_ref() .map(|f| f.start_at.is_some() || f.ended_at.is_some()) .unwrap_or(true) { let focused = draw_focus_indicator - .filter(|_| !self.fullscreen.contains_key(output)) + .filter(|_| !self.fullscreen.is_some()) .and_then(|seat| self.focus_stack.get(seat).last().cloned()); // floating surfaces @@ -1078,9 +992,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, @@ -1102,11 +1015,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 e4052048..d44f4aa3 100644 --- a/src/state.rs +++ b/src/state.rs @@ -512,50 +512,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, + ); } }); @@ -566,10 +550,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/utils/geometry.rs b/src/utils/geometry.rs new file mode 100644 index 00000000..19e6f6ed --- /dev/null +++ b/src/utils/geometry.rs @@ -0,0 +1,149 @@ +use smithay::{ + output::Output, + utils::{Coordinate, Logical, Point, Rectangle, Size}, +}; + +use super::prelude::OutputExt; + +/// Marker type for coordinates in global space +#[derive(Debug)] +pub struct Global; + +/// Marker type for coordinates in workspace local space +#[derive(Debug)] +pub struct Local; + +pub trait PointExt { + fn as_global(self) -> Point; + fn as_local(self) -> Point; +} + +pub trait PointGlobalExt { + fn to_local(self, output: &Output) -> Point; + fn as_logical(self) -> Point; +} + +pub trait PointLocalExt { + fn to_global(self, output: &Output) -> Point; + fn as_logical(self) -> Point; +} + +pub trait SizeExt { + fn as_logical(self) -> Size; + fn as_local(self) -> Size; + fn as_global(self) -> Size; +} + +pub trait RectExt { + fn as_global(self) -> Rectangle; + fn as_local(self) -> Rectangle; +} + +pub trait RectGlobalExt { + fn to_local(self, output: &Output) -> Rectangle; + fn as_logical(self) -> Rectangle; +} + +pub trait RectLocalExt { + fn to_global(self, output: &Output) -> Rectangle; + fn as_logical(self) -> Rectangle; +} + +impl PointExt for Point { + fn as_global(self) -> Point { + (self.x, self.y).into() + } + + fn as_local(self) -> Point { + (self.x, self.y).into() + } +} + +impl PointGlobalExt for Point { + fn to_local(self, output: &Output) -> Point { + let point = (self.to_f64() - output.geometry().loc.to_f64()).as_logical(); + (C::from_f64(point.x), C::from_f64(point.y)).into() + } + + fn as_logical(self) -> Point { + (self.x, self.y).into() + } +} + +impl PointLocalExt for Point { + fn to_global(self, output: &Output) -> Point { + let point = + (self.to_f64().as_logical() + output.geometry().loc.to_f64().as_logical()).as_global(); + (C::from_f64(point.x), C::from_f64(point.y)).into() + } + + fn as_logical(self) -> Point { + (self.x, self.y).into() + } +} + +impl SizeExt for Size { + fn as_logical(self) -> Size { + (self.w, self.h).into() + } + fn as_global(self) -> Size { + self + } + fn as_local(self) -> Size { + (self.w, self.h).into() + } +} + +impl SizeExt for Size { + fn as_logical(self) -> Size { + (self.w, self.h).into() + } + fn as_global(self) -> Size { + (self.w, self.h).into() + } + fn as_local(self) -> Size { + self + } +} + +impl SizeExt for Size { + fn as_logical(self) -> Size { + self + } + fn as_global(self) -> Size { + (self.w, self.h).into() + } + fn as_local(self) -> Size { + (self.w, self.h).into() + } +} + +impl RectExt for Rectangle { + fn as_global(self) -> Rectangle { + Rectangle::from_loc_and_size(self.loc.as_global(), (self.size.w, self.size.h)) + } + + fn as_local(self) -> Rectangle { + Rectangle::from_loc_and_size(self.loc.as_local(), (self.size.w, self.size.h)) + } +} + +impl RectGlobalExt for Rectangle { + fn to_local(self, output: &Output) -> Rectangle { + Rectangle::from_loc_and_size(self.loc.to_local(output), (self.size.w, self.size.h)) + } + + fn as_logical(self) -> Rectangle { + Rectangle::from_loc_and_size(self.loc.as_logical(), self.size.as_logical()) + } +} + +impl RectLocalExt for Rectangle { + fn to_global(self, output: &Output) -> Rectangle { + Rectangle::from_loc_and_size(self.loc.to_global(output), (self.size.w, self.size.h)) + } + + fn as_logical(self) -> Rectangle { + Rectangle::from_loc_and_size(self.loc.as_logical(), self.size.as_logical()) + } +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index ff39a61d..6531f067 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -2,6 +2,7 @@ mod ids; pub(crate) use self::ids::id_gen; +pub mod geometry; pub mod iced; pub mod prelude; pub mod tween; diff --git a/src/utils/prelude.rs b/src/utils/prelude.rs index e4cac742..6135a053 100644 --- a/src/utils/prelude.rs +++ b/src/utils/prelude.rs @@ -11,20 +11,21 @@ use smithay::{ Seat, }, output::Output, - utils::{Buffer, IsAlive, Logical, Monotonic, Point, Rectangle, Time, Transform}, + utils::{Buffer, IsAlive, Monotonic, Point, Rectangle, Time, Transform}, wayland::compositor::with_states, }; +pub use super::geometry::*; pub use crate::shell::{Shell, Workspace}; pub use crate::state::{Common, State}; pub use crate::wayland::handlers::xdg_shell::popup::update_reactive_popups; pub trait OutputExt { - fn geometry(&self) -> Rectangle; + fn geometry(&self) -> Rectangle; } impl OutputExt for Output { - fn geometry(&self) -> Rectangle { + fn geometry(&self) -> Rectangle { Rectangle::from_loc_and_size(self.current_location(), { Transform::from(self.current_transform()) .transform_size( @@ -36,6 +37,7 @@ impl OutputExt for Output { .to_logical(self.current_scale().fractional_scale()) .to_i32_round() }) + .as_global() } } 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 dd775448..1aeffcb7 100644 --- a/src/wayland/handlers/screencopy.rs +++ b/src/wayland/handlers/screencopy.rs @@ -47,7 +47,7 @@ use crate::{ }, shell::{CosmicMappedRenderElement, CosmicSurface, WorkspaceRenderElement}, state::{BackendData, ClientState, Common, State}, - utils::prelude::OutputExt, + utils::prelude::{OutputExt, PointExt}, wayland::protocols::{ screencopy::{ delegate_screencopy, BufferInfo, BufferParams, CursorMode as ScreencopyCursorMode, @@ -106,7 +106,7 @@ impl ScreencopyHandler for State { if let Some(pointer) = seat.get_pointer() { if output .geometry() - .contains(pointer.current_location().to_i32_round()) + .contains(pointer.current_location().to_i32_round().as_global()) { session.cursor_enter(seat, InputType::Pointer); } @@ -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/wayland/handlers/xdg_shell/popup.rs b/src/wayland/handlers/xdg_shell/popup.rs index 99842a3a..225968f7 100644 --- a/src/wayland/handlers/xdg_shell/popup.rs +++ b/src/wayland/handlers/xdg_shell/popup.rs @@ -27,27 +27,28 @@ use std::sync::Mutex; use tracing::{trace, warn}; impl Shell { - pub fn unconstrain_popup(&self, surface: &PopupSurface, positioner: &PositionerState) { + pub fn unconstrain_popup(&self, surface: &PopupSurface) { if let Some(parent) = get_popup_toplevel(&surface) { if let Some(elem) = self.element_for_wl_surface(&parent) { let workspace = self.space_for(elem).unwrap(); - let mut element_geo = workspace.element_geometry(elem).unwrap(); + let mut element_geo = workspace + .element_geometry(elem) + .unwrap() + .to_global(workspace.output()); let (window, offset) = elem .windows() .find(|(w, _)| w.wl_surface().as_ref() == Some(&parent)) .unwrap(); let window_geo_offset = window.geometry().loc; - let window_loc = element_geo.loc + offset + window_geo_offset; - let anchor_point = get_anchor_point(&positioner) + window_loc; + let window_loc: Point = + element_geo.loc + offset.as_global() + window_geo_offset.as_global(); if workspace.is_tiled(elem) { - element_geo.loc = (0, 0).into(); //-= window_loc; - if !unconstrain_xdg_popup_tile(surface, element_geo) { - if let Some(output) = workspace.output_under(anchor_point) { - unconstrain_xdg_popup(surface, window_loc, output.geometry()); - } + element_geo.loc = (0, 0).into(); + if !unconstrain_xdg_popup_tile(surface, element_geo.as_logical()) { + unconstrain_xdg_popup(surface, window_loc, workspace.output().geometry()); } - } else if let Some(output) = workspace.output_under(anchor_point) { - unconstrain_xdg_popup(surface, window_loc, output.geometry()); + } else { + unconstrain_xdg_popup(surface, window_loc, workspace.output().geometry()); } } else if let Some((output, layer_surface)) = self.outputs().find_map(|o| { let map = layer_map_for_output(o); @@ -62,7 +63,7 @@ impl Shell { pub fn update_reactive_popups<'a>( window: &Window, - loc: Point, + loc: Point, outputs: impl Iterator, ) { let output_geo = outputs.map(|o| o.geometry()).collect::>(); @@ -79,7 +80,7 @@ pub fn update_reactive_popups<'a>( attributes.current.positioner.clone() }); if positioner.reactive { - let anchor_point = get_anchor_point(&positioner) + loc; + let anchor_point = loc + get_anchor_point(&positioner).as_global(); if let Some(rect) = output_geo .iter() .find(|geo| geo.contains(anchor_point)) @@ -116,11 +117,11 @@ fn unconstrain_xdg_popup_tile(surface: &PopupSurface, rect: Rectangle, - rect: Rectangle, + window_loc: Point, + mut rect: Rectangle, ) { - let mut relative = rect; - relative.loc -= window_loc; + rect.loc -= window_loc; + let relative = rect.as_logical(); let geometry = surface.with_pending_state(|state| state.positioner.get_geometry()); let offset = check_constrained(geometry, relative); @@ -139,7 +140,7 @@ fn unconstrain_layer_popup(surface: &PopupSurface, output: &Output, layer_surfac let layer_geo = map.layer_geometry(layer_surface).unwrap(); // the output_rect represented relative to the parents coordinate system - let mut relative = Rectangle::from_loc_and_size((0, 0), output.geometry().size); + let mut relative = Rectangle::from_loc_and_size((0, 0), output.geometry().size).as_logical(); relative.loc -= layer_geo.loc; let geometry = surface.with_pending_state(|state| state.positioner.get_geometry()); let offset = check_constrained(geometry, relative); diff --git a/src/xwayland.rs b/src/xwayland.rs index cc3b6d52..cef811d8 100644 --- a/src/xwayland.rs +++ b/src/xwayland.rs @@ -276,13 +276,16 @@ impl XwmHandler for State { { let space = self.common.shell.space_for(mapped).unwrap(); if space.is_floating(mapped) { - mapped.set_geometry(Rectangle::from_loc_and_size( - current_geo.loc, - ( - w.map(|w| w as i32).unwrap_or(current_geo.size.w), - h.map(|h| h as i32).unwrap_or(current_geo.size.h), - ), - )) + mapped.set_geometry( + Rectangle::from_loc_and_size( + current_geo.loc, + ( + w.map(|w| w as i32).unwrap_or(current_geo.size.w), + h.map(|h| h as i32).unwrap_or(current_geo.size.h), + ), + ) + .as_global(), + ) } } else { if let Some(x) = x { @@ -324,13 +327,13 @@ impl XwmHandler for State { } } - let geo = window.geometry(); + let geo = window.geometry().as_global(); for (output, overlap) in self.common.shell.outputs().cloned().map(|o| { let intersection = o.geometry().intersection(geo); (o, intersection) }) { if let Some(overlap) = overlap { - window.output_enter(&output, overlap); + window.output_enter(&output, overlap.as_logical()); } else { window.output_leave(&output); } @@ -359,14 +362,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); } } } @@ -382,18 +382,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) } } }