feat: overlap notify

This commit is contained in:
Ashley Wulber 2024-11-26 16:09:31 -05:00
parent 6e91eabf4c
commit 301892aaa5
No known key found for this signature in database
GPG key ID: 5216D4F46A90A820
11 changed files with 441 additions and 51 deletions

View file

@ -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

View file

@ -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<Layer>,
logical_rect: crate::Rectangle,
},
OverlapLayerRemove {
identifier: String,
},
}

View file

@ -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()
}
}
}
}

View file

@ -2,5 +2,6 @@
pub mod activation;
pub mod layer_surface;
pub mod overlap_notify;
pub mod popup;
pub mod session_lock;

View file

@ -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<Message>(id: Id, enable: bool) -> Task<Message> {
task::effect(Action::PlatformSpecific(
platform_specific::Action::Wayland(wayland::Action::OverlapNotify(
id, enable,
)),
))
}

View file

@ -7,6 +7,7 @@ use crate::platform_specific::SurfaceIdWrapper;
use crate::{
Control,
futures::futures::channel::mpsc,
handlers::overlap::OverlapNotifyV1,
platform_specific::wayland::{
handlers::{
wp_fractional_scaling::FractionalScalingManager,
@ -17,25 +18,29 @@ use crate::{
subsurface_widget::SubsurfaceState,
};
use raw_window_handle::HasDisplayHandle;
use cctk::sctk::reexports::calloop_wayland_source::WaylandSource;
use cctk::sctk::{
activation::ActivationState,
compositor::CompositorState,
globals::GlobalData,
output::OutputState,
reexports::{
calloop::{self, EventLoop},
client::{
ConnectError, Connection, Proxy, globals::registry_queue_init,
use cctk::{
sctk::{
activation::ActivationState,
compositor::CompositorState,
globals::GlobalData,
output::OutputState,
reexports::{
calloop::{self, EventLoop},
client::{
ConnectError, Connection, Proxy, globals::registry_queue_init,
},
},
registry::RegistryState,
seat::SeatState,
session_lock::SessionLockState,
shell::{WaylandSurface, wlr_layer::LayerShell, xdg::XdgShell},
shm::Shm,
},
registry::RegistryState,
seat::SeatState,
session_lock::SessionLockState,
shell::{WaylandSurface, wlr_layer::LayerShell, xdg::XdgShell},
shm::Shm,
toplevel_info::ToplevelInfoState,
toplevel_management::ToplevelManagerState,
};
use raw_window_handle::HasDisplayHandle;
use state::{FrameStatus, SctkWindow};
#[cfg(feature = "a11y")]
use std::sync::{Arc, Mutex};
@ -188,7 +193,6 @@ impl SctkEventLoop {
event_loop,
state: SctkState {
connection,
registry_state,
seat_state: SeatState::new(&globals, &qh),
output_state: OutputState::new(&globals, &qh),
compositor_state: CompositorState::bind(&globals, &qh)
@ -201,6 +205,16 @@ 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(),
toplevel_info: ToplevelInfoState::try_new(
&registry_state,
&qh,
),
toplevel_manager: ToplevelManagerState::try_new(
&registry_state,
&qh,
),
registry_state,
queue_handle: qh,
loop_handle,
@ -228,6 +242,7 @@ impl SctkEventLoop {
pending_popup: Default::default(),
activation_token_ctr: 0,
token_senders: HashMap::new(),
overlap_notifications: HashMap::new(),
},
_features: Default::default(),
};

View file

@ -1,6 +1,9 @@
use crate::{
Control,
handlers::activation::IcedRequestData,
handlers::{
activation::IcedRequestData,
overlap::{OverlapNotificationV1, OverlapNotifyV1},
},
platform_specific::{
Event,
wayland::{
@ -39,10 +42,11 @@ use iced_runtime::{
},
},
};
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::{LoopHandle, timer::TimeoutAction},
@ -74,15 +78,16 @@ use cctk::sctk::{
WaylandSurface,
wlr_layer::{
Anchor, KeyboardInteractivity, Layer, LayerShell, LayerSurface,
LayerSurfaceConfigure,
LayerSurfaceConfigure, SurfaceKind,
},
xdg::{
XdgPositioner, XdgShell,
popup::{Popup, PopupConfigure},
},
},
shm::{Shm, multi::MultiPool},
};
shm::{multi::MultiPool, Shm},
}, toplevel_info::ToplevelInfoState, toplevel_management::ToplevelManagerState};
use wayland_protocols::{
wp::{
fractional_scale::v1::client::wp_fractional_scale_v1::WpFractionalScaleV1,
@ -325,7 +330,11 @@ pub struct SctkState {
/// a memory pool
pub(crate) _multipool: Option<MultiPool<WlSurface>>,
// all present outputs
/// all notification objects
pub(crate) overlap_notifications:
HashMap<ObjectId, ZcosmicOverlapNotificationV1>,
/// all present outputs
pub(crate) outputs: Vec<WlOutput>,
// 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
@ -378,6 +387,9 @@ pub struct SctkState {
pub(crate) to_commit: HashMap<core::window::Id, WlSurface>,
pub(crate) destroyed: HashSet<core::window::Id>,
pub(crate) pending_popup: Option<(SctkPopupSettings, usize)>,
pub(crate) overlap_notify: Option<OverlapNotifyV1>,
pub(crate) toplevel_info: Option<ToplevelInfoState>,
pub(crate) toplevel_manager: Option<ToplevelManagerState>,
pub(crate) activation_token_ctr: u32,
pub(crate) token_senders: HashMap<u32, oneshot::Sender<Option<String>>>,
@ -1175,6 +1187,27 @@ 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, OverlapNotificationV1 { surface: layer_surface.surface.wl_surface().clone() });
_ = 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(())
}

View file

@ -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;

View file

@ -0,0 +1,117 @@
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<SctkState>,
) -> Result<OverlapNotifyV1, BindError> {
let notify = globals.bind(qh, 1..=1, GlobalData)?;
Ok(OverlapNotifyV1 { notify })
}
}
impl Dispatch<ZcosmicOverlapNotifyV1, GlobalData, SctkState>
for OverlapNotifyV1
{
fn event(
_: &mut SctkState,
_: &ZcosmicOverlapNotifyV1,
_: <ZcosmicOverlapNotifyV1 as Proxy>::Event,
_: &GlobalData,
_: &Connection,
_: &QueueHandle<SctkState>,
) {
}
}
pub struct OverlapNotificationV1 {
pub surface: WlSurface,
}
impl Dispatch<ZcosmicOverlapNotificationV1, OverlapNotificationV1, SctkState>
for OverlapNotificationV1
{
fn event(
state: &mut SctkState,
_: &ZcosmicOverlapNotificationV1,
event: <ZcosmicOverlapNotificationV1 as Proxy>::Event,
data: &OverlapNotificationV1,
_: &Connection,
_: &QueueHandle<SctkState>,
) {
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: OverlapNotificationV1] => OverlapNotificationV1);

View file

@ -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 {
self.toplevel_manager.as_mut().unwrap()
}
fn capabilities(
&mut self,
_conn: &Connection,
_: &QueueHandle<Self>,
_capabilities: Vec<
WEnum<zcosmic_toplevel_manager_v1::ZcosmicToplelevelManagementCapabilitiesV1>,
>,
) {
// TODO
}
}
impl ToplevelInfoHandler for SctkState {
fn toplevel_info_state(&mut self) -> &mut ToplevelInfoState {
self.toplevel_info.as_mut().unwrap()
}
fn new_toplevel(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
_toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
) {
// TODO
}
fn update_toplevel(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
_toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
) {
// TODO
}
fn toplevel_closed(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
_toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
) {
// TODO
}
}
cctk::delegate_toplevel_info!(SctkState);
cctk::delegate_toplevel_manager!(SctkState);

View file

@ -20,9 +20,12 @@ use iced_futures::{
Clipboard as _,
event::{
PlatformSpecific,
wayland::{LayerEvent, PopupEvent, SessionLockEvent},
wayland::{
LayerEvent, OverlapNotifyEvent, PopupEvent, SessionLockEvent,
},
},
},
event,
futures::channel::mpsc,
};
use iced_graphics::{Compositor, compositor};
@ -37,30 +40,33 @@ use iced_runtime::{
user_interface,
};
use cctk::sctk::{
output::OutputInfo,
reexports::{
calloop::channel,
client::{
Proxy, QueueHandle,
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::{
Proxy, QueueHandle,
backend::ObjectId,
protocol::{
wl_display::WlDisplay, wl_keyboard::WlKeyboard,
wl_output::WlOutput, wl_pointer::WlPointer,
wl_seat::WlSeat, wl_surface::WlSurface, wl_touch::WlTouch,
},
},
csd_frame::WindowManagerCapabilities,
},
seat::{
Capability,
keyboard::{KeyEvent, Modifiers},
pointer::{PointerEvent, PointerEventKind},
},
session_lock::SessionLockSurfaceConfigure,
shell::{
wlr_layer::{Layer, LayerSurfaceConfigure},
xdg::{popup::PopupConfigure, window::WindowConfigure},
},
csd_frame::WindowManagerCapabilities,
},
seat::{
Capability,
keyboard::{KeyEvent, Modifiers},
pointer::{PointerEvent, PointerEventKind},
},
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<Layer>,
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
@ -1195,6 +1224,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,
},
),
),
),
))
}
}
}
}
}