From ad35c18cda172428529ddc0818b6d93cf8163e0e Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Fri, 27 Sep 2024 23:39:08 +0200 Subject: [PATCH 1/3] focus: Introduce render_input_order --- src/shell/focus/mod.rs | 2 + src/shell/focus/order.rs | 374 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 376 insertions(+) create mode 100644 src/shell/focus/order.rs diff --git a/src/shell/focus/mod.rs b/src/shell/focus/mod.rs index e2038375..a3b855a3 100644 --- a/src/shell/focus/mod.rs +++ b/src/shell/focus/mod.rs @@ -21,10 +21,12 @@ use std::{borrow::Cow, mem, sync::Mutex}; use tracing::{debug, trace}; +pub use self::order::{render_input_order, Stage}; use self::target::{KeyboardFocusTarget, WindowGroup}; use super::{grabs::SeatMoveGrabState, layout::floating::FloatingLayout, SeatExt}; +mod order; pub mod target; pub struct FocusStack<'a>(pub(super) Option<&'a IndexSet>); diff --git a/src/shell/focus/order.rs b/src/shell/focus/order.rs new file mode 100644 index 00000000..f035d908 --- /dev/null +++ b/src/shell/focus/order.rs @@ -0,0 +1,374 @@ +use std::{ops::ControlFlow, time::Instant}; + +use cosmic_comp_config::workspace::WorkspaceLayout; +use keyframe::{ease, functions::EaseInOutCubic}; +use smithay::{ + desktop::{layer_map_for_output, LayerSurface, PopupKind, PopupManager}, + output::{Output, OutputNoMode}, + utils::{Logical, Point}, + wayland::{session_lock::LockSurface, shell::wlr_layer::Layer}, + xwayland::X11Surface, +}; + +use crate::{ + backend::render::ElementFilter, + shell::{ + layout::{floating::FloatingLayout, tiling::ANIMATION_DURATION}, + Shell, Workspace, WorkspaceDelta, + }, + utils::{geometry::*, prelude::OutputExt, quirks::WORKSPACE_OVERVIEW_NAMESPACE}, + wayland::protocols::workspace::WorkspaceHandle, +}; + +pub enum Stage<'a> { + SessionLock(Option<&'a LockSurface>), + LayerPopup { + layer: LayerSurface, + popup: &'a PopupKind, + location: Point, + }, + LayerSurface { + layer: LayerSurface, + location: Point, + }, + OverrideRedirect { + surface: &'a X11Surface, + location: Point, + }, + StickyPopups(&'a FloatingLayout), + Sticky(&'a FloatingLayout), + WorkspacePopups { + workspace: &'a Workspace, + offset: Point, + }, + Workspace { + workspace: &'a Workspace, + offset: Point, + }, +} + +pub fn render_input_order( + shell: &Shell, + output: &Output, + previous: Option<(WorkspaceHandle, usize, WorkspaceDelta)>, + current: (WorkspaceHandle, usize), + element_filter: ElementFilter, + callback: impl FnMut(Stage) -> ControlFlow, ()>, +) -> Result { + match render_input_order_internal(shell, output, previous, current, element_filter, callback) { + ControlFlow::Break(result) => result, + ControlFlow::Continue(_) => Ok(R::default()), + } +} + +fn render_input_order_internal( + shell: &Shell, + output: &Output, + previous: Option<(WorkspaceHandle, usize, WorkspaceDelta)>, + current: (WorkspaceHandle, usize), + element_filter: ElementFilter, + mut callback: impl FnMut(Stage) -> ControlFlow, ()>, +) -> ControlFlow, ()> { + // Session Lock + if let Some(session_lock) = &shell.session_lock { + return callback(Stage::SessionLock(session_lock.surfaces.get(output))); + } + + // Overlay-level layer shell + // overlay is above everything + for (layer, popup, location) in layer_popups(output, Layer::Overlay, element_filter) { + callback(Stage::LayerPopup { + layer, + popup: &popup, + location, + })?; + } + for (layer, location) in layer_surfaces(output, Layer::Overlay, element_filter) { + callback(Stage::LayerSurface { layer, location })?; + } + + // calculate a bunch of stuff for workspace transitions + + let Some(set) = shell.workspaces.sets.get(output) else { + return ControlFlow::Break(Err(OutputNoMode)); + }; + let Some(workspace) = set.workspaces.iter().find(|w| w.handle == current.0) else { + return ControlFlow::Break(Err(OutputNoMode)); + }; + let output_size = output.geometry().size; + let has_fullscreen = workspace + .fullscreen + .as_ref() + .filter(|f| !f.is_animating()) + .is_some(); + + let (previous, current_offset) = match previous.as_ref() { + Some((previous, previous_idx, start)) => { + let layout = shell.workspaces.layout; + + let Some(workspace) = shell.workspaces.space_for_handle(&previous) else { + return ControlFlow::Break(Err(OutputNoMode)); + }; + let has_fullscreen = workspace.fullscreen.is_some(); + + let percentage = match start { + WorkspaceDelta::Shortcut(st) => ease( + EaseInOutCubic, + 0.0, + 1.0, + Instant::now().duration_since(*st).as_millis() as f32 + / ANIMATION_DURATION.as_millis() as f32, + ), + WorkspaceDelta::Gesture(prog) => *prog as f32, + WorkspaceDelta::GestureEnd(st, spring) => { + (spring.value_at(Instant::now().duration_since(*st)) as f32).clamp(0.0, 1.0) + } + }; + + let offset = Point::::from(match (layout, *previous_idx < current.1) { + (WorkspaceLayout::Vertical, true) => { + (0, (-output_size.h as f32 * percentage).round() as i32) + } + (WorkspaceLayout::Vertical, false) => { + (0, (output_size.h as f32 * percentage).round() as i32) + } + (WorkspaceLayout::Horizontal, true) => { + ((-output_size.w as f32 * percentage).round() as i32, 0) + } + (WorkspaceLayout::Horizontal, false) => { + ((output_size.w as f32 * percentage).round() as i32, 0) + } + }); + + ( + Some((previous, has_fullscreen, offset)), + Point::::from(match (layout, *previous_idx < current.1) { + (WorkspaceLayout::Vertical, true) => (0, output_size.h + offset.y), + (WorkspaceLayout::Vertical, false) => (0, -(output_size.h - offset.y)), + (WorkspaceLayout::Horizontal, true) => (output_size.w + offset.x, 0), + (WorkspaceLayout::Horizontal, false) => (-(output_size.w - offset.x), 0), + }), + ) + } + None => (None, Point::default()), + }; + + // Top-level layer shell popups + if !has_fullscreen { + for (layer, popup, location) in layer_popups(output, Layer::Top, element_filter) { + callback(Stage::LayerPopup { + layer, + popup: &popup, + location, + })?; + } + } + + if element_filter != ElementFilter::LayerShellOnly { + // overlay redirect windows + // they need to be over sticky windows, because they could be popups of sticky windows, + // and we can't differenciate that. + for (surface, location) in shell + .override_redirect_windows + .iter() + .filter(|or| { + (*or) + .geometry() + .as_global() + .intersection(output.geometry()) + .is_some() + }) + .map(|or| (or, or.geometry().loc.as_global())) + { + callback(Stage::OverrideRedirect { surface, location })?; + } + + // sticky window popups + if !has_fullscreen { + callback(Stage::StickyPopups(&set.sticky_layer))?; + } + } + + if element_filter != ElementFilter::LayerShellOnly { + // previous workspace popups + if let Some((previous_handle, _, offset)) = previous.as_ref() { + let Some(workspace) = shell.workspaces.space_for_handle(previous_handle) else { + return ControlFlow::Break(Err(OutputNoMode)); + }; + + callback(Stage::WorkspacePopups { + workspace, + offset: *offset, + })?; + } + + // current workspace popups + let Some(workspace) = shell.workspaces.space_for_handle(¤t.0) else { + return ControlFlow::Break(Err(OutputNoMode)); + }; + + callback(Stage::WorkspacePopups { + workspace, + offset: current_offset, + })?; + } + + if !has_fullscreen { + // bottom layer popups + for (layer, popup, location) in layer_popups(output, Layer::Bottom, element_filter) { + callback(Stage::LayerPopup { + layer, + popup: &popup, + location, + })?; + } + } + + if let Some((_, has_fullscreen, offset)) = previous.as_ref() { + if !has_fullscreen { + // previous bottom layer popups + for (layer, popup, location) in layer_popups(output, Layer::Bottom, element_filter) { + callback(Stage::LayerPopup { + layer, + popup: &popup, + location: location + offset.as_global(), + })?; + } + } + } + + if !has_fullscreen { + // background layer popups + for (layer, popup, location) in layer_popups(output, Layer::Background, element_filter) { + callback(Stage::LayerPopup { + layer, + popup: &popup, + location, + })?; + } + } + + if let Some((_, has_fullscreen, offset)) = previous.as_ref() { + if !has_fullscreen { + // previous background layer popups + for (layer, popup, location) in layer_popups(output, Layer::Background, element_filter) + { + callback(Stage::LayerPopup { + layer, + popup: &popup, + location: location + offset.as_global(), + })?; + } + } + } + + if !has_fullscreen { + // top-layer shell + for (layer, location) in layer_surfaces(output, Layer::Top, element_filter) { + callback(Stage::LayerSurface { layer, location })?; + } + + // sticky windows + if element_filter != ElementFilter::LayerShellOnly { + callback(Stage::Sticky(&set.sticky_layer))?; + } + } + + if element_filter != ElementFilter::LayerShellOnly { + // workspace windows + callback(Stage::Workspace { + workspace, + offset: current_offset, + })?; + + // previous workspace windows + if let Some((previous_handle, _, offset)) = previous.as_ref() { + let Some(workspace) = shell.workspaces.space_for_handle(previous_handle) else { + return ControlFlow::Break(Err(OutputNoMode)); + }; + callback(Stage::Workspace { + workspace, + offset: *offset, + })?; + } + } + + if !has_fullscreen { + // bottom layer + for (layer, mut location) in layer_surfaces(output, Layer::Bottom, element_filter) { + location += current_offset.as_global(); + callback(Stage::LayerSurface { layer, location })?; + } + } + + if let Some((_, has_fullscreen, offset)) = previous.as_ref() { + if !has_fullscreen { + // previous bottom layer + for (layer, mut location) in layer_surfaces(output, Layer::Bottom, element_filter) { + location += offset.as_global(); + callback(Stage::LayerSurface { layer, location })?; + } + } + } + + if !has_fullscreen { + // background layer + for (layer, mut location) in layer_surfaces(output, Layer::Background, element_filter) { + location += current_offset.as_global(); + callback(Stage::LayerSurface { layer, location })?; + } + } + + if let Some((_, has_fullscreen, offset)) = previous.as_ref() { + if !has_fullscreen { + // previous background layer + for (layer, mut location) in layer_surfaces(output, Layer::Background, element_filter) { + location += offset.as_global(); + callback(Stage::LayerSurface { layer, location })?; + } + } + } + + ControlFlow::Continue(()) +} + +fn layer_popups<'a>( + output: &'a Output, + layer: Layer, + element_filter: ElementFilter, +) -> impl Iterator)> + 'a { + layer_surfaces(output, layer, element_filter).flat_map(move |(surface, location)| { + let location_clone = location.clone(); + let surface_clone = surface.clone(); + PopupManager::popups_for_surface(surface.wl_surface()).map(move |(popup, popup_offset)| { + let offset = (popup_offset - popup.geometry().loc) + .as_local() + .to_global(output); + (surface_clone.clone(), popup, (location_clone + offset)) + }) + }) +} + +fn layer_surfaces<'a>( + output: &'a Output, + layer: Layer, + element_filter: ElementFilter, +) -> impl Iterator)> + 'a { + // we want to avoid deadlocks on the layer-map in callbacks, so we need to clone the layer surfaces + let layers = { + let layer_map = layer_map_for_output(output); + layer_map + .layers_on(layer) + .rev() + .map(|s| (s.clone(), layer_map.layer_geometry(s).unwrap())) + .collect::>() + }; + + layers + .into_iter() + .filter(move |(s, _)| { + !(element_filter == ElementFilter::ExcludeWorkspaceOverview + && s.namespace() == WORKSPACE_OVERVIEW_NAMESPACE) + }) + .map(|(surface, geometry)| (surface, geometry.loc.as_local().to_global(output))) +} From 13f6428ce32ace8cbd480144ca42dd568cb91560 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Fri, 27 Sep 2024 23:41:58 +0200 Subject: [PATCH 2/3] render: Use `render_input_order` --- src/backend/render/element.rs | 6 +- src/backend/render/mod.rs | 580 ++++++++++-------------- src/shell/element/mod.rs | 73 +-- src/shell/element/stack.rs | 59 ++- src/shell/element/surface.rs | 65 +-- src/shell/element/window.rs | 71 ++- src/shell/grabs/moving.rs | 19 +- src/shell/layout/floating/mod.rs | 66 ++- src/shell/layout/tiling/mod.rs | 740 +++++++++++++++++++++---------- src/shell/workspace.rs | 236 ++++++++-- 10 files changed, 1153 insertions(+), 762 deletions(-) diff --git a/src/backend/render/element.rs b/src/backend/render/element.rs index dfedb3b8..3f8af67e 100644 --- a/src/backend/render/element.rs +++ b/src/backend/render/element.rs @@ -24,7 +24,7 @@ where ::TextureId: 'static, CosmicMappedRenderElement: RenderElement, { - Workspace(RelocateRenderElement>), + Workspace(RelocateRenderElement>>), Cursor(RelocateRenderElement>), Dnd(WaylandSurfaceRenderElement), MoveGrab(CosmicMappedRenderElement), @@ -266,13 +266,13 @@ where } } -impl From> for CosmicElement +impl From>> for CosmicElement where R: Renderer + ImportAll + ImportMem + AsGlowRenderer, ::TextureId: 'static, CosmicMappedRenderElement: RenderElement, { - fn from(elem: WorkspaceRenderElement) -> Self { + fn from(elem: CropRenderElement>) -> Self { Self::Workspace(RelocateRenderElement::from_element( elem, (0, 0), diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index 80c78225..aa545068 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -4,6 +4,7 @@ use std::{ borrow::Borrow, cell::RefCell, collections::HashMap, + ops::ControlFlow, sync::{Arc, RwLock, Weak}, time::Instant, }; @@ -14,16 +15,13 @@ use crate::{ backend::{kms::render::gles::GbmGlowBackend, render::element::DamageElement}, shell::{ element::CosmicMappedKey, - focus::target::WindowGroup, + focus::{render_input_order, target::WindowGroup, Stage}, grabs::{SeatMenuGrabState, SeatMoveGrabState}, layout::tiling::ANIMATION_DURATION, - CosmicMappedRenderElement, OverviewMode, SeatExt, SessionLock, Trigger, WorkspaceDelta, + CosmicMappedRenderElement, OverviewMode, SeatExt, Trigger, WorkspaceDelta, WorkspaceRenderElement, }, - utils::{ - prelude::*, - quirks::{workspace_overview_is_open, WORKSPACE_OVERVIEW_NAMESPACE}, - }, + utils::{prelude::*, quirks::workspace_overview_is_open}, wayland::{ handlers::{ data_device::get_dnd_icon, @@ -34,9 +32,7 @@ use crate::{ }; use cosmic::Theme; -use cosmic_comp_config::workspace::WorkspaceLayout; use element::FromGlesError; -use keyframe::{ease, functions::EaseInOutCubic}; use smithay::{ backend::{ allocator::dmabuf::Dmabuf, @@ -46,7 +42,7 @@ use smithay::{ damage::{Error as RenderError, OutputDamageTracker, RenderOutputResult}, element::{ surface::{render_elements_from_surface_tree, WaylandSurfaceRenderElement}, - utils::{Relocate, RelocateRenderElement}, + utils::{CropRenderElement, Relocate, RelocateRenderElement}, AsRenderElements, Element, Id, Kind, RenderElement, }, gles::{ @@ -60,13 +56,12 @@ use smithay::{ TextureFilter, }, }, - desktop::{layer_map_for_output, PopupManager}, input::Seat, output::{Output, OutputNoMode}, - utils::{IsAlive, Logical, Monotonic, Physical, Point, Rectangle, Scale, Time, Transform}, + utils::{IsAlive, Logical, Monotonic, Point, Rectangle, Scale, Time, Transform}, wayland::{ dmabuf::get_dmabuf, - shell::wlr_layer::Layer, + session_lock::LockSurface, shm::{shm_format_to_fourcc, with_buffer_contents}, }, }; @@ -504,60 +499,6 @@ pub enum ElementFilter { LayerShellOnly, } -#[derive(Clone, Debug)] -pub struct SplitRenderElements { - pub w_elements: Vec, - pub p_elements: Vec, -} - -impl Default for SplitRenderElements { - fn default() -> Self { - Self { - w_elements: Vec::new(), - p_elements: Vec::new(), - } - } -} - -impl SplitRenderElements { - pub fn extend(&mut self, other: Self) { - self.w_elements.extend(other.w_elements); - self.p_elements.extend(other.p_elements); - } - - pub fn extend_map E>(&mut self, other: SplitRenderElements, mut f: F) { - self.w_elements - .extend(other.w_elements.into_iter().map(&mut f)); - self.p_elements - .extend(other.p_elements.into_iter().map(&mut f)); - } - - pub fn join(mut self) -> Vec { - self.p_elements.extend(self.w_elements); - self.p_elements - } -} - -impl SplitRenderElements> -where - R: Renderer + ImportAll + ImportMem + AsGlowRenderer, - CosmicMappedRenderElement: RenderElement, -{ - fn extend_from_workspace_elements>>( - &mut self, - other: SplitRenderElements, - offset: Point, - ) { - self.extend_map(other, |element| { - CosmicElement::Workspace(RelocateRenderElement::from_element( - element.into(), - offset, - Relocate::Relative, - )) - }) - } -} - #[profiling::function] pub fn workspace_elements( _gpu: Option<&DrmNode>, @@ -578,7 +519,7 @@ where CosmicMappedRenderElement: RenderElement, WorkspaceRenderElement: RenderElement, { - let mut elements = SplitRenderElements::default(); + let mut elements = Vec::new(); let theme = shell.read().unwrap().theme().clone(); let seats = shell @@ -588,8 +529,9 @@ where .iter() .cloned() .collect::>(); + let scale = output.current_scale().fractional_scale(); - elements.p_elements.extend(cursor_elements( + elements.extend(cursor_elements( renderer, seats.iter(), &theme, @@ -602,7 +544,6 @@ where #[cfg(feature = "debug")] { let output_geo = output.geometry(); - let scale = output.current_scale().fractional_scale(); if let Some((state, timings)) = _fps { let debug_active = shell.read().unwrap().debug_active; @@ -621,23 +562,12 @@ where ) .map_err(FromGlesError::from_gles_error) .map_err(RenderError::Rendering)?; - elements.p_elements.push(fps_overlay.into()); + elements.push(fps_overlay.into()); } } let shell = shell.read().unwrap(); - // If session locked, only show session lock surfaces - if let Some(session_lock) = &shell.session_lock { - elements.p_elements.extend( - session_lock_elements(renderer, output, session_lock) - .into_iter() - .map(|x| WorkspaceRenderElement::from(x).into()), - ); - return Ok(elements.join()); - } - - let theme = theme.cosmic(); let overview = shell.overview_mode(); let (resize_mode, resize_indicator) = shell.resize_mode(); let resize_indicator = resize_indicator.map(|indicator| (resize_mode, indicator)); @@ -666,310 +596,252 @@ where .unwrap() .is_some(); let focused_output = last_active_seat.focused_or_active_output(); - let output_size = output.geometry().size; - let output_scale = output.current_scale().fractional_scale(); - let set = shell.workspaces.sets.get(output).ok_or(OutputNoMode)?; let workspace = set .workspaces .iter() .find(|w| w.handle == current.0) .ok_or(OutputNoMode)?; - let is_active_space = workspace.outputs().any(|o| o == &focused_output); - - let has_fullscreen = workspace - .fullscreen - .as_ref() - .filter(|f| !f.is_animating()) - .is_some(); - let overlay_elements = split_layer_elements(renderer, output, Layer::Overlay, element_filter); - - // overlay is above everything - elements - .p_elements - .extend(overlay_elements.p_elements.into_iter().map(Into::into)); - elements - .p_elements - .extend(overlay_elements.w_elements.into_iter().map(Into::into)); - - if !has_fullscreen { - elements.extend_from_workspace_elements( - split_layer_elements(renderer, output, Layer::Top, element_filter), - (0, 0).into(), - ); - }; - + let is_active_space = workspace.output == focused_output; let active_hint = if shell.active_hint { - theme.active_hint as u8 + theme.cosmic().active_hint as u8 } else { 0 }; - // overlay redirect windows - // they need to be over sticky windows, because they could be popups of sticky windows, - // and we can't differenciate that. - if element_filter != ElementFilter::LayerShellOnly { - elements.p_elements.extend( - shell - .override_redirect_windows - .iter() - .filter(|or| { - (*or) - .geometry() - .as_global() - .intersection(workspace.output.geometry()) - .is_some() - }) - .flat_map(|or| { - AsRenderElements::::render_elements::>( - or, - renderer, - (or.geometry().loc - workspace.output.geometry().loc.as_logical()) - .to_physical_precise_round(output_scale), - Scale::from(output_scale), - 1.0, - ) - }) - .map(|p_element| p_element.into()), - ); - } - - // sticky windows - if !has_fullscreen && element_filter != ElementFilter::LayerShellOnly { - let alpha = match &overview.0 { - OverviewMode::Started(_, started) => { - (1.0 - (Instant::now().duration_since(*started).as_millis() - / ANIMATION_DURATION.as_millis()) as f32) - .max(0.0) - * 0.4 - + 0.6 - } - OverviewMode::Ended(_, ended) => { - ((Instant::now().duration_since(*ended).as_millis() - / ANIMATION_DURATION.as_millis()) as f32) - * 0.4 - + 0.6 - } - OverviewMode::Active(_) => 0.6, - OverviewMode::None => 1.0, - }; - - let current_focus = (!move_active && is_active_space) - .then_some(last_active_seat) - .map(|seat| workspace.focus_stack.get(seat)); - - elements.extend_from_workspace_elements( - set.sticky_layer.render( - renderer, - current_focus.as_ref().and_then(|stack| stack.last()), - resize_indicator.clone(), - active_hint, - alpha, - theme, - ), - (0, 0).into(), - ); - } - - let offset = match previous.as_ref() { - Some((previous, previous_idx, start)) => { - let layout = shell.workspaces.layout; + let output_size = output + .geometry() + .size + .as_logical() + .to_physical_precise_round(scale); + let crop_to_output = |element: WorkspaceRenderElement| { + CropRenderElement::from_element( + element.into(), + scale, + Rectangle::from_loc_and_size((0, 0), output_size), + ) + }; - let workspace = shell - .workspaces - .space_for_handle(&previous) - .ok_or(OutputNoMode)?; - let has_fullscreen = workspace.fullscreen.is_some(); - let is_active_space = workspace.outputs().any(|o| o == &focused_output); - - let percentage = match start { - WorkspaceDelta::Shortcut(st) => ease( - EaseInOutCubic, - 0.0, - 1.0, - Instant::now().duration_since(*st).as_millis() as f32 - / ANIMATION_DURATION.as_millis() as f32, - ), - WorkspaceDelta::Gesture(prog) => *prog as f32, - WorkspaceDelta::GestureEnd(st, spring) => { - (spring.value_at(Instant::now().duration_since(*st)) as f32).clamp(0.0, 1.0) + render_input_order( + &*shell, + output, + previous, + current, + element_filter, + |stage| { + match stage { + Stage::SessionLock(lock_surface) => { + elements.extend( + session_lock_elements(renderer, output, lock_surface) + .into_iter() + .map(Into::into) + .flat_map(crop_to_output) + .map(Into::into), + ); } - }; - let offset = Point::::from(match (layout, *previous_idx < current.1) { - (WorkspaceLayout::Vertical, true) => { - (0, (-output_size.h as f32 * percentage).round() as i32) + Stage::LayerPopup { + popup, location, .. + } => { + elements.extend( + render_elements_from_surface_tree::<_, WorkspaceRenderElement<_>>( + renderer, + popup.wl_surface(), + location + .to_local(output) + .as_logical() + .to_physical_precise_round(scale), + Scale::from(scale), + 1.0, + Kind::Unspecified, + ) + .into_iter() + .flat_map(crop_to_output) + .map(Into::into), + ); } - (WorkspaceLayout::Vertical, false) => { - (0, (output_size.h as f32 * percentage).round() as i32) + Stage::LayerSurface { layer, location } => { + elements.extend( + render_elements_from_surface_tree::<_, WorkspaceRenderElement<_>>( + renderer, + &layer.wl_surface(), + location + .to_local(output) + .as_logical() + .to_physical_precise_round(scale), + Scale::from(scale), + 1.0, + Kind::Unspecified, + ) + .into_iter() + .flat_map(crop_to_output) + .map(Into::into), + ); } - (WorkspaceLayout::Horizontal, true) => { - ((-output_size.w as f32 * percentage).round() as i32, 0) + Stage::OverrideRedirect { surface, location } => { + elements.extend( + AsRenderElements::::render_elements::>( + surface, + renderer, + location + .to_local(output) + .as_logical() + .to_physical_precise_round(scale), + Scale::from(scale), + 1.0, + ) + .into_iter() + .flat_map(crop_to_output) + .map(Into::into), + ); } - (WorkspaceLayout::Horizontal, false) => { - ((output_size.w as f32 * percentage).round() as i32, 0) + Stage::StickyPopups(layout) => { + let alpha = match &overview.0 { + OverviewMode::Started(_, started) => { + (1.0 - (Instant::now().duration_since(*started).as_millis() + / ANIMATION_DURATION.as_millis()) + as f32) + .max(0.0) + * 0.4 + + 0.6 + } + OverviewMode::Ended(_, ended) => { + ((Instant::now().duration_since(*ended).as_millis() + / ANIMATION_DURATION.as_millis()) + as f32) + * 0.4 + + 0.6 + } + OverviewMode::Active(_) => 0.6, + OverviewMode::None => 1.0, + }; + + elements.extend( + layout + .render_popups(renderer, alpha) + .into_iter() + .map(Into::into) + .flat_map(crop_to_output) + .map(Into::into), + ); } - }); - - elements.extend_from_workspace_elements( - workspace - .render::( - renderer, - (!move_active && is_active_space).then_some(last_active_seat), - overview.clone(), - resize_indicator.clone(), - active_hint, - theme, + Stage::Sticky(layout) => { + let alpha = match &overview.0 { + OverviewMode::Started(_, started) => { + (1.0 - (Instant::now().duration_since(*started).as_millis() + / ANIMATION_DURATION.as_millis()) + as f32) + .max(0.0) + * 0.4 + + 0.6 + } + OverviewMode::Ended(_, ended) => { + ((Instant::now().duration_since(*ended).as_millis() + / ANIMATION_DURATION.as_millis()) + as f32) + * 0.4 + + 0.6 + } + OverviewMode::Active(_) => 0.6, + OverviewMode::None => 1.0, + }; + + let current_focus = (!move_active && is_active_space) + .then_some(last_active_seat) + .map(|seat| workspace.focus_stack.get(seat)); + + elements.extend( + layout + .render( + renderer, + current_focus.as_ref().and_then(|stack| stack.last()), + resize_indicator.clone(), + active_hint, + alpha, + &theme.cosmic(), + ) + .into_iter() + .map(Into::into) + .flat_map(crop_to_output) + .map(Into::into), ) - .map_err(|_| OutputNoMode)?, - offset.to_physical_precise_round(output_scale), - ); - - if !has_fullscreen { - elements.extend_from_workspace_elements( - background_layer_elements(renderer, output, element_filter), - offset.to_physical_precise_round(output_scale), - ); - } - - Point::::from(match (layout, *previous_idx < current.1) { - (WorkspaceLayout::Vertical, true) => (0, output_size.h + offset.y), - (WorkspaceLayout::Vertical, false) => (0, -(output_size.h - offset.y)), - (WorkspaceLayout::Horizontal, true) => (output_size.w + offset.x, 0), - (WorkspaceLayout::Horizontal, false) => (-(output_size.w - offset.x), 0), - }) - } - None => (0, 0).into(), - }; - - if element_filter != ElementFilter::LayerShellOnly { - elements.extend_from_workspace_elements( - workspace - .render::( - renderer, - (!move_active && is_active_space).then_some(&last_active_seat), - overview, - resize_indicator, - active_hint, - theme, - ) - .map_err(|_| OutputNoMode)?, - offset.to_physical_precise_round(output_scale), - ); - } - - if !has_fullscreen { - elements.extend_from_workspace_elements( - background_layer_elements(renderer, output, element_filter), - offset.to_physical_precise_round(output_scale), - ); - } - - Ok(elements.join()) -} - -pub fn split_layer_elements( - renderer: &mut R, - output: &Output, - layer: Layer, - element_filter: ElementFilter, -) -> SplitRenderElements> -where - R: Renderer + ImportAll + ImportMem + AsGlowRenderer, - ::TextureId: Clone + 'static, - ::Error: FromGlesError, - CosmicMappedRenderElement: RenderElement, - WorkspaceRenderElement: RenderElement, -{ - let layer_map = layer_map_for_output(output); - let output_scale = output.current_scale().fractional_scale(); - - let mut elements = SplitRenderElements::default(); - - layer_map - .layers_on(layer) - .rev() - .filter(|s| { - !(element_filter == ElementFilter::ExcludeWorkspaceOverview - && s.namespace() == WORKSPACE_OVERVIEW_NAMESPACE) - }) - .filter_map(|surface| { - layer_map - .layer_geometry(surface) - .map(|geo| (geo.loc, surface)) - }) - .for_each(|(location, surface)| { - let location = location.to_physical_precise_round(output_scale); - let surface = surface.wl_surface(); - let scale = Scale::from(output_scale); - - elements - .p_elements - .extend(PopupManager::popups_for_surface(surface).flat_map( - |(popup, popup_offset)| { - let offset = (popup_offset - popup.geometry().loc) - .to_f64() - .to_physical(scale) - .to_i32_round(); - - render_elements_from_surface_tree( + } + Stage::WorkspacePopups { workspace, offset } => { + elements.extend( + match workspace.render_popups( renderer, - popup.wl_surface(), - location + offset, - scale, - 1.0, - Kind::Unspecified, - ) - }, - )); - - elements - .w_elements - .extend(render_elements_from_surface_tree( - renderer, - surface, - location, - scale, - 1.0, - Kind::Unspecified, - )); - }); + (!move_active && is_active_space).then_some(last_active_seat), + overview.clone(), + &theme.cosmic(), + ) { + Ok(elements) => { + elements + .into_iter() + .flat_map(crop_to_output) + .map(|element| { + CosmicElement::Workspace( + RelocateRenderElement::from_element( + element, + offset.to_physical_precise_round(scale), + Relocate::Relative, + ), + ) + }) + } + Err(_) => { + return ControlFlow::Break(Err(OutputNoMode)); + } + }, + ); + } + Stage::Workspace { workspace, offset } => { + elements.extend( + match workspace.render( + renderer, + (!move_active && is_active_space).then_some(last_active_seat), + overview.clone(), + resize_indicator.clone(), + active_hint, + &theme.cosmic(), + ) { + Ok(elements) => { + elements + .into_iter() + .flat_map(crop_to_output) + .map(|element| { + CosmicElement::Workspace( + RelocateRenderElement::from_element( + element, + offset.to_physical_precise_round(scale), + Relocate::Relative, + ), + ) + }) + } + Err(_) => { + return ControlFlow::Break(Err(OutputNoMode)); + } + }, + ); + } + }; - elements -} + ControlFlow::Continue(()) + }, + )?; -// bottom and background layer surfaces -pub fn background_layer_elements( - renderer: &mut R, - output: &Output, - element_filter: ElementFilter, -) -> SplitRenderElements> -where - R: Renderer + ImportAll + ImportMem + AsGlowRenderer, - ::TextureId: Clone + 'static, - ::Error: FromGlesError, - CosmicMappedRenderElement: RenderElement, - WorkspaceRenderElement: RenderElement, -{ - let mut elements = split_layer_elements(renderer, output, Layer::Bottom, element_filter); - elements.extend(split_layer_elements( - renderer, - output, - Layer::Background, - element_filter, - )); - elements + Ok(elements) } fn session_lock_elements( renderer: &mut R, output: &Output, - session_lock: &SessionLock, + lock_surface: Option<&LockSurface>, ) -> Vec> where R: Renderer + ImportAll, ::TextureId: Clone + 'static, { - if let Some(surface) = session_lock.surfaces.get(output) { + if let Some(surface) = lock_surface { let scale = Scale::from(output.current_scale().fractional_scale()); render_elements_from_surface_tree( renderer, diff --git a/src/shell/element/mod.rs b/src/shell/element/mod.rs index 4a2cfb67..0860c07d 100644 --- a/src/shell/element/mod.rs +++ b/src/shell/element/mod.rs @@ -1,8 +1,5 @@ use crate::{ - backend::render::{ - element::{AsGlowRenderer, FromGlesError}, - SplitRenderElements, - }, + backend::render::element::{AsGlowRenderer, FromGlesError}, state::State, utils::{iced::IcedElementInternal, prelude::*}, }; @@ -657,13 +654,42 @@ impl CosmicMapped { } } - pub fn split_render_elements( + pub fn popup_render_elements( &self, renderer: &mut R, location: smithay::utils::Point, scale: smithay::utils::Scale, alpha: f32, - ) -> SplitRenderElements + ) -> Vec + where + R: Renderer + ImportAll + ImportMem + AsGlowRenderer, + ::TextureId: Send + Clone + 'static, + CosmicMappedRenderElement: RenderElement, + C: From>, + { + match &self.element { + CosmicMappedInternal::Stack(s) => s + .popup_render_elements::>( + renderer, location, scale, alpha, + ), + CosmicMappedInternal::Window(w) => w + .popup_render_elements::>( + renderer, location, scale, alpha, + ), + _ => unreachable!(), + } + .into_iter() + .map(C::from) + .collect() + } + + pub fn render_elements( + &self, + renderer: &mut R, + location: smithay::utils::Point, + scale: smithay::utils::Scale, + alpha: f32, + ) -> Vec where R: Renderer + ImportAll + ImportMem + AsGlowRenderer, ::TextureId: Send + Clone + 'static, @@ -671,7 +697,7 @@ impl CosmicMapped { C: From>, { #[cfg(feature = "debug")] - let debug_elements = if let Some(debug) = self.debug.lock().unwrap().as_mut() { + let mut elements = if let Some(debug) = self.debug.lock().unwrap().as_mut() { let window = self.active_window(); let window_geo = window.geometry(); let (min_size, max_size, size) = @@ -840,30 +866,21 @@ impl CosmicMapped { Vec::new() }; #[cfg(not(feature = "debug"))] - let debug_elements = Vec::new(); - - let mut elements = SplitRenderElements { - w_elements: debug_elements, - p_elements: Vec::new(), - }; + let mut elements = Vec::new(); #[cfg_attr(not(feature = "debug"), allow(unused_mut))] - elements.extend_map( - match &self.element { - CosmicMappedInternal::Stack(s) => s - .split_render_elements::>( - renderer, location, scale, alpha, - ), - CosmicMappedInternal::Window(w) => w - .split_render_elements::>( - renderer, location, scale, alpha, - ), - _ => unreachable!(), - }, - C::from, - ); + elements.extend(match &self.element { + CosmicMappedInternal::Stack(s) => s.render_elements::>( + renderer, location, scale, alpha, + ), + CosmicMappedInternal::Window(w) => w + .render_elements::>( + renderer, location, scale, alpha, + ), + _ => unreachable!(), + }); - elements + elements.into_iter().map(C::from).collect() } pub(crate) fn update_theme(&self, theme: cosmic::Theme) { diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index 2a9167a4..388eb813 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -1,6 +1,6 @@ use super::{surface::RESIZE_BORDER, window::Focus, CosmicSurface}; use crate::{ - backend::render::{cursor::CursorState, SplitRenderElements}, + backend::render::cursor::CursorState, shell::{ focus::target::PointerFocusTarget, grabs::{ReleaseMode, ResizeEdge}, @@ -541,13 +541,40 @@ impl CosmicStack { self.0.loop_handle() } - pub fn split_render_elements( + pub fn popup_render_elements( &self, renderer: &mut R, location: Point, scale: Scale, alpha: f32, - ) -> SplitRenderElements + ) -> Vec + where + R: Renderer + ImportAll + ImportMem, + ::TextureId: Send + Clone + 'static, + C: From>, + { + let window_loc = location + Point::from((0, (TAB_HEIGHT as f64 * scale.y) as i32)); + self.0.with_program(|p| { + let windows = p.windows.lock().unwrap(); + let active = p.active.load(Ordering::SeqCst); + + windows[active] + .popup_render_elements::>( + renderer, window_loc, scale, alpha, + ) + .into_iter() + .map(C::from) + .collect() + }) + } + + pub fn render_elements( + &self, + renderer: &mut R, + location: Point, + scale: Scale, + alpha: f32, + ) -> Vec where R: Renderer + ImportAll + ImportMem, ::TextureId: Send + Clone + 'static, @@ -564,28 +591,20 @@ impl CosmicStack { let stack_loc = location + offset; let window_loc = location + Point::from((0, (TAB_HEIGHT as f64 * scale.y) as i32)); - let w_elements = AsRenderElements::::render_elements::>( + let mut elements = AsRenderElements::::render_elements::>( &self.0, renderer, stack_loc, scale, alpha, ); - let mut elements = SplitRenderElements { - w_elements: w_elements.into_iter().map(C::from).collect(), - p_elements: Vec::new(), - }; - - elements.extend_map( - self.0.with_program(|p| { - let windows = p.windows.lock().unwrap(); - let active = p.active.load(Ordering::SeqCst); + elements.extend(self.0.with_program(|p| { + let windows = p.windows.lock().unwrap(); + let active = p.active.load(Ordering::SeqCst); - windows[active].split_render_elements::>( - renderer, window_loc, scale, alpha, - ) - }), - C::from, - ); + windows[active].render_elements::>( + renderer, window_loc, scale, alpha, + ) + })); - elements + elements.into_iter().map(C::from).collect() } pub(crate) fn set_theme(&self, theme: cosmic::Theme) { diff --git a/src/shell/element/surface.rs b/src/shell/element/surface.rs index d13272e6..512f7e84 100644 --- a/src/shell/element/surface.rs +++ b/src/shell/element/surface.rs @@ -35,7 +35,9 @@ use smithay::{ }, wayland_server::protocol::wl_surface::WlSurface, }, - utils::{user_data::UserDataMap, IsAlive, Logical, Rectangle, Serial, Size}, + utils::{ + user_data::UserDataMap, IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size, + }, wayland::{ compositor::{with_states, SurfaceData}, seat::WaylandFocus, @@ -45,7 +47,6 @@ use smithay::{ }; use crate::{ - backend::render::SplitRenderElements, state::{State, SurfaceDmabufFeedback}, utils::prelude::*, wayland::handlers::decoration::PreferredDecorationMode, @@ -590,13 +591,13 @@ impl CosmicSurface { self.0.user_data() } - pub fn split_render_elements( + pub fn popup_render_elements( &self, renderer: &mut R, - location: smithay::utils::Point, - scale: smithay::utils::Scale, + location: Point, + scale: Scale, alpha: f32, - ) -> SplitRenderElements + ) -> Vec where R: Renderer + ImportAll, ::TextureId: Clone + 'static, @@ -605,9 +606,8 @@ impl CosmicSurface { match self.0.underlying_surface() { WindowSurface::Wayland(toplevel) => { let surface = toplevel.wl_surface(); - - let p_elements = PopupManager::popups_for_surface(surface) - .flat_map(|(popup, popup_offset)| { + PopupManager::popups_for_surface(surface) + .flat_map(move |(popup, popup_offset)| { let offset = (self.0.geometry().loc + popup_offset - popup.geometry().loc) .to_physical_precise_round(scale); @@ -620,26 +620,40 @@ impl CosmicSurface { element::Kind::Unspecified, ) }) - .collect(); + .collect() + } + WindowSurface::X11(_) => Vec::new(), + } + } + + pub fn render_elements( + &self, + renderer: &mut R, + location: Point, + scale: Scale, + alpha: f32, + ) -> Vec + where + R: Renderer + ImportAll, + ::TextureId: Clone + 'static, + C: From>, + { + match self.0.underlying_surface() { + WindowSurface::Wayland(toplevel) => { + let surface = toplevel.wl_surface(); - let w_elements = render_elements_from_surface_tree( + render_elements_from_surface_tree( renderer, surface, location, scale, alpha, element::Kind::Unspecified, - ); - - SplitRenderElements { - w_elements, - p_elements, - } + ) + } + WindowSurface::X11(surface) => { + surface.render_elements(renderer, location, scale, alpha) } - WindowSurface::X11(surface) => SplitRenderElements { - w_elements: surface.render_elements(renderer, location, scale, alpha), - p_elements: Vec::new(), - }, } } @@ -663,10 +677,7 @@ impl SpaceElement for CosmicSurface { SpaceElement::bbox(&self.0) } - fn is_in_input_region( - &self, - point: &smithay::utils::Point, - ) -> bool { + fn is_in_input_region(&self, point: &Point) -> bool { SpaceElement::is_in_input_region(&self.0, point) } @@ -784,8 +795,8 @@ where fn render_elements>( &self, renderer: &mut R, - location: smithay::utils::Point, - scale: smithay::utils::Scale, + location: Point, + scale: Scale, alpha: f32, ) -> Vec { self.0.render_elements(renderer, location, scale, alpha) diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index 887ce664..9070994c 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -1,5 +1,5 @@ use crate::{ - backend::render::{cursor::CursorState, SplitRenderElements}, + backend::render::cursor::CursorState, shell::{ focus::target::PointerFocusTarget, grabs::{ReleaseMode, ResizeEdge}, @@ -41,7 +41,7 @@ use smithay::{ output::Output, reexports::wayland_server::protocol::wl_surface::WlSurface, render_elements, - utils::{IsAlive, Logical, Point, Rectangle, Serial, Size}, + utils::{IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size}, wayland::seat::WaylandFocus, }; use std::{ @@ -308,13 +308,13 @@ impl CosmicWindow { self.0.loop_handle() } - pub fn split_render_elements( + pub fn popup_render_elements( &self, renderer: &mut R, - location: smithay::utils::Point, - scale: smithay::utils::Scale, + location: Point, + scale: Scale, alpha: f32, - ) -> SplitRenderElements + ) -> Vec where R: Renderer + ImportAll + ImportMem, ::TextureId: Send + Clone + 'static, @@ -328,17 +328,44 @@ impl CosmicWindow { location }; - let mut elements = SplitRenderElements::default(); + self.0.with_program(|p| { + p.window + .popup_render_elements::>( + renderer, window_loc, scale, alpha, + ) + .into_iter() + .map(C::from) + .collect() + }) + } - elements.extend_map( - self.0.with_program(|p| { - p.window - .split_render_elements::>( - renderer, window_loc, scale, alpha, - ) - }), - C::from, - ); + pub fn render_elements( + &self, + renderer: &mut R, + location: Point, + scale: Scale, + alpha: f32, + ) -> Vec + where + R: Renderer + ImportAll + ImportMem, + ::TextureId: Send + Clone + 'static, + C: From>, + { + let has_ssd = self.0.with_program(|p| p.has_ssd(false)); + + let window_loc = if has_ssd { + location + Point::from((0, (SSD_HEIGHT as f64 * scale.y) as i32)) + } else { + location + }; + + let mut elements = Vec::new(); + + elements.extend(self.0.with_program(|p| { + p.window.render_elements::>( + renderer, window_loc, scale, alpha, + ) + })); if has_ssd { let ssd_loc = location @@ -346,16 +373,12 @@ impl CosmicWindow { .0 .with_program(|p| p.window.geometry().loc) .to_physical_precise_round(scale); - elements.w_elements.extend( - AsRenderElements::::render_elements::>( - &self.0, renderer, ssd_loc, scale, alpha, - ) - .into_iter() - .map(C::from), - ) + elements.extend(AsRenderElements::::render_elements::< + CosmicWindowRenderElement, + >(&self.0, renderer, ssd_loc, scale, alpha)) } - elements + elements.into_iter().map(C::from).collect() } pub(crate) fn set_theme(&self, theme: cosmic::Theme) { diff --git a/src/shell/grabs/moving.rs b/src/shell/grabs/moving.rs index 9a41702e..25106fa6 100644 --- a/src/shell/grabs/moving.rs +++ b/src/shell/grabs/moving.rs @@ -2,8 +2,7 @@ use crate::{ backend::render::{ - cursor::CursorState, element::AsGlowRenderer, BackdropShader, IndicatorShader, Key, - SplitRenderElements, Usage, + cursor::CursorState, element::AsGlowRenderer, BackdropShader, IndicatorShader, Key, Usage, }, shell::{ element::{ @@ -181,12 +180,18 @@ impl MoveGrabState { _ => vec![], }; - let SplitRenderElements { - w_elements, - p_elements, - } = self + let w_elements = self .window - .split_render_elements::>( + .render_elements::>( + renderer, + (render_location - self.window.geometry().loc) + .to_physical_precise_round(output_scale), + output_scale, + alpha, + ); + let p_elements = self + .window + .popup_render_elements::>( renderer, (render_location - self.window.geometry().loc) .to_physical_precise_round(output_scale), diff --git a/src/shell/layout/floating/mod.rs b/src/shell/layout/floating/mod.rs index 140fb5d5..5c80fe62 100644 --- a/src/shell/layout/floating/mod.rs +++ b/src/shell/layout/floating/mod.rs @@ -24,7 +24,7 @@ use smithay::{ }; use crate::{ - backend::render::{element::AsGlowRenderer, IndicatorShader, Key, SplitRenderElements, Usage}, + backend::render::{element::AsGlowRenderer, IndicatorShader, Key, Usage}, shell::{ element::{ resize_indicator::ResizeIndicator, @@ -1260,6 +1260,52 @@ impl FloatingLayout { } self.refresh(); //fixup any out of bounds elements } + #[profiling::function] + pub fn render_popups( + &self, + renderer: &mut R, + alpha: f32, + ) -> Vec> + where + R: Renderer + ImportAll + ImportMem + AsGlowRenderer, + ::TextureId: Send + Clone + 'static, + CosmicMappedRenderElement: RenderElement, + CosmicWindowRenderElement: RenderElement, + CosmicStackRenderElement: RenderElement, + { + let output = self.space.outputs().next().unwrap(); + let output_scale = output.current_scale().fractional_scale(); + + let mut elements = Vec::default(); + + for elem in self + .animations + .iter() + .filter(|(_, anim)| matches!(anim, Animation::Minimize { .. })) + .map(|(elem, _)| elem) + .chain(self.space.elements().rev()) + { + let (geometry, alpha) = self + .animations + .get(elem) + .map(|anim| (*anim.previous_geometry(), alpha * anim.alpha())) + .unwrap_or_else(|| (self.space.element_geometry(elem).unwrap().as_local(), alpha)); + + let render_location = geometry.loc - elem.geometry().loc.as_local(); + elements.extend( + elem.popup_render_elements( + renderer, + render_location + .as_logical() + .to_physical_precise_round(output_scale), + output_scale.into(), + alpha, + ), + ); + } + + elements + } #[profiling::function] pub fn render( @@ -1270,7 +1316,7 @@ impl FloatingLayout { indicator_thickness: u8, alpha: f32, theme: &cosmic::theme::CosmicTheme, - ) -> SplitRenderElements> + ) -> Vec> where R: Renderer + ImportAll + ImportMem + AsGlowRenderer, ::TextureId: Send + Clone + 'static, @@ -1285,7 +1331,7 @@ impl FloatingLayout { }; let output_scale = output.current_scale().fractional_scale(); - let mut elements = SplitRenderElements::default(); + let mut elements = Vec::default(); for elem in self .animations @@ -1301,10 +1347,7 @@ impl FloatingLayout { .unwrap_or_else(|| (self.space.element_geometry(elem).unwrap().as_local(), alpha)); let render_location = geometry.loc - elem.geometry().loc.as_local(); - let SplitRenderElements { - mut w_elements, - p_elements, - } = elem.split_render_elements( + let mut window_elements = elem.render_elements( renderer, render_location .as_logical() @@ -1331,7 +1374,7 @@ impl FloatingLayout { y: geometry.size.h as f64 / buffer_size.h as f64, }; - w_elements = w_elements + window_elements = window_elements .into_iter() .map(|element| match element { CosmicMappedRenderElement::Stack(elem) => { @@ -1387,7 +1430,7 @@ impl FloatingLayout { resize.resize(resize_geometry.size.as_logical()); resize.output_enter(output, Rectangle::default() /* unused */); - elements.w_elements.extend( + window_elements.extend( resize .render_elements::>( renderer, @@ -1419,12 +1462,11 @@ impl FloatingLayout { active_window_hint.blue, ], ); - elements.w_elements.push(element.into()); + window_elements.push(element.into()); } } - elements.w_elements.extend(w_elements); - elements.p_elements.extend(p_elements); + elements.extend(window_elements); } elements diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index feeebcd8..308f8f8e 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -2,8 +2,8 @@ use crate::{ backend::render::{ - element::AsGlowRenderer, BackdropShader, IndicatorShader, Key, SplitRenderElements, Usage, - ACTIVE_GROUP_COLOR, GROUP_COLOR, + element::AsGlowRenderer, BackdropShader, IndicatorShader, Key, Usage, ACTIVE_GROUP_COLOR, + GROUP_COLOR, }, shell::{ element::{ @@ -60,7 +60,7 @@ use smithay::{ input::Seat, output::Output, reexports::wayland_server::Client, - utils::{IsAlive, Logical, Point, Rectangle, Scale, Size}, + utils::{IsAlive, Logical, Physical, Point, Rectangle, Scale, Size}, wayland::{compositor::add_blocker, seat::WaylandFocus}, }; use std::{ @@ -3863,7 +3863,7 @@ impl TilingLayout { resize_indicator: Option<(ResizeMode, ResizeIndicator)>, indicator_thickness: u8, theme: &cosmic::theme::CosmicTheme, - ) -> Result>, OutputNotMapped> + ) -> Result>, OutputNotMapped> where R: Renderer + ImportAll + ImportMem + AsGlowRenderer, ::TextureId: Send + Clone + 'static, @@ -3896,7 +3896,7 @@ impl TilingLayout { }; let draw_groups = overview.0.alpha(); - let mut elements = SplitRenderElements::default(); + let mut elements = Vec::default(); let is_overview = !matches!(overview.0, OverviewMode::None); let is_mouse_tiling = (matches!(overview.0.trigger(), Some(Trigger::Pointer(_)))) @@ -3931,7 +3931,7 @@ impl TilingLayout { .unzip(); // all old windows we want to fade out - elements.extend(render_old_tree( + elements.extend(render_old_tree_windows( reference_tree, target_tree, renderer, @@ -3969,7 +3969,7 @@ impl TilingLayout { .unzip(); // all alive windows - elements.extend(render_new_tree( + elements.extend(render_new_tree_windows( target_tree, reference_tree, renderer, @@ -4001,12 +4001,139 @@ impl TilingLayout { // tiling hints if let Some(group_elements) = group_elements { - elements.w_elements.extend(group_elements); + elements.extend(group_elements); } Ok(elements) } + #[profiling::function] + pub fn render_popups( + &self, + renderer: &mut R, + seat: Option<&Seat>, + non_exclusive_zone: Rectangle, + overview: (OverviewMode, Option<(SwapIndicator, Option<&Tree>)>), + theme: &cosmic::theme::CosmicTheme, + ) -> Result>, OutputNotMapped> + where + R: Renderer + ImportAll + ImportMem + AsGlowRenderer, + ::TextureId: Send + Clone + 'static, + CosmicMappedRenderElement: RenderElement, + CosmicWindowRenderElement: RenderElement, + CosmicStackRenderElement: RenderElement, + { + let output_scale = self.output.current_scale().fractional_scale(); + + let (target_tree, duration, _) = if self.queue.animation_start.is_some() { + self.queue + .trees + .get(1) + .expect("Animation ongoing, should have two trees") + } else { + self.queue.trees.front().unwrap() + }; + let reference_tree = self + .queue + .animation_start + .is_some() + .then(|| &self.queue.trees.front().unwrap().0); + + 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) + } else { + 1.0 + }; + let draw_groups = overview.0.alpha(); + + let mut elements = Vec::default(); + + let is_mouse_tiling = (matches!(overview.0.trigger(), Some(Trigger::Pointer(_)))) + .then(|| self.last_overview_hover.as_ref().map(|x| &x.1)); + let swap_desc = if let Some(Trigger::KeyboardSwap(_, desc)) = overview.0.trigger() { + Some(desc.clone()) + } else { + None + }; + + // all gone windows and fade them out + let old_geometries = if let Some(reference_tree) = reference_tree.as_ref() { + let (geometries, _) = if let Some(transition) = draw_groups { + Some(geometries_for_groupview( + reference_tree, + &mut *renderer, + non_exclusive_zone, + seat, // TODO: Would be better to be an old focus, + // but for that we have to associate focus with a tree (and animate focus changes properly) + 1.0 - transition, + transition, + output_scale, + &self.placeholder_id, + is_mouse_tiling, + swap_desc.clone(), + overview.1.as_ref().and_then(|(_, tree)| tree.clone()), + theme, + )) + } else { + None + } + .unzip(); + + // all old windows we want to fade out + elements.extend(render_old_tree_popups( + reference_tree, + target_tree, + renderer, + geometries.clone(), + output_scale, + percentage, + swap_desc.is_some(), + )); + + geometries + } else { + None + }; + + let (geometries, _) = if let Some(transition) = draw_groups { + Some(geometries_for_groupview( + target_tree, + &mut *renderer, + non_exclusive_zone, + seat, + transition, + transition, + output_scale, + &self.placeholder_id, + is_mouse_tiling, + swap_desc.clone(), + overview.1.as_ref().and_then(|(_, tree)| tree.clone()), + theme, + )) + } else { + None + } + .unzip(); + + // all alive windows + elements.extend(render_new_tree_popups( + target_tree, + reference_tree, + renderer, + geometries, + old_geometries, + seat, + &self.output, + percentage, + overview, + swap_desc.clone(), + )); + + Ok(elements) + } + fn gaps(&self) -> (i32, i32) { let g = self.theme.cosmic().gaps; (g.0 as i32, g.1 as i32) @@ -4690,7 +4817,48 @@ where (geometries, elements) } -fn render_old_tree( +fn render_old_tree_popups( + reference_tree: &Tree, + target_tree: &Tree, + renderer: &mut R, + geometries: Option>>, + output_scale: f64, + percentage: f32, + is_swap_mode: bool, +) -> Vec> +where + R: Renderer + ImportAll + ImportMem + AsGlowRenderer, + ::TextureId: Send + Clone + 'static, + CosmicMappedRenderElement: RenderElement, + CosmicWindowRenderElement: RenderElement, + CosmicStackRenderElement: RenderElement, +{ + let mut elements = Vec::default(); + + render_old_tree( + reference_tree, + target_tree, + geometries, + output_scale, + percentage, + is_swap_mode, + |mapped, elem_geometry, geo, alpha, _| { + elements.extend( + mapped.popup_render_elements::>( + renderer, + geo.loc.as_logical().to_physical_precise_round(output_scale) + - elem_geometry.loc, + Scale::from(output_scale), + alpha, + ), + ); + }, + ); + + elements +} + +fn render_old_tree_windows( reference_tree: &Tree, target_tree: &Tree, renderer: &mut R, @@ -4700,7 +4868,7 @@ fn render_old_tree( indicator_thickness: u8, is_swap_mode: bool, theme: &cosmic::theme::CosmicTheme, -) -> SplitRenderElements> +) -> Vec> where R: Renderer + ImportAll + ImportMem + AsGlowRenderer, ::TextureId: Send + Clone + 'static, @@ -4709,8 +4877,80 @@ where CosmicStackRenderElement: RenderElement, { let window_hint = crate::theme::active_window_hint(theme); - let mut elements = SplitRenderElements::default(); + let mut elements = Vec::default(); + + render_old_tree( + reference_tree, + target_tree, + geometries, + output_scale, + percentage, + is_swap_mode, + |mapped, elem_geometry, geo, alpha, is_minimizing| { + let window_elements = mapped.render_elements::>( + renderer, + geo.loc.as_logical().to_physical_precise_round(output_scale) - elem_geometry.loc, + Scale::from(output_scale), + alpha, + ); + + elements.extend(window_elements.into_iter().flat_map(|element| { + match element { + CosmicMappedRenderElement::Stack(elem) => constrain_render_elements( + std::iter::once(elem), + geo.loc.as_logical().to_physical_precise_round(output_scale) + - elem_geometry.loc, + geo.as_logical().to_physical_precise_round(output_scale), + elem_geometry, + ConstrainScaleBehavior::Stretch, + ConstrainAlign::CENTER, + output_scale, + ) + .next() + .map(CosmicMappedRenderElement::TiledStack), + CosmicMappedRenderElement::Window(elem) => constrain_render_elements( + std::iter::once(elem), + geo.loc.as_logical().to_physical_precise_round(output_scale) + - elem_geometry.loc, + geo.as_logical().to_physical_precise_round(output_scale), + elem_geometry, + ConstrainScaleBehavior::Stretch, + ConstrainAlign::CENTER, + output_scale, + ) + .next() + .map(CosmicMappedRenderElement::TiledWindow), + x => Some(x), + } + })); + if is_minimizing && indicator_thickness > 0 { + elements.push(CosmicMappedRenderElement::FocusIndicator( + IndicatorShader::focus_element( + renderer, + Key::Window(Usage::FocusIndicator, mapped.clone().key()), + geo, + indicator_thickness, + output_scale, + alpha, + [window_hint.red, window_hint.green, window_hint.blue], + ), + )); + } + }, + ); + + elements +} +fn render_old_tree( + reference_tree: &Tree, + target_tree: &Tree, + geometries: Option>>, + output_scale: f64, + percentage: f32, + is_swap_mode: bool, + mut processor: impl FnMut(&CosmicMapped, Rectangle, Rectangle, f32, bool), +) { if let Some(root) = reference_tree.root_node_id() { let geometries = geometries.unwrap_or_default(); reference_tree @@ -4779,71 +5019,71 @@ where }; let elem_geometry = mapped.geometry().to_physical_precise_round(output_scale); - let SplitRenderElements { - w_elements, - p_elements, - } = mapped.split_render_elements::>( - renderer, - geo.loc.as_logical().to_physical_precise_round(output_scale) - - elem_geometry.loc, - Scale::from(output_scale), - alpha, - ); - elements - .w_elements - .extend(w_elements.into_iter().flat_map(|element| { - match element { - CosmicMappedRenderElement::Stack(elem) => constrain_render_elements( - std::iter::once(elem), - geo.loc.as_logical().to_physical_precise_round(output_scale) - - elem_geometry.loc, - geo.as_logical().to_physical_precise_round(output_scale), - elem_geometry, - ConstrainScaleBehavior::Stretch, - ConstrainAlign::CENTER, - output_scale, - ) - .next() - .map(CosmicMappedRenderElement::TiledStack), - CosmicMappedRenderElement::Window(elem) => constrain_render_elements( - std::iter::once(elem), - geo.loc.as_logical().to_physical_precise_round(output_scale) - - elem_geometry.loc, - geo.as_logical().to_physical_precise_round(output_scale), - elem_geometry, - ConstrainScaleBehavior::Stretch, - ConstrainAlign::CENTER, - output_scale, - ) - .next() - .map(CosmicMappedRenderElement::TiledWindow), - x => Some(x), - } - })); - if minimize_geo.is_some() && indicator_thickness > 0 { - elements - .w_elements - .push(CosmicMappedRenderElement::FocusIndicator( - IndicatorShader::focus_element( - renderer, - Key::Window(Usage::FocusIndicator, mapped.clone().key()), - geo, - indicator_thickness, - output_scale, - alpha, - [window_hint.red, window_hint.green, window_hint.blue], - ), - )); - } - elements.p_elements.extend(p_elements); + processor(mapped, elem_geometry, geo, alpha, minimize_geo.is_some()) }); } +} - elements +fn render_new_tree_popups( + target_tree: &Tree, + reference_tree: Option<&Tree>, + renderer: &mut R, + geometries: Option>>, + old_geometries: Option>>, + seat: Option<&Seat>, + output: &Output, + percentage: f32, + overview: (OverviewMode, Option<(SwapIndicator, Option<&Tree>)>), + swap_desc: Option, +) -> Vec> +where + R: Renderer + ImportAll + ImportMem + AsGlowRenderer, + ::TextureId: Send + Clone + 'static, + CosmicMappedRenderElement: RenderElement, + CosmicWindowRenderElement: RenderElement, + CosmicStackRenderElement: RenderElement, +{ + let mut popup_elements = Vec::new(); + let output_scale = output.current_scale().fractional_scale(); + + let is_active_output = seat + .map(|seat| &seat.active_output() == output) + .unwrap_or(false); + + let (_, swap_tree) = overview.1.unzip(); + let swap_desc = swap_desc.filter(|_| is_active_output); + let swap_tree = swap_tree.flatten().filter(|_| is_active_output); + + render_new_tree( + target_tree, + reference_tree, + geometries, + old_geometries, + percentage, + swap_tree, + swap_desc.as_ref(), + |_node_id, data, geo, _original_geo, alpha, _| { + if let Data::Mapped { mapped, .. } = data { + let elem_geometry = mapped.geometry().to_physical_precise_round(output_scale); + + popup_elements.extend( + mapped.popup_render_elements::>( + renderer, + geo.loc.as_logical().to_physical_precise_round(output_scale) + - elem_geometry.loc, + Scale::from(output_scale), + alpha, + ), + ); + } + }, + ); + + popup_elements } -fn render_new_tree( +fn render_new_tree_windows( target_tree: &Tree, reference_tree: Option<&Tree>, renderer: &mut R, @@ -4862,7 +5102,7 @@ fn render_new_tree( swapping_stack_surface_id: &Id, placeholder_id: &Id, theme: &cosmic::theme::CosmicTheme, -) -> SplitRenderElements> +) -> Vec> where R: Renderer + ImportAll + ImportMem + AsGlowRenderer, ::TextureId: Send + Clone + 'static, @@ -4905,7 +5145,6 @@ where let mut animating_window_elements = Vec::new(); let mut window_elements = Vec::new(); - let mut popup_elements = Vec::new(); let mut group_backdrop = None; let mut indicators = Vec::new(); @@ -4916,10 +5155,11 @@ where let output_scale = output.current_scale().fractional_scale(); let (swap_indicator, swap_tree) = overview.1.unzip(); - let swap_tree = swap_tree.flatten().filter(|_| is_active_output); let swap_desc = swap_desc.filter(|_| is_active_output); + let swap_tree = swap_tree.flatten().filter(|_| is_active_output); let window_hint = crate::theme::active_window_hint(theme); let group_color = GROUP_COLOR; + // render placeholder, if we are swapping to an empty workspace if target_tree.root_node_id().is_none() && swap_desc.is_some() { window_elements.push( @@ -4968,165 +5208,48 @@ where (swap_geo.loc.as_logical() - window_geo.loc).to_physical_precise_round(output_scale); swap_elements.extend( - window - .render_elements::>( - renderer, - render_loc, - output_scale.into(), - 1.0, - ) - .into_iter() - .map(|window| { - CosmicMappedRenderElement::GrabbedWindow(RescaleRenderElement::from_element( - window, - swap_geo - .loc - .as_logical() - .to_physical_precise_round(output_scale), - ease( - Linear, - 1.0, - swap_factor(window_geo.size), - transition.unwrap_or(1.0), - ), - )) - }), + AsRenderElements::render_elements::>( + &window, + renderer, + render_loc, + output_scale.into(), + 1.0, + ) + .into_iter() + .map(|window| { + CosmicMappedRenderElement::GrabbedWindow(RescaleRenderElement::from_element( + window, + swap_geo + .loc + .as_logical() + .to_physical_precise_round(output_scale), + ease( + Linear, + 1.0, + swap_factor(window_geo.size), + transition.unwrap_or(1.0), + ), + )) + }), ) } // render actual tree nodes - let old_geometries = old_geometries.unwrap_or_default(); - let geometries = geometries.unwrap_or_default(); - target_tree - .root_node_id() - .into_iter() - .flat_map(|root| target_tree.traverse_pre_order_ids(root).unwrap()) - .map(|id| (target_tree, id)) - .chain( - swap_tree - .into_iter() - .flat_map(|tree| { - let sub_root = &swap_desc.as_ref().unwrap().node; - if swap_desc.as_ref().unwrap().stack_window.is_none() { - Some( - tree.traverse_pre_order_ids(sub_root) - .unwrap() - .map(move |id| (tree, id)), - ) - } else { - None - } - }) - .flatten(), - ) - .for_each(|(target_tree, node_id)| { - let data = target_tree.get(&node_id).unwrap().data(); - let (original_geo, scaled_geo) = (data.geometry(), geometries.get(&node_id)); - - let (old_original_geo, old_scaled_geo) = - if let Some(reference_tree) = reference_tree.as_ref() { - if let Some(root) = reference_tree.root_node_id() { - reference_tree - .traverse_pre_order_ids(root) - .unwrap() - .find(|id| &node_id == id) - .map(|node_id| { - ( - reference_tree.get(&node_id).unwrap().data().geometry(), - old_geometries.get(&node_id), - ) - }) - } else { - None - } - } else { - None - } - .unzip(); - let mut old_geo = old_original_geo.map(|original_geo| { - let (scale, offset) = old_scaled_geo - .unwrap() - .map(|adapted_geo| scale_to_center(original_geo, adapted_geo)) - .unwrap_or_else(|| (1.0.into(), (0, 0).into())); - ( - old_scaled_geo - .unwrap() - .map(|adapted_geo| { - Rectangle::from_loc_and_size( - adapted_geo.loc + offset, - ( - (original_geo.size.w as f64 * scale).round() as i32, - (original_geo.size.h as f64 * scale).round() as i32, - ), - ) - }) - .unwrap_or(*original_geo), - 1.0, - ) - }); - - let was_minimized = if let Data::Mapped { - minimize_rect: Some(minimize_rect), - .. - } = &data - { - old_geo = Some((*minimize_rect, (percentage * 2.0).min(1.0))); - true - } else { - false - }; - - let (scale, offset) = scaled_geo - .map(|adapted_geo| scale_to_center(original_geo, adapted_geo)) - .unwrap_or_else(|| (1.0.into(), (0, 0).into())); - let new_geo = scaled_geo - .map(|adapted_geo| { - Rectangle::from_loc_and_size( - adapted_geo.loc + offset, - ( - (original_geo.size.w as f64 * scale).round() as i32, - (original_geo.size.h as f64 * scale).round() as i32, - ), - ) - }) - .unwrap_or(*original_geo); - - let (geo, alpha, animating) = if let Some((old_geo, alpha)) = old_geo.filter(|_| { - swap_desc - .as_ref() - .map(|desc| desc.node != node_id && desc.stack_window.is_none()) - .unwrap_or(true) - }) { - ( - if was_minimized { - ease( - EaseInOutCubic, - EaseRectangle(old_geo), - EaseRectangle(new_geo), - percentage, - ) - .unwrap() - } else { - ease( - Linear, - EaseRectangle(old_geo), - EaseRectangle(new_geo), - percentage, - ) - .unwrap() - }, - alpha, - old_geo != new_geo, - ) - } else { - (new_geo, percentage, false) - }; - + render_new_tree( + target_tree, + reference_tree, + geometries, + old_geometries, + percentage, + swap_tree, + swap_desc.as_ref(), + |node_id, data, geo, original_geo, alpha, animating| { if swap_desc.as_ref().map(|desc| &desc.node) == Some(&node_id) || focused.as_ref() == Some(&node_id) { if indicator_thickness > 0 || data.is_group() { let mut geo = geo.clone(); + if data.is_group() { let outer_gap: i32 = (if is_overview { GAP_KEYBOARD } else { 4 } as f32 * percentage) @@ -5251,10 +5374,8 @@ where if let Data::Mapped { mapped, .. } = data { let elem_geometry = mapped.geometry().to_physical_precise_round(output_scale); - let SplitRenderElements { - mut w_elements, - p_elements, - } = mapped.split_render_elements::>( + + let mut elements = mapped.render_elements::>( renderer, //original_location, geo.loc.as_logical().to_physical_precise_round(output_scale) @@ -5262,6 +5383,7 @@ where Scale::from(output_scale), alpha, ); + if swap_desc .as_ref() .filter(|swap_desc| swap_desc.node == node_id) @@ -5284,7 +5406,7 @@ where { let mut geo = mapped.active_window_geometry().as_local(); geo.loc += original_geo.loc; - w_elements.insert( + elements.insert( 0, CosmicMappedRenderElement::Overlay(BackdropShader::element( renderer, @@ -5305,7 +5427,7 @@ where (ConstrainScaleBehavior::CutOff, ConstrainAlign::TOP_LEFT) }; - let w_elements = w_elements.into_iter().flat_map(|element| match element { + let elements = elements.into_iter().flat_map(|element| match element { CosmicMappedRenderElement::Stack(elem) => constrain_render_elements( std::iter::once(elem), geo.loc.as_logical().to_physical_precise_round(output_scale) @@ -5344,6 +5466,7 @@ where .map(CosmicMappedRenderElement::TiledOverlay), x => Some(x), }); + if swap_desc .as_ref() .map(|swap_desc| { @@ -5356,21 +5479,19 @@ where }) .unwrap_or(false) { - swap_elements.extend(w_elements); + swap_elements.extend(elements); } else { if animating { - animating_window_elements.extend(w_elements); + animating_window_elements.extend(elements); } else { - window_elements.extend(w_elements); - } - if !mapped.is_maximized(false) { - popup_elements.extend(p_elements); + window_elements.extend(elements); } } } - }); + }, + ); - window_elements = resize_elements + resize_elements .into_iter() .flatten() .chain(swap_elements) @@ -5378,12 +5499,147 @@ where .chain(window_elements) .chain(animating_window_elements) .chain(group_backdrop.into_iter().map(Into::into)) - .collect(); + .collect() +} - SplitRenderElements { - w_elements: window_elements, - p_elements: popup_elements, - } +fn render_new_tree( + target_tree: &Tree, + reference_tree: Option<&Tree>, + geometries: Option>>, + old_geometries: Option>>, + percentage: f32, + swap_tree: Option<&Tree>, + swap_desc: Option<&NodeDesc>, + mut processor: impl FnMut(NodeId, &Data, Rectangle, &Rectangle, f32, bool), +) { + let old_geometries = old_geometries.unwrap_or_default(); + let geometries = geometries.unwrap_or_default(); + target_tree + .root_node_id() + .into_iter() + .flat_map(|root| target_tree.traverse_pre_order_ids(root).unwrap()) + .map(|id| (target_tree, id)) + .chain( + swap_tree + .into_iter() + .flat_map(|tree| { + let sub_root = &swap_desc.unwrap().node; + if swap_desc.unwrap().stack_window.is_none() { + Some( + tree.traverse_pre_order_ids(sub_root) + .unwrap() + .map(move |id| (tree, id)), + ) + } else { + None + } + }) + .flatten(), + ) + .for_each(|(target_tree, node_id)| { + let data = target_tree.get(&node_id).unwrap().data(); + let (original_geo, scaled_geo) = (data.geometry(), geometries.get(&node_id)); + + let (old_original_geo, old_scaled_geo) = + if let Some(reference_tree) = reference_tree.as_ref() { + if let Some(root) = reference_tree.root_node_id() { + reference_tree + .traverse_pre_order_ids(root) + .unwrap() + .find(|id| &node_id == id) + .map(|node_id| { + ( + reference_tree.get(&node_id).unwrap().data().geometry(), + old_geometries.get(&node_id), + ) + }) + } else { + None + } + } else { + None + } + .unzip(); + let mut old_geo = old_original_geo.map(|original_geo| { + let (scale, offset) = old_scaled_geo + .unwrap() + .map(|adapted_geo| scale_to_center(original_geo, adapted_geo)) + .unwrap_or_else(|| (1.0.into(), (0, 0).into())); + ( + old_scaled_geo + .unwrap() + .map(|adapted_geo| { + Rectangle::from_loc_and_size( + adapted_geo.loc + offset, + ( + (original_geo.size.w as f64 * scale).round() as i32, + (original_geo.size.h as f64 * scale).round() as i32, + ), + ) + }) + .unwrap_or(*original_geo), + 1.0, + ) + }); + + let was_minimized = if let Data::Mapped { + minimize_rect: Some(minimize_rect), + .. + } = &data + { + old_geo = Some((*minimize_rect, (percentage * 2.0).min(1.0))); + true + } else { + false + }; + + let (scale, offset) = scaled_geo + .map(|adapted_geo| scale_to_center(original_geo, adapted_geo)) + .unwrap_or_else(|| (1.0.into(), (0, 0).into())); + let new_geo = scaled_geo + .map(|adapted_geo| { + Rectangle::from_loc_and_size( + adapted_geo.loc + offset, + ( + (original_geo.size.w as f64 * scale).round() as i32, + (original_geo.size.h as f64 * scale).round() as i32, + ), + ) + }) + .unwrap_or(*original_geo); + + let (geo, alpha, animating) = if let Some((old_geo, alpha)) = old_geo.filter(|_| { + swap_desc + .map(|desc| desc.node != node_id && desc.stack_window.is_none()) + .unwrap_or(true) + }) { + ( + if was_minimized { + ease( + EaseInOutCubic, + EaseRectangle(old_geo), + EaseRectangle(new_geo), + percentage, + ) + .unwrap() + } else { + ease( + Linear, + EaseRectangle(old_geo), + EaseRectangle(new_geo), + percentage, + ) + .unwrap() + }, + alpha, + old_geo != new_geo, + ) + } else { + (new_geo, percentage, false) + }; + + processor(node_id, data, geo, original_geo, alpha, animating) + }); } fn scale_to_center( diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 8da5c2f5..0d0ac5f1 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -1,7 +1,7 @@ use crate::{ backend::render::{ element::{AsGlowRenderer, FromGlesError}, - BackdropShader, SplitRenderElements, + BackdropShader, }, shell::{ layout::{floating::FloatingLayout, tiling::TilingLayout}, @@ -921,10 +921,6 @@ impl Workspace { .chain(self.tiling_layer.mapped().map(|(w, _)| w)) } - pub fn outputs(&self) -> impl Iterator { - self.floating_layer.space.outputs() - } - pub fn is_empty(&self) -> bool { self.floating_layer.mapped().next().is_none() && self.tiling_layer.mapped().next().is_none() @@ -1012,7 +1008,7 @@ impl Workspace { resize_indicator: Option<(ResizeMode, ResizeIndicator)>, indicator_thickness: u8, theme: &CosmicTheme, - ) -> Result>, OutputNotMapped> + ) -> Result>, OutputNotMapped> where R: Renderer + ImportAll + ImportMem + AsGlowRenderer, ::TextureId: Send + Clone + 'static, @@ -1021,7 +1017,7 @@ impl Workspace { CosmicStackRenderElement: RenderElement, WorkspaceRenderElement: RenderElement, { - let mut elements = SplitRenderElements::default(); + let mut elements = Vec::default(); let output_scale = self.output.current_scale().fractional_scale(); let zone = { @@ -1104,26 +1100,19 @@ impl Workspace { y: target_geo.size.h as f64 / bbox.size.h as f64, }; - let SplitRenderElements { - w_elements, - p_elements, - } = fullscreen - .surface - .split_render_elements::>( - renderer, - render_loc, - output_scale.into(), - alpha, - ); - elements.w_elements.extend( - w_elements + elements.extend( + fullscreen + .surface + .render_elements::>( + renderer, + render_loc, + output_scale.into(), + alpha, + ) .into_iter() .map(|elem| RescaleRenderElement::from_element(elem, render_loc, scale)) .map(Into::into), ); - elements - .p_elements - .extend(p_elements.into_iter().map(Into::into)) } if self @@ -1155,16 +1144,18 @@ impl Workspace { OverviewMode::None => 1.0, }; - elements.extend_map( - self.floating_layer.render::( - renderer, - focused.as_ref(), - resize_indicator.clone(), - indicator_thickness, - alpha, - theme, - ), - WorkspaceRenderElement::from, + elements.extend( + self.floating_layer + .render::( + renderer, + focused.as_ref(), + resize_indicator.clone(), + indicator_thickness, + alpha, + theme, + ) + .into_iter() + .map(WorkspaceRenderElement::from), ); let alpha = match &overview.0 { @@ -1181,21 +1172,23 @@ impl Workspace { }; //tiling surfaces - elements.extend_map( - self.tiling_layer.render::( - renderer, - draw_focus_indicator, - zone, - overview, - resize_indicator, - indicator_thickness, - theme, - )?, - WorkspaceRenderElement::from, + elements.extend( + self.tiling_layer + .render::( + renderer, + draw_focus_indicator, + zone, + overview, + resize_indicator, + indicator_thickness, + theme, + )? + .into_iter() + .map(WorkspaceRenderElement::from), ); if let Some(alpha) = alpha { - elements.w_elements.push( + elements.push( Into::>::into(BackdropShader::element( renderer, self.backdrop_id.clone(), @@ -1214,6 +1207,159 @@ impl Workspace { Ok(elements) } + + #[profiling::function] + pub fn render_popups<'a, R>( + &self, + renderer: &mut R, + draw_focus_indicator: Option<&Seat>, + overview: (OverviewMode, Option<(SwapIndicator, Option<&Tree>)>), + theme: &CosmicTheme, + ) -> Result>, OutputNotMapped> + where + R: Renderer + ImportAll + ImportMem + AsGlowRenderer, + ::TextureId: Send + Clone + 'static, + CosmicMappedRenderElement: RenderElement, + CosmicWindowRenderElement: RenderElement, + CosmicStackRenderElement: RenderElement, + WorkspaceRenderElement: RenderElement, + { + let mut elements = Vec::default(); + + 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() + }; + + if let Some(fullscreen) = self.fullscreen.as_ref() { + // fullscreen window + let bbox = fullscreen.surface.bbox().as_local(); + let element_geo = Rectangle::from_loc_and_size( + self.element_for_surface(&fullscreen.surface) + .and_then(|elem| { + self.floating_layer + .element_geometry(elem) + .or_else(|| self.tiling_layer.element_geometry(elem)) + .map(|mut geo| { + geo.loc -= elem.geometry().loc.as_local(); + geo + }) + }) + .unwrap_or(bbox) + .loc, + fullscreen.original_geometry.size.as_local(), + ); + + 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 { + full_geo.loc.x += (full_geo.size.w - bbox.size.w) / 2; + full_geo.size.w = bbox.size.w; + } + if bbox.size.h < full_geo.size.h { + full_geo.loc.y += (full_geo.size.h - bbox.size.h) / 2; + full_geo.size.h = bbox.size.h; + } + } + } + + let (target_geo, alpha) = match (fullscreen.start_at, fullscreen.ended_at) { + (Some(started), _) => { + let duration = Instant::now().duration_since(started).as_secs_f64() + / FULLSCREEN_ANIMATION_DURATION.as_secs_f64(); + ( + ease( + EaseInOutCubic, + EaseRectangle(element_geo), + EaseRectangle(full_geo), + duration, + ) + .0, + ease(EaseInOutCubic, 0.0, 1.0, duration), + ) + } + (_, Some(ended)) => { + let duration = Instant::now().duration_since(ended).as_secs_f64() + / FULLSCREEN_ANIMATION_DURATION.as_secs_f64(); + ( + ease( + EaseInOutCubic, + EaseRectangle(full_geo), + EaseRectangle(element_geo), + duration, + ) + .0, + ease(EaseInOutCubic, 1.0, 0.0, duration), + ) + } + (None, None) => (full_geo, 1.0), + }; + + let render_loc = target_geo + .loc + .as_logical() + .to_physical_precise_round(output_scale); + + elements.extend( + fullscreen + .surface + .popup_render_elements::>( + renderer, + render_loc, + output_scale.into(), + alpha, + ) + .into_iter() + .map(Into::into), + ); + } + + if self + .fullscreen + .as_ref() + .map(|f| f.start_at.is_some() || f.ended_at.is_some()) + .unwrap_or(true) + { + // floating surfaces + let alpha = match &overview.0 { + OverviewMode::Started(_, started) => { + (1.0 - (Instant::now().duration_since(*started).as_millis() + / ANIMATION_DURATION.as_millis()) as f32) + .max(0.0) + * 0.4 + + 0.6 + } + OverviewMode::Ended(_, ended) => { + ((Instant::now().duration_since(*ended).as_millis() + / ANIMATION_DURATION.as_millis()) as f32) + * 0.4 + + 0.6 + } + OverviewMode::Active(_) => 0.6, + OverviewMode::None => 1.0, + }; + + elements.extend( + self.floating_layer + .render_popups::(renderer, alpha) + .into_iter() + .map(WorkspaceRenderElement::from), + ); + + //tiling surfaces + elements.extend( + self.tiling_layer + .render_popups::(renderer, draw_focus_indicator, zone, overview, theme)? + .into_iter() + .map(WorkspaceRenderElement::from), + ); + } + + Ok(elements) + } } impl FocusStacks { From 415dcc46020eb31e22e6864e675ff24f9383e234 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Thu, 10 Oct 2024 23:18:04 +0200 Subject: [PATCH 3/3] input: Use `render_input_order` --- src/input/mod.rs | 423 ++++++++++++++++++++----------- src/shell/element/mod.rs | 5 +- src/shell/element/stack.rs | 68 ++--- src/shell/element/window.rs | 31 +-- src/shell/focus/mod.rs | 9 +- src/shell/grabs/moving.rs | 4 +- src/shell/layout/floating/mod.rs | 118 ++++++++- src/shell/layout/tiling/mod.rs | 124 ++++++--- src/shell/mod.rs | 144 ++--------- src/shell/workspace.rs | 163 +++++++++++- 10 files changed, 707 insertions(+), 382 deletions(-) diff --git a/src/input/mod.rs b/src/input/mod.rs index 846fae87..44d7862f 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only use crate::{ + backend::render::ElementFilter, config::{ key_bindings::{ cosmic_keystate_from_smithay, cosmic_modifiers_eq_smithay, @@ -10,7 +11,11 @@ use crate::{ }, input::gestures::{GestureState, SwipeAction}, shell::{ - focus::target::{KeyboardFocusTarget, PointerFocusTarget}, + focus::{ + render_input_order, + target::{KeyboardFocusTarget, PointerFocusTarget}, + Stage, + }, grabs::{ReleaseMode, ResizeEdge}, layout::{ floating::ResizeGrabMarker, @@ -39,10 +44,7 @@ use smithay::{ TabletToolButtonEvent, TabletToolEvent, TabletToolProximityEvent, TabletToolTipEvent, TabletToolTipState, TouchEvent, }, - desktop::{ - layer_map_for_output, space::SpaceElement, utils::under_from_surface_tree, - WindowSurfaceType, - }, + desktop::{utils::under_from_surface_tree, WindowSurfaceType}, input::{ keyboard::{FilterResult, KeysymHandle, ModifiersState}, pointer::{ @@ -58,15 +60,13 @@ use smithay::{ reexports::{ input::Device as InputDevice, wayland_server::protocol::wl_shm::Format as ShmFormat, }, - utils::{Point, Serial, SERIAL_COUNTER}, + utils::{Point, Rectangle, Serial, SERIAL_COUNTER}, wayland::{ keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitorSeat, pointer_constraints::{with_pointer_constraint, PointerConstraint}, seat::WaylandFocus, - shell::wlr_layer::Layer as WlrLayer, tablet_manager::{TabletDescriptor, TabletSeatTrait}, }, - xwayland::X11Surface, }; use tracing::{error, trace}; use xkbcommon::xkb::{Keycode, Keysym}; @@ -76,6 +76,7 @@ use std::{ borrow::Cow, cell::RefCell, collections::HashSet, + ops::ControlFlow, time::{Duration, Instant}, }; @@ -329,9 +330,8 @@ impl State { } else if self.common.config.cosmic_conf.focus_follows_cursor { let shell = self.common.shell.read().unwrap(); let old_keyboard_target = - shell.keyboard_target_from_position(original_position, ¤t_output); - let new_keyboard_target = - shell.keyboard_target_from_position(position, &output); + State::element_under(original_position, ¤t_output, &*shell); + let new_keyboard_target = State::element_under(position, &output, &*shell); if old_keyboard_target != new_keyboard_target && new_keyboard_target.is_some() @@ -497,7 +497,8 @@ impl State { }); } - let shell = self.common.shell.read().unwrap(); + let mut shell = self.common.shell.write().unwrap(); + shell.update_pointer_position(position.to_local(&output), &output); if output != current_output { for session in cursor_sessions_for_output(&*shell, ¤t_output) { @@ -638,15 +639,15 @@ impl State { let global_position = seat.get_pointer().unwrap().current_location().as_global(); - let shell = self.common.shell.write().unwrap(); - let under = shell.keyboard_target_from_position(global_position, &output); - // Don't check override redirect windows, because we don't set keyboard focus to them explicitly. - // These cases are handled by the XwaylandKeyboardGrab. - if let Some(target) = shell.element_under(global_position, &output) { - if seat.get_keyboard().unwrap().modifier_state().logo - && !shortcuts_inhibited - { - if let Some(surface) = target.toplevel().map(Cow::into_owned) { + let under = { + let shell = self.common.shell.read().unwrap(); + State::element_under(global_position, &output, &shell) + }; + if let Some(target) = under { + if let Some(surface) = target.toplevel().map(Cow::into_owned) { + if seat.get_keyboard().unwrap().modifier_state().logo + && !shortcuts_inhibited + { let seat_clone = seat.clone(); let mouse_button = PointerButtonEvent::button(&event); @@ -754,10 +755,9 @@ impl State { } } } - } - std::mem::drop(shell); - Shell::set_focus(self, under.as_ref(), &seat, Some(serial), false); + Shell::set_focus(self, Some(&target), &seat, Some(serial), false); + } } } else { let mut shell = self.common.shell.write().unwrap(); @@ -1814,142 +1814,263 @@ impl State { } } - // TODO: Try to get rid of the *mutable* Shell references (needed for hovered_stack in floating_layout) + pub fn element_under( + global_pos: Point, + output: &Output, + shell: &Shell, + ) -> Option { + let (previous_workspace, workspace) = shell.workspaces.active(output); + let (previous_idx, idx) = shell.workspaces.active_num(output); + let previous_workspace = previous_workspace + .zip(previous_idx) + .map(|((w, start), idx)| (w.handle, idx, start)); + let workspace = (workspace.handle, idx); + let element_filter = if workspace_overview_is_open(output) { + ElementFilter::LayerShellOnly + } else { + ElementFilter::All + }; + + render_input_order( + shell, + output, + previous_workspace, + workspace, + element_filter, + |stage| { + match stage { + Stage::SessionLock(lock_surface) => { + return ControlFlow::Break(Ok(lock_surface + .cloned() + .map(KeyboardFocusTarget::LockSurface))) + } + Stage::LayerPopup { + layer, + popup, + location, + } => { + if layer.can_receive_keyboard_focus() { + let surface = popup.wl_surface(); + if under_from_surface_tree( + surface, + global_pos.as_logical(), + location.as_logical(), + WindowSurfaceType::POPUP | WindowSurfaceType::SUBSURFACE, + ) + .is_some() + { + return ControlFlow::Break(Ok(Some( + KeyboardFocusTarget::LayerSurface(layer), + ))); + } + } + } + Stage::LayerSurface { layer, location } => { + if layer.can_receive_keyboard_focus() { + if under_from_surface_tree( + layer.wl_surface(), + global_pos.as_logical(), + location.as_logical(), + WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE, + ) + .is_some() + { + return ControlFlow::Break(Ok(Some( + KeyboardFocusTarget::LayerSurface(layer), + ))); + } + } + } + Stage::OverrideRedirect { .. } => { + // Override redirect windows take a grab on their own via + // the Xwayland keyboard grab protocol. Don't focus them via click. + } + Stage::StickyPopups(layout) => { + if let Some(element) = + layout.popup_element_under(global_pos.to_local(output)) + { + return ControlFlow::Break(Ok(Some(element))); + } + } + Stage::Sticky(layout) => { + if let Some(element) = + layout.toplevel_element_under(global_pos.to_local(output)) + { + return ControlFlow::Break(Ok(Some(element))); + } + } + Stage::WorkspacePopups { workspace, offset } => { + let location = global_pos + offset.as_global().to_f64(); + let output = workspace.output(); + let output_geo = output.geometry().to_local(output); + if Rectangle::from_loc_and_size(offset.as_local(), output_geo.size) + .intersection(output_geo) + .is_some_and(|geometry| { + geometry.contains(global_pos.to_local(output).to_i32_round()) + }) + { + if let Some(element) = workspace.popup_element_under(location) { + return ControlFlow::Break(Ok(Some(element))); + } + } + } + Stage::Workspace { workspace, offset } => { + let location = global_pos + offset.as_global().to_f64(); + let output = workspace.output(); + let output_geo = output.geometry().to_local(output); + if Rectangle::from_loc_and_size(offset.as_local(), output_geo.size) + .intersection(output_geo) + .is_some_and(|geometry| { + geometry.contains(global_pos.to_local(output).to_i32_round()) + }) + { + if let Some(element) = workspace.toplevel_element_under(location) { + return ControlFlow::Break(Ok(Some(element))); + } + } + } + } + ControlFlow::Continue(()) + }, + ) + .ok() + .flatten() + } + pub fn surface_under( global_pos: Point, output: &Output, - shell: &mut Shell, + shell: &Shell, ) -> Option<(PointerFocusTarget, Point)> { - let session_lock = shell.session_lock.as_ref(); + let (previous_workspace, workspace) = shell.workspaces.active(output); + let (previous_idx, idx) = shell.workspaces.active_num(output); + let previous_workspace = previous_workspace + .zip(previous_idx) + .map(|((w, start), idx)| (w.handle, idx, start)); + let workspace = (workspace.handle, idx); + + let element_filter = if workspace_overview_is_open(output) { + ElementFilter::LayerShellOnly + } else { + ElementFilter::All + }; + let relative_pos = global_pos.to_local(output); let output_geo = output.geometry(); - - if let Some(session_lock) = session_lock { - return session_lock.surfaces.get(output).map(|surface| { - ( - PointerFocusTarget::WlSurface { - surface: surface.wl_surface().clone(), - toplevel: None, - }, - output_geo.loc.to_f64(), - ) - }); - } - - if let Some(window) = shell.workspaces.active(output).1.get_fullscreen() { - let layers = layer_map_for_output(output); - if let Some(layer) = layers.layer_under(WlrLayer::Overlay, relative_pos.as_logical()) { - let layer_loc = layers.layer_geometry(layer).unwrap().loc; - if let Some((wl_surface, surface_loc)) = layer.surface_under( - relative_pos.as_logical() - layer_loc.to_f64(), - WindowSurfaceType::ALL, - ) { - return Some(( - PointerFocusTarget::WlSurface { - surface: wl_surface, - toplevel: None, - }, - (output_geo.loc + layer_loc.as_global() + surface_loc.as_global()).to_f64(), - )); - } - } - if let Some((surface, geo)) = shell - .override_redirect_windows - .iter() - .find(|or| { - or.is_in_input_region( - &(global_pos.as_logical() - X11Surface::geometry(*or).loc.to_f64()), - ) - }) - .and_then(|or| { - or.wl_surface() - .map(|surface| (surface, X11Surface::geometry(or).loc.as_global().to_f64())) - }) - { - return Some(( - PointerFocusTarget::WlSurface { - surface, - toplevel: None, - }, - geo, - )); - } - PointerFocusTarget::under_surface(window, relative_pos.as_logical()).map( - |(target, surface_loc)| { - (target, (output_geo.loc + surface_loc.as_global()).to_f64()) - }, - ) - } else { - { - let layers = layer_map_for_output(output); - if let Some(layer) = layers - .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 let Some((wl_surface, surface_loc)) = layer.surface_under( - relative_pos.as_logical() - layer_loc.to_f64(), - WindowSurfaceType::ALL, - ) { - return Some(( - PointerFocusTarget::WlSurface { - surface: wl_surface, - toplevel: None, - }, - (output_geo.loc + layer_loc.as_global() + surface_loc.as_global()) - .to_f64(), - )); + let overview = shell.overview_mode().0; + + render_input_order( + shell, + output, + previous_workspace, + workspace, + element_filter, + |stage| { + match stage { + Stage::SessionLock(lock_surface) => { + return ControlFlow::Break(Ok(lock_surface.map(|surface| { + ( + PointerFocusTarget::WlSurface { + surface: surface.wl_surface().clone(), + toplevel: None, + }, + output_geo.loc.to_f64(), + ) + }))); } - } - } - if let Some((surface, geo)) = shell - .override_redirect_windows - .iter() - .find(|or| { - or.is_in_input_region( - &(global_pos.as_logical() - X11Surface::geometry(*or).loc.to_f64()), - ) - }) - .and_then(|or| { - or.wl_surface() - .map(|surface| (surface, X11Surface::geometry(or).loc.as_global().to_f64())) - }) - { - return Some(( - PointerFocusTarget::WlSurface { - surface, - toplevel: None, - }, - geo, - )); - } - if let Some((target, loc)) = shell.surface_under(global_pos, output) { - 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 layer_loc = layers.layer_geometry(layer).unwrap().loc; - if let Some((wl_surface, surface_loc)) = layer.surface_under( - relative_pos.as_logical() - layer_loc.to_f64(), - WindowSurfaceType::ALL, - ) { - return Some(( - PointerFocusTarget::WlSurface { - surface: wl_surface, - toplevel: None, - }, - (output_geo.loc + layer_loc.as_global() + surface_loc.as_global()) - .to_f64(), - )); + Stage::LayerPopup { + popup, location, .. + } => { + let surface = popup.wl_surface(); + if let Some((surface, surface_loc)) = under_from_surface_tree( + surface, + global_pos.as_logical(), + location.as_logical(), + WindowSurfaceType::ALL, + ) { + return ControlFlow::Break(Ok(Some(( + PointerFocusTarget::WlSurface { + surface, + toplevel: None, + }, + surface_loc.as_global().to_f64(), + )))); + } + } + Stage::LayerSurface { layer, location } => { + let surface = layer.wl_surface(); + if let Some((surface, surface_loc)) = under_from_surface_tree( + surface, + global_pos.as_logical(), + location.as_logical(), + WindowSurfaceType::ALL, + ) { + return ControlFlow::Break(Ok(Some(( + PointerFocusTarget::WlSurface { + surface, + toplevel: None, + }, + surface_loc.as_global().to_f64(), + )))); + } + } + Stage::OverrideRedirect { surface, location } => { + if let Some(surface) = surface.wl_surface() { + if let Some((surface, surface_loc)) = under_from_surface_tree( + &surface, + global_pos.as_logical(), + location.as_logical(), + WindowSurfaceType::ALL, + ) { + return ControlFlow::Break(Ok(Some(( + PointerFocusTarget::WlSurface { + surface, + toplevel: None, + }, + surface_loc.as_global().to_f64(), + )))); + } + } + } + Stage::StickyPopups(floating_layer) => { + if let Some(under) = floating_layer + .popup_surface_under(relative_pos) + .map(|(target, point)| (target, point.to_global(output))) + { + return ControlFlow::Break(Ok(Some(under))); + } + } + Stage::Sticky(floating_layer) => { + if let Some(under) = floating_layer + .toplevel_surface_under(relative_pos) + .map(|(target, point)| (target, point.to_global(output))) + { + return ControlFlow::Break(Ok(Some(under))); + } + } + Stage::WorkspacePopups { workspace, offset } => { + let global_pos = global_pos + offset.to_f64().as_global(); + if let Some(under) = + workspace.popup_surface_under(global_pos, overview.clone()) + { + return ControlFlow::Break(Ok(Some(under))); + } + } + Stage::Workspace { workspace, offset } => { + let global_pos = global_pos + offset.to_f64().as_global(); + if let Some(under) = + workspace.toplevel_surface_under(global_pos, overview.clone()) + { + return ControlFlow::Break(Ok(Some(under))); + } } } - } - None - } + + ControlFlow::Continue(()) + }, + ) + .ok() + .flatten() } } diff --git a/src/shell/element/mod.rs b/src/shell/element/mod.rs index 0860c07d..2a8cebab 100644 --- a/src/shell/element/mod.rs +++ b/src/shell/element/mod.rs @@ -302,10 +302,11 @@ impl CosmicMapped { pub fn focus_under( &self, relative_pos: Point, + surface_type: WindowSurfaceType, ) -> Option<(PointerFocusTarget, Point)> { match &self.element { - CosmicMappedInternal::Stack(stack) => stack.focus_under(relative_pos), - CosmicMappedInternal::Window(window) => window.focus_under(relative_pos), + CosmicMappedInternal::Stack(stack) => stack.focus_under(relative_pos, surface_type), + CosmicMappedInternal::Window(window) => window.focus_under(relative_pos, surface_type), _ => unreachable!(), } } diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index 388eb813..3a0597f6 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -420,48 +420,52 @@ impl CosmicStack { pub fn focus_under( &self, mut relative_pos: Point, + surface_type: WindowSurfaceType, ) -> Option<(PointerFocusTarget, Point)> { self.0.with_program(|p| { let mut stack_ui = None; let geo = p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)].geometry(); - let point_i32 = relative_pos.to_i32_round::(); - if (point_i32.x - geo.loc.x >= -RESIZE_BORDER && point_i32.x - geo.loc.x < 0) - || (point_i32.y - geo.loc.y >= -RESIZE_BORDER && point_i32.y - geo.loc.y < 0) - || (point_i32.x - geo.loc.x >= geo.size.w - && point_i32.x - geo.loc.x < geo.size.w + RESIZE_BORDER) - || (point_i32.y - geo.loc.y >= geo.size.h - && point_i32.y - geo.loc.y < geo.size.h + TAB_HEIGHT + RESIZE_BORDER) - { - stack_ui = Some(( - PointerFocusTarget::StackUI(self.clone()), - Point::from((0., 0.)), - )); - } + if surface_type.contains(WindowSurfaceType::TOPLEVEL) { + let point_i32 = relative_pos.to_i32_round::(); + if (point_i32.x - geo.loc.x >= -RESIZE_BORDER && point_i32.x - geo.loc.x < 0) + || (point_i32.y - geo.loc.y >= -RESIZE_BORDER && point_i32.y - geo.loc.y < 0) + || (point_i32.x - geo.loc.x >= geo.size.w + && point_i32.x - geo.loc.x < geo.size.w + RESIZE_BORDER) + || (point_i32.y - geo.loc.y >= geo.size.h + && point_i32.y - geo.loc.y < geo.size.h + TAB_HEIGHT + RESIZE_BORDER) + { + stack_ui = Some(( + PointerFocusTarget::StackUI(self.clone()), + Point::from((0., 0.)), + )); + } - if point_i32.y - geo.loc.y < TAB_HEIGHT { - stack_ui = Some(( - PointerFocusTarget::StackUI(self.clone()), - Point::from((0., 0.)), - )); + if point_i32.y - geo.loc.y < TAB_HEIGHT { + stack_ui = Some(( + PointerFocusTarget::StackUI(self.clone()), + Point::from((0., 0.)), + )); + } } relative_pos.y -= TAB_HEIGHT as f64; let active_window = &p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)]; - active_window - .0 - .surface_under(relative_pos, WindowSurfaceType::ALL) - .map(|(surface, surface_offset)| { - ( - PointerFocusTarget::WlSurface { - surface, - toplevel: Some(active_window.clone().into()), - }, - surface_offset.to_f64() + Point::from((0., TAB_HEIGHT as f64)), - ) - }) - .or(stack_ui) + stack_ui.or_else(|| { + active_window + .0 + .surface_under(relative_pos, surface_type) + .map(|(surface, surface_offset)| { + ( + PointerFocusTarget::WlSurface { + surface, + toplevel: Some(active_window.clone().into()), + }, + surface_offset.to_f64() + Point::from((0., TAB_HEIGHT as f64)), + ) + }) + }) }) } @@ -1034,7 +1038,7 @@ impl SpaceElement for CosmicStack { }) } fn is_in_input_region(&self, point: &Point) -> bool { - self.focus_under(*point).is_some() + self.focus_under(*point, WindowSurfaceType::ALL).is_some() } fn set_activate(&self, activated: bool) { SpaceElement::set_activate(&self.0, activated); diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index 9070994c..97d24e55 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -243,11 +243,12 @@ impl CosmicWindow { pub fn focus_under( &self, mut relative_pos: Point, + surface_type: WindowSurfaceType, ) -> Option<(PointerFocusTarget, Point)> { self.0.with_program(|p| { let mut offset = Point::from((0., 0.)); let mut window_ui = None; - if p.has_ssd(false) { + if p.has_ssd(false) && surface_type.contains(WindowSurfaceType::TOPLEVEL) { let geo = p.window.geometry(); let point_i32 = relative_pos.to_i32_round::(); @@ -275,19 +276,19 @@ impl CosmicWindow { offset.y += SSD_HEIGHT as f64; } - p.window - .0 - .surface_under(relative_pos, WindowSurfaceType::ALL) - .map(|(surface, surface_offset)| { - ( - PointerFocusTarget::WlSurface { - surface, - toplevel: Some(p.window.clone().into()), - }, - (offset + surface_offset.to_f64()), - ) - }) - .or(window_ui) + window_ui.or_else(|| { + p.window.0.surface_under(relative_pos, surface_type).map( + |(surface, surface_offset)| { + ( + PointerFocusTarget::WlSurface { + surface, + toplevel: Some(p.window.clone().into()), + }, + (offset + surface_offset.to_f64()), + ) + }, + ) + }) }) } @@ -561,7 +562,7 @@ impl SpaceElement for CosmicWindow { }) } fn is_in_input_region(&self, point: &Point) -> bool { - self.focus_under(*point).is_some() + self.focus_under(*point, WindowSurfaceType::ALL).is_some() } fn set_activate(&self, activated: bool) { if self diff --git a/src/shell/focus/mod.rs b/src/shell/focus/mod.rs index a3b855a3..9f3bc71a 100644 --- a/src/shell/focus/mod.rs +++ b/src/shell/focus/mod.rs @@ -232,8 +232,8 @@ fn update_focus_state( if should_update_cursor && state.common.config.cosmic_conf.cursor_follows_focus { if target.is_some() { //need to borrow mutably for surface under - let mut shell = state.common.shell.write().unwrap(); - // get geometry of the target element + let shell = state.common.shell.read().unwrap(); + // get the top left corner of the target element let geometry = shell.focused_geometry(target.unwrap()); if let Some(geometry) = geometry { // get the center of the target element @@ -247,10 +247,9 @@ fn update_focus_state( .cloned() .unwrap_or(seat.active_output()); - let focus = shell - .surface_under(new_pos, &output) + let focus = State::surface_under(new_pos, &output, &*shell) .map(|(focus, loc)| (focus, loc.as_logical())); - //drop here to avoid multiple mutable borrows + //drop here to avoid multiple borrows mem::drop(shell); seat.get_pointer().unwrap().motion( state, diff --git a/src/shell/grabs/moving.rs b/src/shell/grabs/moving.rs index 25106fa6..3bbb83ab 100644 --- a/src/shell/grabs/moving.rs +++ b/src/shell/grabs/moving.rs @@ -27,7 +27,7 @@ use smithay::{ ImportAll, ImportMem, Renderer, }, }, - desktop::{layer_map_for_output, space::SpaceElement}, + desktop::{layer_map_for_output, space::SpaceElement, WindowSurfaceType}, input::{ pointer::{ AxisFrame, ButtonEvent, CursorIcon, GestureHoldBeginEvent, GestureHoldEndEvent, @@ -864,7 +864,7 @@ impl Drop for MoveGrab { let current_location = pointer.current_location(); if let Some((target, offset)) = - mapped.focus_under(current_location - position.as_logical().to_f64()) + mapped.focus_under(current_location - position.as_logical().to_f64(), WindowSurfaceType::ALL) { pointer.motion( state, diff --git a/src/shell/layout/floating/mod.rs b/src/shell/layout/floating/mod.rs index 5c80fe62..a3f10355 100644 --- a/src/shell/layout/floating/mod.rs +++ b/src/shell/layout/floating/mod.rs @@ -728,16 +728,115 @@ impl FloatingLayout { self.space.element_geometry(elem).map(RectExt::as_local) } - pub fn element_under(&self, location: Point) -> Option { + pub fn popup_element_under(&self, location: Point) -> Option { self.space - .element_under(location.as_logical()) - .map(|(mapped, _)| mapped.clone().into()) + .elements() + .rev() + .map(|e| (e, self.space.element_location(e).unwrap() - e.geometry().loc)) + .filter(|(e, render_location)| { + let mut bbox = e.bbox(); + bbox.loc += *render_location; + bbox.to_f64().contains(location.as_logical()) + }) + .find_map(|(e, render_location)| { + let render_location = render_location + .as_local() + .to_f64(); + let point = location - render_location; + if e.focus_under(point.as_logical(), WindowSurfaceType::POPUP | WindowSurfaceType::SUBSURFACE).is_some() { + Some(e.clone().into()) + } else { + None + } + }) + } + + pub fn toplevel_element_under(&self, location: Point) -> Option { + self.space + .elements() + .rev() + .map(|e| (e, self.space.element_location(e).unwrap() - e.geometry().loc)) + .filter(|(e, render_location)| { + let mut bbox = e.bbox(); + bbox.loc += *render_location; + bbox.to_f64().contains(location.as_logical()) + }) + .find_map(|(e, render_location)| { + let render_location = render_location + .as_local() + .to_f64(); + let point = location - render_location; + if e.focus_under(point.as_logical(), WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE).is_some() { + Some(e.clone().into()) + } else { + None + } + }) } - pub fn surface_under( - &mut self, + pub fn popup_surface_under( + &self, + location: Point, + ) -> Option<(PointerFocusTarget, Point)> { + self.space + .elements() + .rev() + .map(|e| (e, self.space.element_location(e).unwrap() - e.geometry().loc)) + .filter(|(e, render_location)| { + let mut bbox = e.bbox(); + bbox.loc += *render_location; + bbox.to_f64().contains(location.as_logical()) + }) + .find_map(|(e, render_location)| { + let render_location = render_location + .as_local() + .to_f64(); + let point = location - render_location; + e.focus_under( + point.as_logical(), + WindowSurfaceType::POPUP | WindowSurfaceType::SUBSURFACE, + ) + .map(|(surface, surface_offset)| { + (surface, render_location + surface_offset.as_local()) + }) + }) + } + + pub fn toplevel_surface_under( + &self, location: Point, ) -> Option<(PointerFocusTarget, Point)> { + self.space + .elements() + .rev() + .map(|e| (e, self.space.element_location(e).unwrap() - e.geometry().loc)) + .filter(|(e, render_location)| { + let mut bbox = e.bbox(); + bbox.loc += *render_location; + bbox.to_f64().contains(location.as_logical()) + }) + .find_map(|(e, render_location)| { + let render_location = render_location.as_local().to_f64(); + let point = location - render_location; + e.focus_under( + point.as_logical(), + WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE, + ) + .map(|(surface, surface_offset)| { + ( + surface, + render_location + surface_offset.as_local(), + ) + }) + }) + } + + pub fn update_pointer_position(&mut self, location: Option>) { + let Some(location) = location else { + self.hovered_stack.take(); + return; + }; + let res = self .space .element_under(location.as_logical()) @@ -754,15 +853,6 @@ impl FloatingLayout { } else { self.hovered_stack.take(); } - - res.and_then(|(element, space_offset)| { - let point = location - space_offset.to_f64(); - element - .focus_under(point.as_logical()) - .map(|(surface, surface_offset)| { - (surface, space_offset.to_f64() + surface_offset.as_local()) - }) - }) } pub fn stacking_indicator(&self) -> Option> { diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index 308f8f8e..c814f304 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -56,7 +56,7 @@ use smithay::{ glow::GlowRenderer, ImportAll, ImportMem, Renderer, }, - desktop::{layer_map_for_output, space::SpaceElement, PopupKind}, + desktop::{layer_map_for_output, space::SpaceElement, PopupKind, WindowSurfaceType}, input::Seat, output::Output, reexports::wayland_server::Client, @@ -3104,17 +3104,38 @@ impl TilingLayout { None } + + pub fn popup_element_under(&self, location_f64: Point) -> Option { + let location = location_f64.to_i32_round(); + + for (mapped, geo) in self.mapped() { + if !mapped.bbox().contains((location - geo.loc).as_logical()) { + continue; + } - pub fn element_under(&self, location_f64: Point) -> Option { + if mapped.focus_under( + (location_f64 - geo.loc.to_f64()).as_logical() + mapped.geometry().loc.to_f64(), + WindowSurfaceType::POPUP | WindowSurfaceType::SUBSURFACE, + ).is_some() { + return Some(mapped.clone().into()); + } + } + + None + } + + pub fn toplevel_element_under(&self, location_f64: Point) -> Option { let location = location_f64.to_i32_round(); for (mapped, geo) in self.mapped() { if !mapped.bbox().contains((location - geo.loc).as_logical()) { continue; } - if mapped.is_in_input_region( - &((location_f64 - geo.loc.to_f64()).as_logical() + mapped.geometry().loc.to_f64()), - ) { + + if mapped.focus_under( + (location_f64 - geo.loc.to_f64()).as_logical() + mapped.geometry().loc.to_f64(), + WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE, + ).is_some() { return Some(mapped.clone().into()); } } @@ -3122,33 +3143,42 @@ impl TilingLayout { None } - pub fn surface_under( - &mut self, + pub fn popup_surface_under( + &self, location_f64: Point, overview: OverviewMode, ) -> Option<(PointerFocusTarget, Point)> { - let gaps = self.gaps(); - let last_overview_hover = &mut self.last_overview_hover; - let placeholder_id = &self.placeholder_id; - let tree = &self.queue.trees.back().unwrap().0; - let root = tree.root_node_id()?; let location = location_f64.to_i32_round(); - { - 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::None) { + for (mapped, geo) in self.mapped() { + if !mapped.bbox().contains((location - geo.loc).as_logical()) { + continue; + } + if let Some((target, surface_offset)) = mapped.focus_under( + (location_f64 - geo.loc.to_f64()).as_logical() + mapped.geometry().loc.to_f64(), + WindowSurfaceType::POPUP | WindowSurfaceType::SUBSURFACE, + ) { + return Some(( + target, + geo.loc.to_f64() - mapped.geometry().loc.as_local().to_f64() + + surface_offset.as_local(), + )); + } } } - if !matches!( - overview, - OverviewMode::Started(_, _) | OverviewMode::Active(_) - ) { - last_overview_hover.take(); - } + None + } + + pub fn toplevel_surface_under( + &self, + location_f64: Point, + overview: OverviewMode, + ) -> Option<(PointerFocusTarget, Point)> { + let tree = &self.queue.trees.back().unwrap().0; + let root = tree.root_node_id()?; + let location = location_f64.to_i32_round(); if matches!(overview, OverviewMode::None) { for (mapped, geo) in self.mapped() { @@ -3157,6 +3187,7 @@ impl TilingLayout { } if let Some((target, surface_offset)) = mapped.focus_under( (location_f64 - geo.loc.to_f64()).as_logical() + mapped.geometry().loc.to_f64(), + WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE, ) { return Some(( target, @@ -3204,7 +3235,10 @@ impl TilingLayout { + mapped.geometry().loc.to_f64().as_local()) .as_logical(); mapped - .focus_under(test_point) + .focus_under( + test_point, + WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE, + ) .map(|(surface, surface_offset)| { ( surface, @@ -3257,7 +3291,37 @@ impl TilingLayout { } _ => None, } - } else if matches!( + } else { + None + } + } + + pub fn update_pointer_position( + &mut self, + location_f64: Option>, + overview: OverviewMode, + ) { + let gaps = self.gaps(); + let last_overview_hover = &mut self.last_overview_hover; + let placeholder_id = &self.placeholder_id; + let tree = &self.queue.trees.back().unwrap().0; + let Some(root) = tree.root_node_id() else { + return; + }; + + if !matches!( + overview, + OverviewMode::Started(_, _) | OverviewMode::Active(_) + ) || location_f64.is_none() + { + last_overview_hover.take(); + return; + } + + let location_f64 = location_f64.unwrap(); + let location = location_f64.to_i32_round(); + + if matches!( overview.active_trigger(), Some(Trigger::Pointer(_) | Trigger::Touch(_)) ) { @@ -3319,7 +3383,9 @@ impl TilingLayout { } if let Some(res_id) = result { - let mut last_geometry = *geometries.get(&res_id)?; + let Some(mut last_geometry) = geometries.get(&res_id).copied() else { + return; + }; let node = tree.get(&res_id).unwrap(); let data = node.data().clone(); @@ -3716,10 +3782,6 @@ impl TilingLayout { } } } - - None - } else { - None } } diff --git a/src/shell/mod.rs b/src/shell/mod.rs index ca5ec2f8..e8e422d5 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -54,8 +54,6 @@ use smithay::{ xwayland::X11Surface, }; -use smithay::wayland::shell::wlr_layer::Layer as WlrLayer; - use crate::{ backend::render::animations::spring::{Spring, SpringParams}, config::Config, @@ -1532,97 +1530,6 @@ impl Shell { } } - /// Derives a keyboard focus target from a global position, and indicates whether the - /// the shell should start a move request event. Used during cursor related focus checks - pub fn keyboard_target_from_position( - &self, - global_position: Point, - output: &Output, - ) -> Option { - let relative_pos = global_position.to_local(output); - - let mut under: Option = None; - // if the lockscreen is active - if let Some(session_lock) = self.session_lock.as_ref() { - under = session_lock - .surfaces - .get(output) - .map(|lock| lock.clone().into()); - // if the output can receive keyboard focus - } else if let Some(window) = self.active_space(output).get_fullscreen() { - let layers = layer_map_for_output(output); - if let Some(layer) = 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.as_logical() - layer_loc.to_f64(), - WindowSurfaceType::ALL, - ) - .is_some() - { - under = Some(layer.clone().into()); - } - } else { - under = Some(window.clone().into()); - } - } else { - let done = { - let layers = layer_map_for_output(output); - if let Some(layer) = layers - .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.as_logical() - layer_loc.to_f64(), - WindowSurfaceType::ALL, - ) - .is_some() - { - under = Some(layer.clone().into()); - true - } else { - false - } - } else { - false - } - }; - if !done { - // Don't check override redirect windows, because we don't set keyboard focus to them explicitly. - // These cases are handled by the XwaylandKeyboardGrab. - if let Some(target) = self.element_under(global_position, output) { - under = Some(target); - } else { - 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 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() - { - under = Some(layer.clone().into()); - } - }; - } - } - } - - under - } - /// Coerce a keyboard focus target into a CosmicMapped element. This is useful when performing window specific /// actions, such as closing a window pub fn focused_element(&self, focus_target: &KeyboardFocusTarget) -> Option { @@ -2053,6 +1960,26 @@ impl Shell { self.pending_windows.retain(|(s, _, _)| s.alive()); } + pub fn update_pointer_position(&mut self, location: Point, output: &Output) { + for (o, set) in self.workspaces.sets.iter_mut() { + if o == output { + set.sticky_layer.update_pointer_position(Some(location)); + for (i, workspace) in set.workspaces.iter_mut().enumerate() { + if i == set.active { + workspace.update_pointer_position(Some(location), self.overview_mode.clone()); + } else { + workspace.update_pointer_position(None, self.overview_mode.clone()); + } + } + } else { + set.sticky_layer.update_pointer_position(None); + for workspace in &mut set.workspaces { + workspace.update_pointer_position(None, self.overview_mode.clone()); + } + } + } + } + pub fn remap_unfullscreened_window( &mut self, mapped: CosmicMapped, @@ -2382,33 +2309,6 @@ impl Shell { } } - pub fn element_under( - &self, - location: Point, - output: &Output, - ) -> Option { - self.workspaces.sets.get(output).and_then(|set| { - set.sticky_layer - .space - .element_under(location.to_local(output).as_logical()) - .map(|(mapped, _)| mapped.clone().into()) - .or_else(|| set.workspaces[set.active].element_under(location)) - }) - } - pub fn surface_under( - &mut self, - location: Point, - output: &Output, - ) -> Option<(PointerFocusTarget, Point)> { - let overview = self.overview_mode.clone(); - self.workspaces.sets.get_mut(output).and_then(|set| { - set.sticky_layer - .surface_under(location.to_local(output)) - .map(|(target, offset)| (target, offset.to_global(output))) - .or_else(|| set.workspaces[set.active].surface_under(location, overview)) - }) - } - #[must_use] pub fn move_window( &mut self, @@ -2782,7 +2682,7 @@ impl Shell { let mapped = if move_out_of_stack { let new_mapped: CosmicMapped = CosmicWindow::new(window.clone(), evlh.clone(), self.theme.clone()).into(); - start_data.set_focus(new_mapped.focus_under((0., 0.).into())); + start_data.set_focus(new_mapped.focus_under((0., 0.).into(), WindowSurfaceType::ALL)); new_mapped } else { old_mapped.clone() @@ -3250,7 +3150,7 @@ impl Shell { let element_offset = (new_loc - geometry.loc).as_logical(); let focus = mapped - .focus_under(element_offset.to_f64()) + .focus_under(element_offset.to_f64(), WindowSurfaceType::ALL) .map(|(target, surface_offset)| (target, (surface_offset + element_offset.to_f64()))); start_data.set_location(new_loc.as_logical().to_f64()); start_data.set_focus(focus.clone()); diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 0d0ac5f1..00f30285 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -34,7 +34,7 @@ use smithay::{ utils::{DamageSet, OpaqueRegions}, ImportAll, ImportMem, Renderer, }, - desktop::{layer_map_for_output, space::SpaceElement}, + desktop::{layer_map_for_output, space::SpaceElement, WindowSurfaceType}, input::Seat, output::Output, reexports::wayland_server::{Client, Resource}, @@ -434,6 +434,27 @@ impl Workspace { } } + fn fullscreen_geometry(&self) -> Option> { + self.fullscreen.as_ref().map(|fullscreen| { + let bbox = fullscreen.surface.bbox().as_local(); + + let mut full_geo = + Rectangle::from_loc_and_size((0, 0), self.output.geometry().size.as_local()); + if bbox != full_geo { + if bbox.size.w < full_geo.size.w { + full_geo.loc.x += (full_geo.size.w - bbox.size.w) / 2; + full_geo.size.w = bbox.size.w; + } + if bbox.size.h < full_geo.size.h { + full_geo.loc.y += (full_geo.size.h - bbox.size.h) / 2; + full_geo.size.h = bbox.size.h; + } + } + + full_geo + }) + } + pub fn element_for_surface(&self, surface: &S) -> Option<&CosmicMapped> where CosmicSurface: PartialEq, @@ -445,25 +466,151 @@ impl Workspace { .find(|e| e.windows().any(|(w, _)| &w == surface)) } - pub fn element_under(&self, location: Point) -> Option { + pub fn popup_element_under(&self, location: Point) -> Option { + if !self.output.geometry().contains(location.to_i32_round()) { + return None; + } let location = location.to_local(&self.output); + + if let Some(fullscreen) = self.fullscreen.as_ref() { + if !fullscreen.is_animating() { + let geometry = self.fullscreen_geometry().unwrap(); + return fullscreen + .surface + .0 + .surface_under( + (location + geometry.loc.to_f64()).as_logical(), + WindowSurfaceType::POPUP | WindowSurfaceType::SUBSURFACE, + ) + .is_some() + .then(|| KeyboardFocusTarget::Fullscreen(fullscreen.surface.clone())); + } + } + self.floating_layer - .element_under(location) - .or_else(|| self.tiling_layer.element_under(location)) + .popup_element_under(location) + .or_else(|| self.tiling_layer.popup_element_under(location)) } - pub fn surface_under( - &mut self, + pub fn toplevel_element_under( + &self, + location: Point, + ) -> Option { + if !self.output.geometry().contains(location.to_i32_round()) { + return None; + } + let location = location.to_local(&self.output); + + if let Some(fullscreen) = self.fullscreen.as_ref() { + if !fullscreen.is_animating() { + let geometry = self.fullscreen_geometry().unwrap(); + return fullscreen + .surface + .0 + .surface_under( + (location + geometry.loc.to_f64()).as_logical(), + WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE, + ) + .is_some() + .then(|| KeyboardFocusTarget::Fullscreen(fullscreen.surface.clone())); + } + } + + self.floating_layer + .toplevel_element_under(location) + .or_else(|| self.tiling_layer.toplevel_element_under(location)) + } + + pub fn popup_surface_under( + &self, location: Point, overview: OverviewMode, ) -> Option<(PointerFocusTarget, Point)> { + if !self.output.geometry().contains(location.to_i32_round()) { + return None; + } let location = location.to_local(&self.output); + + if let Some(fullscreen) = self.fullscreen.as_ref() { + if !fullscreen.is_animating() { + let geometry = self.fullscreen_geometry().unwrap(); + return fullscreen + .surface + .0 + .surface_under( + (location + geometry.loc.to_f64()).as_logical(), + WindowSurfaceType::POPUP | WindowSurfaceType::SUBSURFACE, + ) + .map(|(surface, surface_offset)| { + ( + PointerFocusTarget::WlSurface { + surface, + toplevel: Some(fullscreen.surface.clone().into()), + }, + (geometry.loc + surface_offset.as_local()) + .to_global(&self.output) + .to_f64(), + ) + }); + } + } + self.floating_layer - .surface_under(location) - .or_else(|| self.tiling_layer.surface_under(location, overview)) + .popup_surface_under(location) + .or_else(|| self.tiling_layer.popup_surface_under(location, overview)) .map(|(m, p)| (m, p.to_global(&self.output))) } + pub fn toplevel_surface_under( + &self, + location: Point, + overview: OverviewMode, + ) -> Option<(PointerFocusTarget, Point)> { + if !self.output.geometry().contains(location.to_i32_round()) { + return None; + } + let location = location.to_local(&self.output); + + if let Some(fullscreen) = self.fullscreen.as_ref() { + if !fullscreen.is_animating() { + let geometry = self.fullscreen_geometry().unwrap(); + return fullscreen + .surface + .0 + .surface_under( + (location + geometry.loc.to_f64()).as_logical(), + WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE, + ) + .map(|(surface, surface_offset)| { + ( + PointerFocusTarget::WlSurface { + surface, + toplevel: Some(fullscreen.surface.clone().into()), + }, + (geometry.loc + surface_offset.as_local()) + .to_global(&self.output) + .to_f64(), + ) + }); + } + } + + self.floating_layer + .toplevel_surface_under(location) + .or_else(|| self.tiling_layer.toplevel_surface_under(location, overview)) + .map(|(m, p)| (m, p.to_global(&self.output))) + } + + pub fn update_pointer_position( + &mut self, + location: Option>, + overview: OverviewMode, + ) { + self.floating_layer.update_pointer_position(location); + self.tiling_layer + .update_pointer_position(location, overview); + } + pub fn element_geometry(&self, elem: &CosmicMapped) -> Option> { self.floating_layer .element_geometry(elem)