diff --git a/core/src/event/wayland/mod.rs b/core/src/event/wayland/mod.rs index dd63fdc817..5fabc86005 100644 --- a/core/src/event/wayland/mod.rs +++ b/core/src/event/wayland/mod.rs @@ -1,5 +1,6 @@ mod layer; mod output; +mod overlap_notify; mod popup; mod seat; mod session_lock; @@ -12,6 +13,7 @@ use cctk::sctk::reexports::client::protocol::{ pub use layer::*; pub use output::*; +pub use overlap_notify::*; pub use popup::*; pub use seat::*; pub use session_lock::*; @@ -26,6 +28,8 @@ pub enum Event { Popup(PopupEvent, WlSurface, Id), /// output event Output(OutputEvent, WlOutput), + /// Overlap notify event + OverlapNotify(overlap_notify::OverlapNotifyEvent), /// window event Window(WindowEvent), /// Seat Event diff --git a/core/src/event/wayland/overlap_notify.rs b/core/src/event/wayland/overlap_notify.rs new file mode 100644 index 0000000000..5d59c29a95 --- /dev/null +++ b/core/src/event/wayland/overlap_notify.rs @@ -0,0 +1,21 @@ +use cctk::{sctk::shell::wlr_layer::Layer, wayland_protocols::ext::foreign_toplevel_list::v1::client::ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1}; + +#[derive(Debug, Clone, PartialEq)] +pub enum OverlapNotifyEvent { + OverlapToplevelAdd { + toplevel: ExtForeignToplevelHandleV1, + logical_rect: crate::Rectangle, + }, + OverlapToplevelRemove { + toplevel: ExtForeignToplevelHandleV1, + }, + OverlapLayerAdd { + identifier: String, + exclusive: u32, + layer: Option, + logical_rect: crate::Rectangle, + }, + OverlapLayerRemove { + identifier: String, + }, +} diff --git a/runtime/src/platform_specific/wayland/mod.rs b/runtime/src/platform_specific/wayland/mod.rs index c370d38592..41b21c527e 100644 --- a/runtime/src/platform_specific/wayland/mod.rs +++ b/runtime/src/platform_specific/wayland/mod.rs @@ -2,6 +2,8 @@ use std::fmt::Debug; +use iced_core::window::Id; + /// activation Actions pub mod activation; @@ -22,21 +24,26 @@ pub enum Action { Activation(activation::Action), /// session lock SessionLock(session_lock::Action), + /// Overlap Notify + OverlapNotify(Id, bool), } impl Debug for Action { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::LayerSurface(arg0) => { + Action::LayerSurface(arg0) => { f.debug_tuple("LayerSurface").field(arg0).finish() } - Self::Popup(arg0) => f.debug_tuple("Popup").field(arg0).finish(), - Self::Activation(arg0) => { + Action::Popup(arg0) => f.debug_tuple("Popup").field(arg0).finish(), + Action::Activation(arg0) => { f.debug_tuple("Activation").field(arg0).finish() } - Self::SessionLock(arg0) => { + Action::SessionLock(arg0) => { f.debug_tuple("SessionLock").field(arg0).finish() } + Action::OverlapNotify(id, _) => { + f.debug_tuple("OverlapNotify").field(id).finish() + } } } } diff --git a/winit/src/platform_specific/wayland/commands/mod.rs b/winit/src/platform_specific/wayland/commands/mod.rs index d097333d09..85ba1000f6 100644 --- a/winit/src/platform_specific/wayland/commands/mod.rs +++ b/winit/src/platform_specific/wayland/commands/mod.rs @@ -2,5 +2,6 @@ pub mod activation; pub mod layer_surface; +pub mod overlap_notify; pub mod popup; pub mod session_lock; diff --git a/winit/src/platform_specific/wayland/commands/overlap_notify.rs b/winit/src/platform_specific/wayland/commands/overlap_notify.rs new file mode 100644 index 0000000000..860838c31b --- /dev/null +++ b/winit/src/platform_specific/wayland/commands/overlap_notify.rs @@ -0,0 +1,14 @@ +use iced_futures::core::window::Id; +use iced_runtime::{ + platform_specific::{self, wayland}, + task, Action, Task, +}; + +/// Request subscription for overlap notification events on the surface +pub fn overlap_notify(id: Id, enable: bool) -> Task { + task::effect(Action::PlatformSpecific( + platform_specific::Action::Wayland(wayland::Action::OverlapNotify( + id, enable, + )), + )) +} diff --git a/winit/src/platform_specific/wayland/event_loop/mod.rs b/winit/src/platform_specific/wayland/event_loop/mod.rs index ecd230ebdc..eb2b148265 100644 --- a/winit/src/platform_specific/wayland/event_loop/mod.rs +++ b/winit/src/platform_specific/wayland/event_loop/mod.rs @@ -6,6 +6,7 @@ pub mod state; use crate::platform_specific::SurfaceIdWrapper; use crate::{ futures::futures::channel::mpsc, + handlers::overlap::OverlapNotifyV1, platform_specific::wayland::{ handlers::{ wp_fractional_scaling::FractionalScalingManager, @@ -17,7 +18,6 @@ use crate::{ subsurface_widget::SubsurfaceState, }; -use raw_window_handle::HasDisplayHandle; use cctk::sctk::reexports::calloop_wayland_source::WaylandSource; use cctk::sctk::{ activation::ActivationState, @@ -36,6 +36,7 @@ use cctk::sctk::{ shell::{wlr_layer::LayerShell, xdg::XdgShell, WaylandSurface}, shm::Shm, }; +use raw_window_handle::HasDisplayHandle; use state::{FrameStatus, SctkWindow}; #[cfg(feature = "a11y")] use std::sync::{Arc, Mutex}; @@ -201,6 +202,7 @@ impl SctkEventLoop { activation_state: ActivationState::bind(&globals, &qh).ok(), session_lock_state: SessionLockState::new(&globals, &qh), session_lock: None, + overlap_notify: OverlapNotifyV1::bind(&globals, &qh).ok(), queue_handle: qh, loop_handle, @@ -228,6 +230,7 @@ impl SctkEventLoop { pending_popup: Default::default(), activation_token_ctr: 0, token_senders: HashMap::new(), + overlap_notifications: HashMap::new(), }, _features: Default::default(), }; diff --git a/winit/src/platform_specific/wayland/event_loop/state.rs b/winit/src/platform_specific/wayland/event_loop/state.rs index 1dea4f5ad7..932757b0fa 100644 --- a/winit/src/platform_specific/wayland/event_loop/state.rs +++ b/winit/src/platform_specific/wayland/event_loop/state.rs @@ -1,5 +1,8 @@ use crate::{ - handlers::activation::IcedRequestData, + handlers::{ + activation::IcedRequestData, + overlap::{OverlapNotificationV1, OverlapNotifyV1}, + }, platform_specific::{ wayland::{ handlers::{ @@ -27,22 +30,11 @@ use winit::{ platform::wayland::WindowExtWayland, }; -use iced_runtime::{ - core::{self, touch, Point}, - keyboard::Modifiers, - platform_specific::{ - self, - wayland::{ - layer_surface::{IcedMargin, IcedOutput, SctkLayerSurfaceSettings}, - popup::SctkPopupSettings, - Action, - }, - }, -}; -use cctk::sctk::{ +use cctk::{cosmic_protocols::overlap_notify::v1::client::zcosmic_overlap_notification_v1::ZcosmicOverlapNotificationV1, sctk::{ activation::{ActivationState, RequestData}, compositor::CompositorState, error::GlobalError, + globals::GlobalData, output::OutputState, reexports::{ calloop::{timer::TimeoutAction, LoopHandle}, @@ -74,7 +66,7 @@ use cctk::sctk::{ shell::{ wlr_layer::{ Anchor, KeyboardInteractivity, Layer, LayerShell, LayerSurface, - LayerSurfaceConfigure, + LayerSurfaceConfigure, SurfaceKind, }, xdg::{ popup::{Popup, PopupConfigure}, @@ -83,6 +75,18 @@ use cctk::sctk::{ WaylandSurface, }, shm::{multi::MultiPool, Shm}, +}}; +use iced_runtime::{ + core::{self, touch, Point}, + keyboard::Modifiers, + platform_specific::{ + self, + wayland::{ + layer_surface::{IcedMargin, IcedOutput, SctkLayerSurfaceSettings}, + popup::SctkPopupSettings, + Action, + }, + }, }; use wayland_protocols::{ wp::{ @@ -326,7 +330,11 @@ pub struct SctkState { /// a memory pool pub(crate) _multipool: Option>, - // all present outputs + /// all notification objects + pub(crate) overlap_notifications: + HashMap, + + /// all present outputs pub(crate) outputs: Vec, // though (for now) only one seat will be active in an iced application at a time, all ought to be tracked // Active seat is the first seat in the list @@ -379,6 +387,7 @@ pub struct SctkState { pub(crate) to_commit: HashMap, pub(crate) destroyed: HashSet, pub(crate) pending_popup: Option<(SctkPopupSettings, usize)>, + pub(crate) overlap_notify: Option, pub(crate) activation_token_ctr: u32, pub(crate) token_senders: HashMap>>, @@ -1176,6 +1185,28 @@ impl SctkState { } } } + Action::OverlapNotify(id, enabled) => { + if let Some(layer_surface) = self.layer_surfaces.iter_mut().find(|l| l.id == id) { + let Some(overlap_notify_state) = self.overlap_notify.as_ref() else { + tracing::error!("Overlap notify is not supported."); + return Ok(()); + }; + let my_id = layer_surface.surface.wl_surface().id(); + + if enabled && !self.overlap_notifications.contains_key(&my_id) { + let SurfaceKind::Wlr(wlr) = &layer_surface.surface.kind() else { + tracing::error!("Overlap notify is not supported for non wlr surface."); + return Ok(()); + }; + let notification = overlap_notify_state.notify.notify_on_overlap(wlr, &self.queue_handle, GlobalData); + _ = self.overlap_notifications.insert(my_id, notification); + } else { + _ = self.overlap_notifications.remove(&my_id); + } + } else { + tracing::error!("Overlap notify subscription cannot be created for surface. No matching layer surface found."); + } + }, }; Ok(()) } diff --git a/winit/src/platform_specific/wayland/handlers/mod.rs b/winit/src/platform_specific/wayland/handlers/mod.rs index 28264f1fd3..0079299f98 100644 --- a/winit/src/platform_specific/wayland/handlers/mod.rs +++ b/winit/src/platform_specific/wayland/handlers/mod.rs @@ -2,11 +2,12 @@ pub mod activation; pub mod compositor; pub mod output; -// pub mod overlap; +pub mod overlap; pub mod seat; pub mod session_lock; pub mod shell; pub mod subcompositor; +pub mod toplevel; pub mod wp_fractional_scaling; pub mod wp_viewporter; diff --git a/winit/src/platform_specific/wayland/handlers/overlap.rs b/winit/src/platform_specific/wayland/handlers/overlap.rs new file mode 100644 index 0000000000..bb1660a874 --- /dev/null +++ b/winit/src/platform_specific/wayland/handlers/overlap.rs @@ -0,0 +1,122 @@ +use cctk::{ + cosmic_protocols::overlap_notify::v1::client::{ + zcosmic_overlap_notification_v1::{self, ZcosmicOverlapNotificationV1}, + zcosmic_overlap_notify_v1::ZcosmicOverlapNotifyV1, + }, sctk::shell::wlr_layer::Layer, wayland_client::{ + self, event_created_child, + globals::{BindError, GlobalList}, + protocol::wl_surface::WlSurface, + Connection, Dispatch, Proxy, QueueHandle, + }, wayland_protocols::ext::foreign_toplevel_list::v1::client::ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1 +}; +use cctk::sctk::globals::GlobalData; +use iced_futures::core::Rectangle; + +use crate::{event_loop::state::SctkState, sctk_event::SctkEvent}; + +#[derive(Debug, Clone)] +pub struct OverlapNotifyV1 { + pub(crate) notify: ZcosmicOverlapNotifyV1, +} + +impl OverlapNotifyV1 { + pub fn bind( + globals: &GlobalList, + qh: &QueueHandle, + ) -> Result { + let notify = globals.bind(qh, 1..=1, GlobalData)?; + Ok(OverlapNotifyV1 { notify }) + } +} + +impl Dispatch + for OverlapNotifyV1 +{ + fn event( + _: &mut SctkState, + _: &ZcosmicOverlapNotifyV1, + _: ::Event, + _: &GlobalData, + _: &Connection, + _: &QueueHandle, + ) { + } +} + +pub struct OverlapNotificationV1 { + surface: WlSurface, +} + +impl Dispatch + for OverlapNotificationV1 +{ + fn event( + state: &mut SctkState, + n: &ZcosmicOverlapNotificationV1, + event: ::Event, + _: &GlobalData, + _: &Connection, + _: &QueueHandle, + ) { + let data: Option<&OverlapNotificationV1> = n.data(); + let Some(data) = data else { + return; + }; + + let surface = data.surface.clone(); + + state.sctk_events.push(match event { + zcosmic_overlap_notification_v1::Event::ToplevelEnter { + toplevel, + x, + y, + width, + height, + } => SctkEvent::OverlapToplevelAdd { + surface, + toplevel, + logical_rect: Rectangle::new( + (x as f32, y as f32).into(), + (width as f32, height as f32).into(), + ), + }, + zcosmic_overlap_notification_v1::Event::ToplevelLeave { + toplevel, + } => { + SctkEvent::OverlapToplevelRemove { surface, toplevel } + } + zcosmic_overlap_notification_v1::Event::LayerEnter { + identifier, + exclusive, + layer, + x, + y, + width, + height, + } => SctkEvent::OverlapLayerAdd { surface, identifier, exclusive, layer: match layer { + wayland_client::WEnum::Value(v) => match v { + cctk::sctk::reexports::protocols_wlr::layer_shell::v1::client::zwlr_layer_shell_v1::Layer::Background => Some(Layer::Background), + cctk::sctk::reexports::protocols_wlr::layer_shell::v1::client::zwlr_layer_shell_v1::Layer::Bottom => Some(Layer::Bottom), + cctk::sctk::reexports::protocols_wlr::layer_shell::v1::client::zwlr_layer_shell_v1::Layer::Top => Some(Layer::Top), + cctk::sctk::reexports::protocols_wlr::layer_shell::v1::client::zwlr_layer_shell_v1::Layer::Overlay => Some(Layer::Overlay), + _ => Default::default(), + }, + wayland_client::WEnum::Unknown(_) => Default::default(), + }, logical_rect: Rectangle::new( + (x as f32, y as f32).into(), + (width as f32, height as f32).into(), + ), }, + zcosmic_overlap_notification_v1::Event::LayerLeave { + identifier, + } => SctkEvent::OverlapLayerRemove { identifier, surface }, + _ => unimplemented!(), + }); + } + + event_created_child!(SctkState, ZcosmicOverlapNotifyV1, [ + 0 => (ExtForeignToplevelHandleV1, Default::default()) + ]); +} + +wayland_client::delegate_dispatch!(SctkState: [ZcosmicOverlapNotifyV1: GlobalData] => OverlapNotifyV1); +wayland_client::delegate_dispatch!(SctkState: [ZcosmicOverlapNotificationV1: GlobalData] => OverlapNotificationV1); diff --git a/winit/src/platform_specific/wayland/handlers/toplevel.rs b/winit/src/platform_specific/wayland/handlers/toplevel.rs new file mode 100644 index 0000000000..2632a86b88 --- /dev/null +++ b/winit/src/platform_specific/wayland/handlers/toplevel.rs @@ -0,0 +1,67 @@ +use cctk::{ + cosmic_protocols::{ + toplevel_info::v1::client::zcosmic_toplevel_handle_v1, + toplevel_management::v1::client::zcosmic_toplevel_manager_v1, + }, + toplevel_info::{ToplevelInfoHandler, ToplevelInfoState}, + toplevel_management::ToplevelManagerHandler, + wayland_client::{self, WEnum}, +}; +use wayland_client::{Connection, QueueHandle}; + +use crate::event_loop::state::SctkState; + +impl ToplevelManagerHandler for SctkState { + fn toplevel_manager_state( + &mut self, + ) -> &mut cctk::toplevel_management::ToplevelManagerState { + todo!() + } + + fn capabilities( + &mut self, + _conn: &Connection, + _: &QueueHandle, + _capabilities: Vec< + WEnum, + >, + ) { + todo!() + } +} + +impl ToplevelInfoHandler for SctkState { + fn toplevel_info_state(&mut self) -> &mut ToplevelInfoState { + todo!() + } + + fn new_toplevel( + &mut self, + _conn: &Connection, + _qh: &QueueHandle, + _toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1, + ) { + todo!() + } + + fn update_toplevel( + &mut self, + _conn: &Connection, + _qh: &QueueHandle, + _toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1, + ) { + todo!() + } + + fn toplevel_closed( + &mut self, + _conn: &Connection, + _qh: &QueueHandle, + _toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1, + ) { + todo!() + } +} + +cctk::delegate_toplevel_info!(SctkState); +cctk::delegate_toplevel_manager!(SctkState); diff --git a/winit/src/platform_specific/wayland/sctk_event.rs b/winit/src/platform_specific/wayland/sctk_event.rs index a1c760f6b4..872d3d8c1b 100755 --- a/winit/src/platform_specific/wayland/sctk_event.rs +++ b/winit/src/platform_specific/wayland/sctk_event.rs @@ -18,11 +18,14 @@ use dnd::DndSurface; use iced_futures::{ core::{ event::{ - wayland::{LayerEvent, PopupEvent, SessionLockEvent}, + wayland::{ + LayerEvent, OverlapNotifyEvent, PopupEvent, SessionLockEvent, + }, PlatformSpecific, }, Clipboard as _, }, + event, futures::channel::mpsc, }; use iced_graphics::Compositor; @@ -37,30 +40,33 @@ use iced_runtime::{ user_interface, Debug, }; -use cctk::sctk::{ - output::OutputInfo, - reexports::{ - calloop::channel, - client::{ - backend::ObjectId, - protocol::{ - wl_display::WlDisplay, wl_keyboard::WlKeyboard, - wl_output::WlOutput, wl_pointer::WlPointer, wl_seat::WlSeat, - wl_surface::WlSurface, wl_touch::WlTouch, +use cctk::{ + cosmic_protocols::overlap_notify::v1::client::zcosmic_overlap_notification_v1, + sctk::{ + output::OutputInfo, + reexports::{ + calloop::channel, + client::{ + backend::ObjectId, + protocol::{ + wl_display::WlDisplay, wl_keyboard::WlKeyboard, + wl_output::WlOutput, wl_pointer::WlPointer, + wl_seat::WlSeat, wl_surface::WlSurface, wl_touch::WlTouch, + }, + Proxy, QueueHandle, }, - Proxy, QueueHandle, + csd_frame::WindowManagerCapabilities, + }, + seat::{ + keyboard::{KeyEvent, Modifiers}, + pointer::{PointerEvent, PointerEventKind}, + Capability, + }, + session_lock::SessionLockSurfaceConfigure, + shell::{ + wlr_layer::{Layer, LayerSurfaceConfigure}, + xdg::{popup::PopupConfigure, window::WindowConfigure}, }, - csd_frame::WindowManagerCapabilities, - }, - seat::{ - keyboard::{KeyEvent, Modifiers}, - pointer::{PointerEvent, PointerEventKind}, - Capability, - }, - session_lock::SessionLockSurfaceConfigure, - shell::{ - wlr_layer::LayerSurfaceConfigure, - xdg::{popup::PopupConfigure, window::WindowConfigure}, }, }; use std::{ @@ -68,7 +74,10 @@ use std::{ num::NonZeroU32, sync::{Arc, Mutex}, }; -use wayland_protocols::wp::viewporter::client::wp_viewport::WpViewport; +use wayland_protocols::{ + ext::foreign_toplevel_list::v1::client::ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1, + wp::viewporter::client::wp_viewport::WpViewport, +}; use winit::{ dpi::PhysicalSize, event::WindowEvent, event_loop::EventLoopProxy, window::WindowId, @@ -120,6 +129,26 @@ pub enum SctkEvent { variant: LayerSurfaceEventVariant, id: WlSurface, }, + OverlapToplevelAdd { + surface: WlSurface, + toplevel: ExtForeignToplevelHandleV1, + logical_rect: iced_runtime::core::Rectangle, + }, + OverlapToplevelRemove { + surface: WlSurface, + toplevel: ExtForeignToplevelHandleV1, + }, + OverlapLayerAdd { + surface: WlSurface, + identifier: String, + exclusive: u32, + layer: Option, + logical_rect: iced_runtime::core::Rectangle, + }, + OverlapLayerRemove { + surface: WlSurface, + identifier: String, + }, PopupEvent { variant: PopupEventVariant, /// this may be the Id of a window or layer surface @@ -1183,6 +1212,87 @@ impl SctkEvent { SctkEvent::Subcompositor(s) => { *subsurface_state = Some(s); } + SctkEvent::OverlapToplevelAdd { + surface, + toplevel, + logical_rect, + } => { + if let Some(id) = surface_ids.get(&surface.id()) { + events.push(( + Some(id.inner()), + iced_runtime::core::Event::PlatformSpecific( + PlatformSpecific::Wayland( + wayland::Event::OverlapNotify( + OverlapNotifyEvent::OverlapToplevelAdd { + toplevel, + logical_rect, + }, + ), + ), + ), + )) + } + } + SctkEvent::OverlapToplevelRemove { surface, toplevel } => { + if let Some(id) = surface_ids.get(&surface.id()) { + events.push(( + Some(id.inner()), + iced_runtime::core::Event::PlatformSpecific( + PlatformSpecific::Wayland( + wayland::Event::OverlapNotify( + OverlapNotifyEvent::OverlapToplevelRemove { + toplevel, + }, + ), + ), + ), + )) + } + } + SctkEvent::OverlapLayerAdd { + surface, + identifier, + exclusive, + layer, + logical_rect, + } => { + if let Some(id) = surface_ids.get(&surface.id()) { + events.push(( + Some(id.inner()), + iced_runtime::core::Event::PlatformSpecific( + PlatformSpecific::Wayland( + wayland::Event::OverlapNotify( + OverlapNotifyEvent::OverlapLayerAdd { + identifier, + exclusive, + layer, + logical_rect, + }, + ), + ), + ), + )) + } + } + SctkEvent::OverlapLayerRemove { + surface, + identifier, + } => { + if let Some(id) = surface_ids.get(&surface.id()) { + events.push(( + Some(id.inner()), + iced_runtime::core::Event::PlatformSpecific( + PlatformSpecific::Wayland( + wayland::Event::OverlapNotify( + OverlapNotifyEvent::OverlapLayerRemove { + identifier, + }, + ), + ), + ), + )) + } + } } } }