From 0092dac08cff7af16c0fd90b9f11d0559163a135 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Thu, 10 Oct 2024 23:18:04 +0200 Subject: [PATCH] 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)