use super::*; impl Ferese { pub fn surface_under(&self, position: Point) -> Option<(WlSurface, Point)> { if self.session_lock.active { return self.lock_surface_under(position); } if self.input_capture.active() { return None; } self.layer_surface_under(position, &[Layer::Overlay, Layer::Top]) .map(|(_, surface, origin)| (surface, origin)) .or_else(|| self.window_surface_under(position)) .or_else(|| { self.layer_surface_under(position, &[Layer::Bottom, Layer::Background]) .map(|(_, surface, origin)| (surface, origin)) }) } pub fn layer_under(&self, position: Point) -> Option<(LayerSurface, WlSurface, Point)> { if self.session_lock.active { return None; } if let Some(layer) = self.layer_surface_under(position, &[Layer::Overlay, Layer::Top]) { return Some(layer); } if self.window_surface_under(position).is_some() { return None; } self.layer_surface_under(position, &[Layer::Bottom, Layer::Background]) } pub(super) fn window_surface_under( &self, position: Point, ) -> Option<(WlSurface, Point)> { if self.overview.is_active() { return None; } let workspace = self.workspace_under_pointer(position)?; self.space.elements().rev().find_map(|window| { let id = self.windows.ids().get(window)?; if self.workspaces.workspace_for_window(*id) != Some(workspace) { return None; } let visual = self.presented_window_rect(*id)?; let inside_visual = position.x >= visual.x && position.y >= visual.y && position.x < visual.x + visual.width && position.y < visual.y + visual.height; if !inside_visual { return None; } let (source_x, source_y) = self.inverse_presented_window_point(*id, position.x, position.y)?; let source_point = Point::from((source_x, source_y)) + window.geometry().loc.to_f64(); let surface = window.surface_under(source_point, WindowSurfaceType::ALL); let (surface, surface_location) = match surface { Some(hit) => hit, None if self.window_fills_visual_bounds(*id, workspace) => { (window.toplevel()?.wl_surface().clone(), Point::from((0, 0))) } None => return None, }; let surface_point = source_point - surface_location.to_f64(); Some((surface, position - surface_point)) }) } pub(super) fn window_fills_visual_bounds(&self, id: WindowId, workspace: WorkspaceId) -> bool { self.windows.record(id).is_some_and(|w| w.maximized) || self .workspaces .workspace(workspace) .is_some_and(|workspace| workspace.fullscreen == Some(id)) } pub(super) fn layer_surface_under( &self, position: Point, layers: &[Layer], ) -> Option<(LayerSurface, WlSurface, Point)> { let output = self.space.output_under(position).next()?; let output_geometry = self.space.output_geometry(output)?; let output_position = position - output_geometry.loc.to_f64(); let map = layer_map_for_output(output); for requested in layers { if *requested == Layer::Top && self.output_has_fullscreen(output) { continue; } for layer in map.layers_on(*requested).rev() { let geometry = map.layer_geometry(layer)?; let layer_position = output_position - geometry.loc.to_f64(); let Some((surface, surface_location)) = layer.surface_under(layer_position, WindowSurfaceType::ALL) else { continue; }; let origin = output_geometry.loc + geometry.loc + surface_location; return Some((layer.clone(), surface, origin.to_f64())); } } None } pub fn send_cursor_frame(&self, output: &Output) { let CursorImageStatus::Surface(surface) = &self.cursor_status else { return; }; let Some(pointer) = self.seat.get_pointer() else { return; }; if self.space.output_under(pointer.current_location()).next() != Some(output) { return; } send_frames_surface_tree(surface, output, self.start_time.elapsed(), None, |surface, _| { self.display_presentation.callback_output(&surface.into()) }); } pub fn window_under_visual(&self, position: Point) -> Option { let overview_active = self.overview.is_active(); let workspace = self.workspace_under_pointer(position)?; let hit = |window: &Window| -> Option { if !self.window_content_ready(window) { return None; } let id = self.windows.ids().get(window)?; if self.workspaces.workspace_for_window(*id) != Some(workspace) { return None; } let visual = self.presented_window_rect(*id)?; let caption_height = if overview_active { 34.0 } else { 0.0 }; let inside_visual = position.x >= visual.x && position.y >= visual.y && position.x < visual.x + visual.width && position.y < visual.y + visual.height + caption_height; if !inside_visual { return None; } if !overview_active { let (source_x, source_y) = self.inverse_presented_window_point(*id, position.x, position.y)?; let source_point = Point::from((source_x, source_y)) + window.geometry().loc.to_f64(); if window.surface_under(source_point, WindowSurfaceType::ALL).is_none() && !self.window_fills_visual_bounds(*id, workspace) { return None; } } Some(window.clone()) }; if overview_active { self.windows.overview_windows().find_map(hit) } else { self.space.elements().rev().find_map(hit) } } pub(super) fn workspace_under_pointer(&self, position: Point) -> Option { let output = self.space.output_under(position).next()?; self.output_workspaces.active_workspace(self.output_id(output)?) } pub(crate) fn window_belongs_to_output(&self, window: WindowId, output: &Output) -> bool { let Some(output_id) = self.output_id(output) else { return false; }; if self.focus_cycle.is_some() && self.overview.is_active() { return self.overview.has_window_preview(window) && self.focus_preview_output(window) == Some(output_id); } let workspace = self.workspaces.workspace_for_window(window); workspace == self.output_workspaces.active_workspace(output_id) || self .workspace_slides .get(&output_id) .is_some_and(|slide| workspace.is_some_and(|workspace| slide.contains(workspace))) } pub fn visual_scale_for_window(&self, window: &Window) -> Option<(f64, f64)> { if !self.overview.is_presenting() { return Some((1.0, 1.0)); } let id = self.windows.ids().get(window)?; let geometry = self.windows.geometry(id)?; let source = geometry.client.committed_size?; let presented = self.presented_window_rect(*id)?; if source.width <= 0 || source.height <= 0 { return None; } Some(( presented.width / f64::from(source.width), presented.height / f64::from(source.height), )) } pub(crate) fn visual_rect_for_window(&self, window: &Window) -> Option> { let id = self.windows.ids().get(window)?; let rect = self.presented_window_rect(*id)?; let size = ClientSize::from_rect(rect); Some(Rectangle::new( (rect.x.round() as i32, rect.y.round() as i32).into(), (size.width, size.height).into(), )) } pub(crate) fn window_content_ready(&self, window: &Window) -> bool { window_has_buffer(window) && self .windows .ids() .get(window) .and_then(|id| self.windows.geometry(id)) .is_some_and(|geometry| geometry.client.committed_size.is_some()) } }