diff --git a/src/main.rs b/src/main.rs index 4c9c48608..aba0727e8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,6 +12,7 @@ use anyhow::{Context, Result}; use state::State; use std::{env, ffi::OsString, os::unix::process::CommandExt, process, sync::Arc}; use tracing::{error, info, warn}; +use wayland::protocols::overlap_notify::OverlapNotifyState; use crate::wayland::handlers::compositor::client_compositor_state; @@ -131,6 +132,7 @@ fn main() -> Result<()> { } state.common.refresh(); state::Common::refresh_focus(state); + OverlapNotifyState::refresh(state); state.common.update_x11_stacking_order(); { diff --git a/src/state.rs b/src/state.rs index 9d71fbae2..fc2ba06ed 100644 --- a/src/state.rs +++ b/src/state.rs @@ -18,6 +18,7 @@ use crate::{ image_source::ImageSourceState, output_configuration::OutputConfigurationState, output_power::OutputPowerState, + overlap_notify::OverlapNotifyState, screencopy::ScreencopyState, toplevel_info::ToplevelInfoState, toplevel_management::{ManagementCapabilities, ToplevelManagementState}, @@ -219,6 +220,7 @@ pub struct Common { pub viewporter_state: ViewporterState, pub kde_decoration_state: KdeDecorationState, pub xdg_decoration_state: XdgDecorationState, + pub overlap_notify_state: OverlapNotifyState, // shell-related wayland state pub xdg_shell_state: XdgShellState, @@ -499,6 +501,8 @@ impl State { let output_state = OutputManagerState::new_with_xdg_output::(dh); let output_configuration_state = OutputConfigurationState::new(dh, client_is_privileged); let output_power_state = OutputPowerState::new::(dh, client_is_privileged); + let overlap_notify_state = + OverlapNotifyState::new::(dh, client_has_no_security_context); let presentation_state = PresentationState::new::(dh, clock.id() as u32); let primary_selection_state = PrimarySelectionState::new::(dh); let image_source_state = ImageSourceState::new::(dh, client_is_privileged); @@ -609,6 +613,7 @@ impl State { output_state, output_configuration_state, output_power_state, + overlap_notify_state, presentation_state, primary_selection_state, data_control_state, diff --git a/src/wayland/handlers/mod.rs b/src/wayland/handlers/mod.rs index 52414103b..870d74e2e 100644 --- a/src/wayland/handlers/mod.rs +++ b/src/wayland/handlers/mod.rs @@ -21,6 +21,7 @@ pub mod layer_shell; pub mod output; pub mod output_configuration; pub mod output_power; +pub mod overlap_notify; pub mod pointer_constraints; pub mod pointer_gestures; pub mod presentation; diff --git a/src/wayland/handlers/overlap_notify.rs b/src/wayland/handlers/overlap_notify.rs new file mode 100644 index 000000000..7b9486532 --- /dev/null +++ b/src/wayland/handlers/overlap_notify.rs @@ -0,0 +1,42 @@ +use smithay::{ + desktop::{layer_map_for_output, LayerSurface, WindowSurfaceType}, + output::Output, + reexports::wayland_protocols_wlr::layer_shell::v1::server::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, +}; + +use crate::{ + state::State, + wayland::protocols::overlap_notify::{ + delegate_overlap_notify, OverlapNotifyHandler, OverlapNotifyState, + }, +}; + +impl OverlapNotifyHandler for State { + fn overlap_notify_state(&mut self) -> &mut OverlapNotifyState { + &mut self.common.overlap_notify_state + } + + fn layer_surface_from_resource(&self, resource: ZwlrLayerSurfaceV1) -> Option { + self.common + .layer_shell_state + .layer_surfaces() + .find(|l| l.shell_surface() == &resource) + .and_then(|l| { + let shell = self.common.shell.read().unwrap(); + let outputs = shell.outputs(); + let ret = outputs.map(|o| layer_map_for_output(o)).find_map(|s| { + s.layer_for_surface(l.wl_surface(), WindowSurfaceType::ALL) + .cloned() + }); + drop(shell); + ret + }) + } + + fn outputs(&self) -> impl Iterator { + let shell = self.common.shell.read().unwrap(); + shell.outputs().cloned().collect::>().into_iter() + } +} + +delegate_overlap_notify!(State); diff --git a/src/wayland/protocols/overlap_notify.rs b/src/wayland/protocols/overlap_notify.rs index 2a79c2687..4298b6efa 100644 --- a/src/wayland/protocols/overlap_notify.rs +++ b/src/wayland/protocols/overlap_notify.rs @@ -12,7 +12,6 @@ use cosmic_protocols::{ zcosmic_toplevel_info_v1::ZcosmicToplevelInfoV1, }, }; -use rand::distributions::{Alphanumeric, DistString}; use smithay::{ desktop::{layer_map_for_output, LayerSurface}, output::Output, @@ -29,7 +28,7 @@ use smithay::{ shell::wlr_layer::{ExclusiveZone, Layer}, }, }; -use wayland_backend::server::GlobalId; +use wayland_backend::server::{GlobalId, ObjectId}; use crate::utils::prelude::{RectExt, RectGlobalExt, RectLocalExt}; @@ -37,6 +36,7 @@ use super::toplevel_info::{ ToplevelHandleState, ToplevelInfoGlobalData, ToplevelInfoHandler, ToplevelState, Window, }; +#[derive(Debug)] pub struct OverlapNotifyState { instances: Vec, global: GlobalId, @@ -83,7 +83,7 @@ impl OverlapNotifyState { W: Window + 'static, { for output in state.outputs() { - let map = layer_map_for_output(output); + let map = layer_map_for_output(&output); for layer_surface in map.layers() { if let Some(data) = layer_surface .user_data() @@ -94,14 +94,18 @@ impl OverlapNotifyState { if inner.has_active_notifications() { let mut new_snapshot = OverlapSnapshot::default(); - let layer_geo = layer_surface.bbox().as_local().to_global(output); + let layer_geo = map + .layer_geometry(layer_surface) + .unwrap_or_default() + .as_local() + .to_global(&output); for window in state.toplevel_info_state().registered_toplevels() { if let Some(window_geo) = window.global_geometry() { if let Some(intersection) = layer_geo.intersection(window_geo) { - // relative to window location + // relative to layer location let region = Rectangle::from_loc_and_size( - intersection.loc - window_geo.loc, + intersection.loc - layer_geo.loc, intersection.size, ) .as_logical(); @@ -111,11 +115,18 @@ impl OverlapNotifyState { } for other_surface in map.layers().filter(|s| *s != layer_surface) { - let other_geo = other_surface.bbox().as_local().to_global(output); + if other_surface.wl_surface().id() == layer_surface.wl_surface().id() { + continue; + } + let other_geo = map + .layer_geometry(other_surface) + .unwrap_or_default() + .as_local() + .to_global(&output); if let Some(intersection) = layer_geo.intersection(other_geo) { - // relative to window location + // relative to layer location let region = Rectangle::from_loc_and_size( - intersection.loc - other_geo.loc, + intersection.loc - layer_geo.loc, intersection.size, ) .as_logical(); @@ -134,7 +145,7 @@ impl OverlapNotifyState { pub trait OverlapNotifyHandler: ToplevelInfoHandler { fn overlap_notify_state(&mut self) -> &mut OverlapNotifyState; fn layer_surface_from_resource(&self, resource: ZwlrLayerSurfaceV1) -> Option; - fn outputs(&self) -> impl Iterator; + fn outputs(&self) -> impl Iterator; } pub struct OverlapNotifyGlobalData { @@ -156,20 +167,26 @@ impl LayerOverlapNotificationDataInternal { } pub fn add_notification(&mut self, new_notification: ZcosmicOverlapNotificationV1) { - for (toplevel, overlap) in &self.last_snapshot.toplevel_overlaps { - if let Ok(toplevel) = toplevel.upgrade() { - new_notification.toplevel_enter( - &toplevel, - overlap.loc.x, - overlap.loc.y, - overlap.size.w, - overlap.size.h, - ); + if let Some(client) = new_notification.client() { + for (toplevel, overlap) in &self.last_snapshot.toplevel_overlaps { + if let Some(toplevel) = toplevel + .upgrade() + .ok() + .filter(|handle| handle.client().is_some_and(|c| c == client)) + { + new_notification.toplevel_enter( + &toplevel, + overlap.loc.x, + overlap.loc.y, + overlap.size.w, + overlap.size.h, + ); + } } } - for (layer_surface, (exclusive, layer, overlap)) in &self.last_snapshot.layer_overlaps { + for (_, (identifier, exclusive, layer, overlap)) in &self.last_snapshot.layer_overlaps { new_notification.layer_enter( - layer_surface.clone(), + identifier.clone(), if *exclusive { 1 } else { 0 }, match layer { Layer::Background => WlrLayer::Background, @@ -196,44 +213,61 @@ impl LayerOverlapNotificationDataInternal { for toplevel in self.last_snapshot.toplevel_overlaps.keys() { if !new_snapshot.toplevel_overlaps.contains_key(toplevel) { if let Ok(toplevel) = toplevel.upgrade() { - for notification in ¬ifications { - notification.toplevel_leave(&toplevel); + if let Some(client) = toplevel.client() { + for notification in notifications + .iter() + .filter(|n| n.client().is_some_and(|c| c == client)) + { + notification.toplevel_leave(&toplevel); + } } } } } for (toplevel, overlap) in &new_snapshot.toplevel_overlaps { - if !self.last_snapshot.toplevel_overlaps.contains_key(toplevel) { + if !self + .last_snapshot + .toplevel_overlaps + .get(toplevel) + .is_some_and(|old_overlap| old_overlap == overlap) + { if let Ok(toplevel) = toplevel.upgrade() { - for notification in ¬ifications { - notification.toplevel_enter( - &toplevel, - overlap.loc.x, - overlap.loc.y, - overlap.size.w, - overlap.size.h, - ); + if let Some(client) = toplevel.client() { + for notification in notifications + .iter() + .filter(|n| n.client().is_some_and(|c| c == client)) + { + notification.toplevel_enter( + &toplevel, + overlap.loc.x, + overlap.loc.y, + overlap.size.w, + overlap.size.h, + ); + } } } } } - for layer_surface in self.last_snapshot.layer_overlaps.keys() { - if new_snapshot.layer_overlaps.contains_key(layer_surface) { + for (layer_surface, (identifier, ..)) in &self.last_snapshot.layer_overlaps { + if !new_snapshot.layer_overlaps.contains_key(layer_surface) { for notification in ¬ifications { - notification.layer_leave(layer_surface.clone()); + notification.layer_leave(identifier.clone()); } } } - for (layer_surface, (exclusive, layer, overlap)) in &new_snapshot.layer_overlaps { + for (layer_surface, (identifier, exclusive, layer, overlap)) in &new_snapshot.layer_overlaps + { if !self .last_snapshot .layer_overlaps - .contains_key(layer_surface) + .get(layer_surface) + .is_some_and(|(_, _, _, old_overlap)| old_overlap == overlap) { for notification in ¬ifications { notification.layer_enter( - layer_surface.clone(), + identifier.clone(), if *exclusive { 1 } else { 0 }, match layer { Layer::Background => WlrLayer::Background, @@ -257,7 +291,7 @@ impl LayerOverlapNotificationDataInternal { #[derive(Debug, Default, Clone)] struct OverlapSnapshot { toplevel_overlaps: HashMap, Rectangle>, - layer_overlaps: HashMap)>, + layer_overlaps: HashMap)>, } impl OverlapSnapshot { @@ -283,18 +317,21 @@ impl OverlapSnapshot { ExclusiveZone::Exclusive(_) ); let layer = layer_surface.layer(); - let identifier = layer_surface.user_data().get_or_insert(Identifier::default); + let id = layer_surface.wl_surface().id(); + let identifier = layer_surface + .user_data() + .get_or_insert(|| Identifier::from(id.clone())); self.layer_overlaps - .insert(identifier.0.clone(), (exclusive, layer, overlap)); + .insert(id, (identifier.0.clone(), exclusive, layer, overlap)); } } struct Identifier(String); -impl Default for Identifier { - fn default() -> Self { - Identifier(Alphanumeric.sample_string(&mut rand::thread_rng(), 32)) +impl From for Identifier { + fn from(value: ObjectId) -> Self { + Identifier(value.to_string()) } } @@ -392,3 +429,18 @@ where } } } + +macro_rules! delegate_overlap_notify { + ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => { + smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ + cosmic_protocols::overlap_notify::v1::server::zcosmic_overlap_notify_v1::ZcosmicOverlapNotifyV1: $crate::wayland::protocols::overlap_notify::OverlapNotifyGlobalData + ] => $crate::wayland::protocols::overlap_notify::OverlapNotifyState); + smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ + cosmic_protocols::overlap_notify::v1::server::zcosmic_overlap_notify_v1::ZcosmicOverlapNotifyV1: () + ] => $crate::wayland::protocols::overlap_notify::OverlapNotifyState); + smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ + cosmic_protocols::overlap_notify::v1::server::zcosmic_overlap_notification_v1::ZcosmicOverlapNotificationV1: () + ] => $crate::wayland::protocols::overlap_notify::OverlapNotifyState); + }; +} +pub(crate) use delegate_overlap_notify;