From 487efafdf54edac23afcf2a98a84a0b1089c441c Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Tue, 8 Oct 2024 12:58:32 +0200 Subject: [PATCH 1/5] ipc: remove X-only code from traits --- src/ifs/ipc.rs | 7 +---- src/ifs/ipc/wl_data_device.rs | 8 ----- src/ifs/ipc/x_data_device.rs | 13 +-------- src/ifs/ipc/zwlr_data_control_device_v1.rs | 29 ------------------- .../ipc/zwp_primary_selection_device_v1.rs | 8 ----- src/xwayland/xwm.rs | 8 ++++- 6 files changed, 9 insertions(+), 64 deletions(-) diff --git a/src/ifs/ipc.rs b/src/ifs/ipc.rs index 0def2a61..35f9f8b4 100644 --- a/src/ifs/ipc.rs +++ b/src/ifs/ipc.rs @@ -6,7 +6,7 @@ use { ipc::{ x_data_device::XIpcDevice, zwlr_data_control_device_v1::ZwlrDataControlDeviceV1, }, - wl_seat::{WlSeatError, WlSeatGlobal}, + wl_seat::WlSeatGlobal, }, utils::{ bitflags::BitflagsExt, cell_ext::CellExt, clonecell::CloneCell, numcell::NumCell, @@ -152,11 +152,6 @@ pub trait IpcVtable: Sized { fn get_device_data(dd: &Self::Device) -> &DeviceData; fn get_device_seat(dd: &Self::Device) -> Rc; - fn set_seat_selection( - seat: &Rc, - source: &Rc, - serial: Option, - ) -> Result<(), WlSeatError>; fn create_offer( dd: &Rc, data: OfferData, diff --git a/src/ifs/ipc/wl_data_device.rs b/src/ifs/ipc/wl_data_device.rs index 35b935ea..98b4de3c 100644 --- a/src/ifs/ipc/wl_data_device.rs +++ b/src/ifs/ipc/wl_data_device.rs @@ -173,14 +173,6 @@ impl IpcVtable for ClipboardIpc { dd.seat.clone() } - fn set_seat_selection( - seat: &Rc, - source: &Rc, - serial: Option, - ) -> Result<(), WlSeatError> { - seat.set_wl_data_source_selection(Some(source.clone()), serial) - } - fn create_offer( device: &Rc, offer_data: OfferData, diff --git a/src/ifs/ipc/x_data_device.rs b/src/ifs/ipc/x_data_device.rs index aa03bb69..6fb48591 100644 --- a/src/ifs/ipc/x_data_device.rs +++ b/src/ifs/ipc/x_data_device.rs @@ -6,7 +6,7 @@ use { x_data_offer::XDataOffer, x_data_source::XDataSource, DeviceData, IpcLocation, IpcVtable, OfferData, Role, }, - wl_seat::{WlSeatError, WlSeatGlobal}, + wl_seat::WlSeatGlobal, }, state::State, xwayland::XWaylandEvent, @@ -77,17 +77,6 @@ impl IpcVtable for T { dd.seat.clone() } - fn set_seat_selection( - seat: &Rc, - source: &Rc, - _serial: Option, - ) -> Result<(), WlSeatError> { - match source.location { - IpcLocation::Clipboard => seat.set_selection(Some(source.clone())), - IpcLocation::PrimarySelection => seat.set_primary_selection(Some(source.clone())), - } - } - fn create_offer( dd: &Rc, data: OfferData, diff --git a/src/ifs/ipc/zwlr_data_control_device_v1.rs b/src/ifs/ipc/zwlr_data_control_device_v1.rs index cf62a678..ecd5cf2f 100644 --- a/src/ifs/ipc/zwlr_data_control_device_v1.rs +++ b/src/ifs/ipc/zwlr_data_control_device_v1.rs @@ -143,11 +143,6 @@ trait WlrIpc { fn wlr_get_device_data(dd: &ZwlrDataControlDeviceV1) -> &DeviceData; - fn wlr_set_seat_selection( - seat: &Rc, - source: &Rc, - ) -> Result<(), WlSeatError>; - fn wlr_send_selection(dd: &ZwlrDataControlDeviceV1, offer: Option<&Rc>); fn wlr_unset(seat: &Rc); @@ -161,13 +156,6 @@ impl WlrIpc for WlrClipboardIpcCore { &dd.clipboard_data } - fn wlr_set_seat_selection( - seat: &Rc, - source: &Rc, - ) -> Result<(), WlSeatError> { - seat.set_selection(Some(source.clone())) - } - fn wlr_send_selection( dd: &ZwlrDataControlDeviceV1, offer: Option<&Rc>, @@ -188,13 +176,6 @@ impl WlrIpc for WlrPrimarySelectionIpcCore { &dd.primary_selection_data } - fn wlr_set_seat_selection( - seat: &Rc, - source: &Rc, - ) -> Result<(), WlSeatError> { - seat.set_primary_selection(Some(source.clone())) - } - fn wlr_send_selection( dd: &ZwlrDataControlDeviceV1, offer: Option<&Rc>, @@ -229,16 +210,6 @@ impl IpcVtable for WlrIpcImpl { dd.seat.clone() } - fn set_seat_selection( - seat: &Rc, - source: &Rc, - serial: Option, - ) -> Result<(), WlSeatError> { - debug_assert!(serial.is_none()); - let _ = serial; - T::wlr_set_seat_selection(seat, source) - } - fn create_offer( device: &Rc, offer_data: OfferData, diff --git a/src/ifs/ipc/zwp_primary_selection_device_v1.rs b/src/ifs/ipc/zwp_primary_selection_device_v1.rs index 346bd091..63296837 100644 --- a/src/ifs/ipc/zwp_primary_selection_device_v1.rs +++ b/src/ifs/ipc/zwp_primary_selection_device_v1.rs @@ -121,14 +121,6 @@ impl IpcVtable for PrimarySelectionIpc { dd.seat.clone() } - fn set_seat_selection( - seat: &Rc, - source: &Rc, - serial: Option, - ) -> Result<(), WlSeatError> { - seat.set_zwp_primary_selection(Some(source.clone()), serial) - } - fn create_offer( device: &Rc, offer_data: OfferData, diff --git a/src/xwayland/xwm.rs b/src/xwayland/xwm.rs index 21e90132..3baf57b1 100644 --- a/src/xwayland/xwm.rs +++ b/src/xwayland/xwm.rs @@ -1749,7 +1749,13 @@ impl Wm { for target in &targets { add_data_source_mime_type::(&source, target); } - if let Err(e) = T::set_seat_selection(&seat, &source, None) { + let res = match source.location { + IpcLocation::Clipboard => seat.set_selection(Some(source.clone())), + IpcLocation::PrimarySelection => { + seat.set_primary_selection(Some(source.clone())) + } + }; + if let Err(e) = res { log::error!("Could not set selection: {}", ErrorFmt(e)); return Ok(()); } From 40f7bc2542a9f4b6ee591340eed82da41225cecd Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Tue, 8 Oct 2024 14:51:37 +0200 Subject: [PATCH 2/5] ipc: remove DynDataSource::offer_to_regular/wlr --- src/ifs/ipc.rs | 22 +++++------ src/ifs/ipc/wl_data_source.rs | 14 +------ src/ifs/ipc/x_data_source.rs | 34 +---------------- src/ifs/ipc/zwlr_data_control_manager_v1.rs | 10 +++-- src/ifs/ipc/zwlr_data_control_source_v1.rs | 37 ++----------------- .../ipc/zwp_primary_selection_source_v1.rs | 14 +------ src/ifs/wl_seat.rs | 6 +-- src/ifs/wl_seat/event_handling.rs | 4 +- 8 files changed, 31 insertions(+), 110 deletions(-) diff --git a/src/ifs/ipc.rs b/src/ifs/ipc.rs index 35f9f8b4..3bb77ed8 100644 --- a/src/ifs/ipc.rs +++ b/src/ifs/ipc.rs @@ -64,9 +64,7 @@ pub trait DataSource: DynDataSource { pub trait DynDataSource: 'static { fn source_data(&self) -> &SourceData; fn send_send(&self, mime_type: &str, fd: Rc); - fn offer_to_regular_client(self: Rc, client: &Rc); fn offer_to_x(self: Rc, dd: &Rc); - fn offer_to_wlr_device(self: Rc, dd: &Rc); fn detach_seat(&self, seat: &Rc); fn cancel_unprivileged_offers(&self); @@ -315,8 +313,8 @@ pub fn detach_seat(src: &S, seat: &Rc) { // data.client.flush(); } -fn offer_source_to_device( - src: &Rc, +fn offer_source_to_device( + src: &Rc, dd: &Rc, data: &SourceData, shared: Rc, @@ -351,31 +349,29 @@ fn offer_source_to_device( } } -fn offer_source_to_x(src: &Rc, dd: &Rc) +fn offer_source_to_x(src: Rc, dd: &Rc) where T: IpcVtable, - S: DynDataSource, { let data = src.source_data(); src.cancel_unprivileged_offers(); let shared = data.shared.get(); shared.role.set(data.role.get()); - offer_source_to_device::(src, dd, data, shared); + offer_source_to_device::(&src, dd, data, shared); } -pub fn offer_source_to_wlr_device(src: &Rc, dd: &Rc) +pub fn offer_source_to_wlr_device(src: Rc, dd: &Rc) where T: IpcVtable, - S: DynDataSource, { let data = src.source_data(); let shared = data.shared.get(); shared.role.set(data.role.get()); - offer_source_to_device::(src, dd, data, shared); + offer_source_to_device::(&src, dd, data, shared); } -fn offer_source_to_regular_client( - src: &Rc, +pub fn offer_source_to_regular_client( + src: Rc, client: &Rc, ) { let data = src.source_data(); @@ -390,7 +386,7 @@ fn offer_source_to_regular_client( let shared = data.shared.get(); shared.role.set(data.role.get()); T::for_each_device(&seat, client.id, |dd| { - offer_source_to_device::(src, dd, data, shared.clone()); + offer_source_to_device::(&src, dd, data, shared.clone()); }); } diff --git a/src/ifs/ipc/wl_data_source.rs b/src/ifs/ipc/wl_data_source.rs index a21feed0..a7d0728a 100644 --- a/src/ifs/ipc/wl_data_source.rs +++ b/src/ifs/ipc/wl_data_source.rs @@ -4,12 +4,10 @@ use { ifs::{ ipc::{ add_data_source_mime_type, break_source_loops, cancel_offers, destroy_data_source, - detach_seat, offer_source_to_regular_client, offer_source_to_wlr_device, - offer_source_to_x, + detach_seat, offer_source_to_x, wl_data_device::ClipboardIpc, wl_data_device_manager::{DND_ALL, DND_NONE}, x_data_device::{XClipboardIpc, XIpcDevice}, - zwlr_data_control_device_v1::{WlrClipboardIpc, ZwlrDataControlDeviceV1}, DataSource, DynDataOffer, DynDataSource, SharedState, SourceData, OFFER_STATE_ACCEPTED, OFFER_STATE_DROPPED, SOURCE_STATE_CANCELLED, SOURCE_STATE_DROPPED, @@ -55,16 +53,8 @@ impl DynDataSource for WlDataSource { WlDataSource::send_send(self, mime_type, fd); } - fn offer_to_regular_client(self: Rc, client: &Rc) { - offer_source_to_regular_client::(&self, client); - } - fn offer_to_x(self: Rc, dd: &Rc) { - offer_source_to_x::(&self, dd); - } - - fn offer_to_wlr_device(self: Rc, dd: &Rc) { - offer_source_to_wlr_device::(&self, dd) + offer_source_to_x::(self, dd); } fn detach_seat(&self, seat: &Rc) { diff --git a/src/ifs/ipc/x_data_source.rs b/src/ifs/ipc/x_data_source.rs index c071b592..e7c03342 100644 --- a/src/ifs/ipc/x_data_source.rs +++ b/src/ifs/ipc/x_data_source.rs @@ -1,17 +1,9 @@ use { crate::{ - client::Client, ifs::{ ipc::{ - cancel_offers, detach_seat, offer_source_to_regular_client, - offer_source_to_wlr_device, - wl_data_device::ClipboardIpc, - x_data_device::XIpcDevice, - zwlr_data_control_device_v1::{ - WlrClipboardIpc, WlrPrimarySelectionIpc, ZwlrDataControlDeviceV1, - }, - zwp_primary_selection_device_v1::PrimarySelectionIpc, - DataSource, DynDataSource, IpcLocation, SourceData, + cancel_offers, detach_seat, x_data_device::XIpcDevice, DataSource, DynDataSource, + IpcLocation, SourceData, }, wl_seat::WlSeatGlobal, }, @@ -54,17 +46,6 @@ impl DynDataSource for XDataSource { }); } - fn offer_to_regular_client(self: Rc, client: &Rc) { - match self.location { - IpcLocation::Clipboard => { - offer_source_to_regular_client::(&self, client) - } - IpcLocation::PrimarySelection => { - offer_source_to_regular_client::(&self, client) - } - } - } - fn offer_to_x(self: Rc, _dd: &Rc) { self.cancel_unprivileged_offers(); self.state.xwayland.queue.push(IpcSetSelection { @@ -74,17 +55,6 @@ impl DynDataSource for XDataSource { }); } - fn offer_to_wlr_device(self: Rc, dd: &Rc) { - match self.location { - IpcLocation::Clipboard => { - offer_source_to_wlr_device::(&self, dd) - } - IpcLocation::PrimarySelection => { - offer_source_to_wlr_device::(&self, dd) - } - } - } - fn detach_seat(&self, seat: &Rc) { detach_seat(self, seat); } diff --git a/src/ifs/ipc/zwlr_data_control_manager_v1.rs b/src/ifs/ipc/zwlr_data_control_manager_v1.rs index ad5ce34f..4959548e 100644 --- a/src/ifs/ipc/zwlr_data_control_manager_v1.rs +++ b/src/ifs/ipc/zwlr_data_control_manager_v1.rs @@ -3,7 +3,11 @@ use { client::{Client, ClientCaps, ClientError, CAP_DATA_CONTROL_MANAGER}, globals::{Global, GlobalName}, ifs::ipc::{ - zwlr_data_control_device_v1::{ZwlrDataControlDeviceV1, PRIMARY_SELECTION_SINCE}, + offer_source_to_wlr_device, + zwlr_data_control_device_v1::{ + WlrClipboardIpc, WlrPrimarySelectionIpc, ZwlrDataControlDeviceV1, + PRIMARY_SELECTION_SINCE, + }, zwlr_data_control_source_v1::ZwlrDataControlSourceV1, }, leaks::Tracker, @@ -78,12 +82,12 @@ impl ZwlrDataControlManagerV1RequestHandler for ZwlrDataControlManagerV1 { seat.global.add_wlr_device(&dev); self.client.add_client_obj(&dev)?; match seat.global.get_selection() { - Some(s) => s.offer_to_wlr_device(&dev), + Some(s) => offer_source_to_wlr_device::(s, &dev), _ => dev.send_selection(None), } if self.version >= PRIMARY_SELECTION_SINCE { match seat.global.get_primary_selection() { - Some(s) => s.offer_to_wlr_device(&dev), + Some(s) => offer_source_to_wlr_device::(s, &dev), _ => dev.send_primary_selection(None), } } diff --git a/src/ifs/ipc/zwlr_data_control_source_v1.rs b/src/ifs/ipc/zwlr_data_control_source_v1.rs index 16fadf5e..0cfef704 100644 --- a/src/ifs/ipc/zwlr_data_control_source_v1.rs +++ b/src/ifs/ipc/zwlr_data_control_source_v1.rs @@ -4,14 +4,9 @@ use { ifs::{ ipc::{ add_data_source_mime_type, break_source_loops, cancel_offers, destroy_data_source, - detach_seat, offer_source_to_regular_client, offer_source_to_wlr_device, - offer_source_to_x, - wl_data_device::ClipboardIpc, + detach_seat, offer_source_to_x, x_data_device::{XClipboardIpc, XIpcDevice, XPrimarySelectionIpc}, - zwlr_data_control_device_v1::{ - WlrClipboardIpc, WlrPrimarySelectionIpc, ZwlrDataControlDeviceV1, - }, - zwp_primary_selection_device_v1::PrimarySelectionIpc, + zwlr_data_control_device_v1::{WlrClipboardIpc, WlrPrimarySelectionIpc}, DataSource, DynDataSource, IpcLocation, SourceData, }, wl_seat::WlSeatGlobal, @@ -49,34 +44,10 @@ impl DynDataSource for ZwlrDataControlSourceV1 { ZwlrDataControlSourceV1::send_send(&self, mime_type, fd); } - fn offer_to_regular_client(self: Rc, client: &Rc) { - match self.location.get() { - IpcLocation::Clipboard => { - offer_source_to_regular_client::(&self, client) - } - IpcLocation::PrimarySelection => { - offer_source_to_regular_client::(&self, client) - } - } - } - fn offer_to_x(self: Rc, dd: &Rc) { match self.location.get() { - IpcLocation::Clipboard => offer_source_to_x::(&self, dd), - IpcLocation::PrimarySelection => { - offer_source_to_x::(&self, dd) - } - } - } - - fn offer_to_wlr_device(self: Rc, dd: &Rc) { - match self.location.get() { - IpcLocation::Clipboard => { - offer_source_to_wlr_device::(&self, dd) - } - IpcLocation::PrimarySelection => { - offer_source_to_wlr_device::(&self, dd) - } + IpcLocation::Clipboard => offer_source_to_x::(self, dd), + IpcLocation::PrimarySelection => offer_source_to_x::(self, dd), } } diff --git a/src/ifs/ipc/zwp_primary_selection_source_v1.rs b/src/ifs/ipc/zwp_primary_selection_source_v1.rs index 9cf1f735..27414003 100644 --- a/src/ifs/ipc/zwp_primary_selection_source_v1.rs +++ b/src/ifs/ipc/zwp_primary_selection_source_v1.rs @@ -4,10 +4,8 @@ use { ifs::{ ipc::{ add_data_source_mime_type, break_source_loops, cancel_offers, destroy_data_source, - detach_seat, offer_source_to_regular_client, offer_source_to_wlr_device, - offer_source_to_x, + detach_seat, offer_source_to_x, x_data_device::{XIpcDevice, XPrimarySelectionIpc}, - zwlr_data_control_device_v1::{WlrPrimarySelectionIpc, ZwlrDataControlDeviceV1}, zwp_primary_selection_device_v1::PrimarySelectionIpc, DataSource, DynDataSource, SourceData, }, @@ -44,16 +42,8 @@ impl DynDataSource for ZwpPrimarySelectionSourceV1 { ZwpPrimarySelectionSourceV1::send_send(self, mime_type, fd) } - fn offer_to_regular_client(self: Rc, client: &Rc) { - offer_source_to_regular_client::(&self, client); - } - fn offer_to_x(self: Rc, dd: &Rc) { - offer_source_to_x::(&self, dd); - } - - fn offer_to_wlr_device(self: Rc, dd: &Rc) { - offer_source_to_wlr_device::(&self, dd) + offer_source_to_x::(self, dd); } fn detach_seat(&self, seat: &Rc) { diff --git a/src/ifs/wl_seat.rs b/src/ifs/wl_seat.rs index 86b5bd2c..0b88f732 100644 --- a/src/ifs/wl_seat.rs +++ b/src/ifs/wl_seat.rs @@ -32,7 +32,7 @@ use { ifs::{ ext_idle_notification_v1::ExtIdleNotificationV1, ipc::{ - self, + self, offer_source_to_regular_client, offer_source_to_wlr_device, wl_data_device::{ClipboardIpc, WlDataDevice}, wl_data_source::WlDataSource, x_data_device::{XClipboardIpc, XIpcDevice, XIpcDeviceId, XPrimarySelectionIpc}, @@ -739,7 +739,7 @@ impl WlSeatGlobal { // client.flush(); } W::for_each_device(self, |device| match &src { - Some(src) => src.clone().offer_to_wlr_device(device), + Some(src) => offer_source_to_wlr_device::(src.clone(), device), _ => W::send_selection(device, None), }); Ok(()) @@ -763,7 +763,7 @@ impl WlSeatGlobal { }); } else { match selection { - Some(src) => src.offer_to_regular_client(client), + Some(src) => offer_source_to_regular_client::(src, client), _ => T::for_each_device(self, client.id, |device| { T::send_selection(device, None); }), diff --git a/src/ifs/wl_seat/event_handling.rs b/src/ifs/wl_seat/event_handling.rs index 0a8ded1d..54a7407e 100644 --- a/src/ifs/wl_seat/event_handling.rs +++ b/src/ifs/wl_seat/event_handling.rs @@ -9,13 +9,13 @@ use { fixed::Fixed, ifs::{ ipc::{ + offer_source_to_regular_client, wl_data_device::{ClipboardIpc, WlDataDevice}, x_data_device::{XClipboardIpc, XPrimarySelectionIpc}, zwlr_data_control_device_v1::ZwlrDataControlDeviceV1, zwp_primary_selection_device_v1::{ PrimarySelectionIpc, ZwpPrimarySelectionDeviceV1, }, - DynDataSource, }, wl_seat::{ tablet::{TabletPad, TabletPadId, TabletTool, TabletToolId}, @@ -1445,7 +1445,7 @@ impl WlSeatGlobal { ) { if let Some(src) = &dnd.src { if !surface.client.is_xwayland { - src.clone().offer_to_regular_client(&surface.client); + offer_source_to_regular_client::(src.clone(), &surface.client); } src.for_each_data_offer(|offer| { offer.send_enter(surface.id, x, y, serial); From dfc0a119356936079557c8a22feef119fa84475b Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Tue, 8 Oct 2024 13:11:42 +0200 Subject: [PATCH 3/5] data-control: remove wlr-specific code --- src/client/objects.rs | 3 +- src/compositor.rs | 1 + src/globals.rs | 2 +- src/ifs/ipc.rs | 22 ++----- src/ifs/ipc/data_control.rs | 21 +++++++ .../zwlr_data_control_device_v1.rs | 63 +++++++++++++------ .../zwlr_data_control_manager_v1.rs | 27 ++++---- .../zwlr_data_control_offer_v1.rs | 7 ++- .../zwlr_data_control_source_v1.rs | 8 ++- src/ifs/wl_seat.rs | 47 +++++++------- src/ifs/wl_seat/event_handling.rs | 12 ---- src/state.rs | 6 +- 12 files changed, 120 insertions(+), 99 deletions(-) create mode 100644 src/ifs/ipc/data_control.rs rename src/ifs/ipc/{ => data_control}/zwlr_data_control_device_v1.rs (80%) rename src/ifs/ipc/{ => data_control}/zwlr_data_control_manager_v1.rs (79%) rename src/ifs/ipc/{ => data_control}/zwlr_data_control_offer_v1.rs (93%) rename src/ifs/ipc/{ => data_control}/zwlr_data_control_source_v1.rs (94%) diff --git a/src/client/objects.rs b/src/client/objects.rs index 5fade5f4..1ebd3ada 100644 --- a/src/client/objects.rs +++ b/src/client/objects.rs @@ -6,7 +6,8 @@ use { ext_image_capture_source_v1::ExtImageCaptureSourceV1, ext_image_copy::ext_image_copy_capture_session_v1::ExtImageCopyCaptureSessionV1, ipc::{ - wl_data_source::WlDataSource, zwlr_data_control_source_v1::ZwlrDataControlSourceV1, + data_control::zwlr_data_control_source_v1::ZwlrDataControlSourceV1, + wl_data_source::WlDataSource, zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1, }, jay_output::JayOutput, diff --git a/src/compositor.rs b/src/compositor.rs index a466757b..a77d6170 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -274,6 +274,7 @@ fn start_compositor2( toplevels: Default::default(), const_40hz_latch: Default::default(), tray_item_ids: Default::default(), + data_control_device_ids: Default::default(), }); state.tracker.register(ClientId::from_raw(0)); create_dummy_output(&state); diff --git a/src/globals.rs b/src/globals.rs index 17357566..b541cb1e 100644 --- a/src/globals.rs +++ b/src/globals.rs @@ -10,8 +10,8 @@ use { ext_output_image_capture_source_manager_v1::ExtOutputImageCaptureSourceManagerV1Global, ext_session_lock_manager_v1::ExtSessionLockManagerV1Global, ipc::{ + data_control::zwlr_data_control_manager_v1::ZwlrDataControlManagerV1Global, wl_data_device_manager::WlDataDeviceManagerGlobal, - zwlr_data_control_manager_v1::ZwlrDataControlManagerV1Global, zwp_primary_selection_device_manager_v1::ZwpPrimarySelectionDeviceManagerV1Global, }, jay_compositor::JayCompositorGlobal, diff --git a/src/ifs/ipc.rs b/src/ifs/ipc.rs index 3bb77ed8..ecd7686a 100644 --- a/src/ifs/ipc.rs +++ b/src/ifs/ipc.rs @@ -2,12 +2,7 @@ use { crate::{ client::{Client, ClientError, ClientId}, fixed::Fixed, - ifs::{ - ipc::{ - x_data_device::XIpcDevice, zwlr_data_control_device_v1::ZwlrDataControlDeviceV1, - }, - wl_seat::WlSeatGlobal, - }, + ifs::{ipc::x_data_device::XIpcDevice, wl_seat::WlSeatGlobal}, utils::{ bitflags::BitflagsExt, cell_ext::CellExt, clonecell::CloneCell, numcell::NumCell, smallmap::SmallMap, @@ -26,6 +21,7 @@ use { uapi::OwnedFd, }; +pub mod data_control; pub mod wl_data_device; pub mod wl_data_device_manager; pub mod wl_data_offer; @@ -33,10 +29,6 @@ pub mod wl_data_source; pub mod x_data_device; pub mod x_data_offer; pub mod x_data_source; -pub mod zwlr_data_control_device_v1; -pub mod zwlr_data_control_manager_v1; -pub mod zwlr_data_control_offer_v1; -pub mod zwlr_data_control_source_v1; pub mod zwp_primary_selection_device_manager_v1; pub mod zwp_primary_selection_device_v1; pub mod zwp_primary_selection_offer_v1; @@ -137,12 +129,6 @@ pub trait IterableIpcVtable: IpcVtable { C: FnMut(&Rc); } -pub trait WlrIpcVtable: IpcVtable { - fn for_each_device(seat: &WlSeatGlobal, f: C) - where - C: FnMut(&Rc); -} - pub trait IpcVtable: Sized { type Device; type Source: DataSource; @@ -360,9 +346,9 @@ where offer_source_to_device::(&src, dd, data, shared); } -pub fn offer_source_to_wlr_device(src: Rc, dd: &Rc) +pub fn offer_source_to_data_control_device(src: Rc, dd: &Rc) where - T: IpcVtable, + T: IpcVtable, { let data = src.source_data(); let shared = data.shared.get(); diff --git a/src/ifs/ipc/data_control.rs b/src/ifs/ipc/data_control.rs new file mode 100644 index 00000000..976f8a1d --- /dev/null +++ b/src/ifs/ipc/data_control.rs @@ -0,0 +1,21 @@ +use { + crate::ifs::ipc::{DynDataSource, IpcLocation}, + std::rc::Rc, +}; + +pub mod zwlr_data_control_device_v1; +pub mod zwlr_data_control_manager_v1; +pub mod zwlr_data_control_offer_v1; +pub mod zwlr_data_control_source_v1; + +linear_ids!(DataControlDeviceIds, DataControlDeviceId, u64); + +pub trait DynDataControlDevice { + fn id(&self) -> DataControlDeviceId; + + fn handle_new_source( + self: Rc, + location: IpcLocation, + source: Option>, + ); +} diff --git a/src/ifs/ipc/zwlr_data_control_device_v1.rs b/src/ifs/ipc/data_control/zwlr_data_control_device_v1.rs similarity index 80% rename from src/ifs/ipc/zwlr_data_control_device_v1.rs rename to src/ifs/ipc/data_control/zwlr_data_control_device_v1.rs index ecd5cf2f..4ab57159 100644 --- a/src/ifs/ipc/zwlr_data_control_device_v1.rs +++ b/src/ifs/ipc/data_control/zwlr_data_control_device_v1.rs @@ -3,13 +3,17 @@ use { client::{Client, ClientError}, ifs::{ ipc::{ - break_device_loops, destroy_data_device, - zwlr_data_control_device_v1::private::{ - WlrClipboardIpcCore, WlrIpcImpl, WlrPrimarySelectionIpcCore, + break_device_loops, + data_control::{ + zwlr_data_control_device_v1::private::{ + WlrClipboardIpcCore, WlrIpcImpl, WlrPrimarySelectionIpcCore, + }, + zwlr_data_control_offer_v1::ZwlrDataControlOfferV1, + zwlr_data_control_source_v1::ZwlrDataControlSourceV1, + DataControlDeviceId, DynDataControlDevice, }, - zwlr_data_control_offer_v1::ZwlrDataControlOfferV1, - zwlr_data_control_source_v1::ZwlrDataControlSourceV1, - DeviceData, IpcLocation, IpcVtable, OfferData, Role, WlrIpcVtable, + destroy_data_device, offer_source_to_data_control_device, DeviceData, + DynDataSource, IpcLocation, IpcVtable, OfferData, Role, }, wl_seat::{WlSeatError, WlSeatGlobal}, }, @@ -28,6 +32,7 @@ pub const PRIMARY_SELECTION_SINCE: Version = Version(2); pub struct ZwlrDataControlDeviceV1 { pub id: ZwlrDataControlDeviceV1Id, + pub data_control_device_id: DataControlDeviceId, pub client: Rc, pub version: Version, pub seat: Rc, @@ -45,6 +50,7 @@ impl ZwlrDataControlDeviceV1 { ) -> Self { Self { id, + data_control_device_id: client.state.data_control_device_ids.next(), client: client.clone(), version, seat: seat.clone(), @@ -111,7 +117,7 @@ impl ZwlrDataControlDeviceV1RequestHandler for ZwlrDataControlDeviceV1 { fn destroy(&self, _req: Destroy, _slf: &Rc) -> Result<(), Self::Error> { destroy_data_device::(self); destroy_data_device::(self); - self.seat.remove_wlr_device(self); + self.seat.remove_data_control_device(self); self.client.remove_obj(self)?; Ok(()) } @@ -138,7 +144,6 @@ pub type WlrClipboardIpc = WlrIpcImpl; pub type WlrPrimarySelectionIpc = WlrIpcImpl; trait WlrIpc { - const MIN_VERSION: Version; const LOCATION: IpcLocation; fn wlr_get_device_data(dd: &ZwlrDataControlDeviceV1) -> &DeviceData; @@ -149,7 +154,6 @@ trait WlrIpc { } impl WlrIpc for WlrClipboardIpcCore { - const MIN_VERSION: Version = Version::ALL; const LOCATION: IpcLocation = IpcLocation::Clipboard; fn wlr_get_device_data(dd: &ZwlrDataControlDeviceV1) -> &DeviceData { @@ -169,7 +173,6 @@ impl WlrIpc for WlrClipboardIpcCore { } impl WlrIpc for WlrPrimarySelectionIpcCore { - const MIN_VERSION: Version = PRIMARY_SELECTION_SINCE; const LOCATION: IpcLocation = IpcLocation::PrimarySelection; fn wlr_get_device_data(dd: &ZwlrDataControlDeviceV1) -> &DeviceData { @@ -188,15 +191,6 @@ impl WlrIpc for WlrPrimarySelectionIpcCore { } } -impl WlrIpcVtable for WlrIpcImpl { - fn for_each_device(seat: &WlSeatGlobal, f: C) - where - C: FnMut(&Rc), - { - seat.for_each_wlr_data_device(T::MIN_VERSION, f) - } -} - impl IpcVtable for WlrIpcImpl { type Device = ZwlrDataControlDeviceV1; type Source = ZwlrDataControlSourceV1; @@ -245,6 +239,35 @@ impl IpcVtable for WlrIpcImpl { } } +impl DynDataControlDevice for ZwlrDataControlDeviceV1 { + fn id(&self) -> DataControlDeviceId { + self.data_control_device_id + } + + fn handle_new_source( + self: Rc, + location: IpcLocation, + source: Option>, + ) { + match location { + IpcLocation::Clipboard => match source { + Some(src) => offer_source_to_data_control_device::(src, &self), + _ => self.send_selection(None), + }, + IpcLocation::PrimarySelection => { + if self.version >= PRIMARY_SELECTION_SINCE { + match source { + Some(src) => offer_source_to_data_control_device::( + src, &self, + ), + _ => self.send_primary_selection(None), + } + } + } + } + } +} + object_base! { self = ZwlrDataControlDeviceV1; version = self.version; @@ -254,7 +277,7 @@ impl Object for ZwlrDataControlDeviceV1 { fn break_loops(&self) { break_device_loops::(self); break_device_loops::(self); - self.seat.remove_wlr_device(self); + self.seat.remove_data_control_device(self); } } diff --git a/src/ifs/ipc/zwlr_data_control_manager_v1.rs b/src/ifs/ipc/data_control/zwlr_data_control_manager_v1.rs similarity index 79% rename from src/ifs/ipc/zwlr_data_control_manager_v1.rs rename to src/ifs/ipc/data_control/zwlr_data_control_manager_v1.rs index 4959548e..8a43469a 100644 --- a/src/ifs/ipc/zwlr_data_control_manager_v1.rs +++ b/src/ifs/ipc/data_control/zwlr_data_control_manager_v1.rs @@ -3,12 +3,11 @@ use { client::{Client, ClientCaps, ClientError, CAP_DATA_CONTROL_MANAGER}, globals::{Global, GlobalName}, ifs::ipc::{ - offer_source_to_wlr_device, - zwlr_data_control_device_v1::{ - WlrClipboardIpc, WlrPrimarySelectionIpc, ZwlrDataControlDeviceV1, - PRIMARY_SELECTION_SINCE, + data_control::{ + zwlr_data_control_device_v1::ZwlrDataControlDeviceV1, + zwlr_data_control_source_v1::ZwlrDataControlSourceV1, DynDataControlDevice, }, - zwlr_data_control_source_v1::ZwlrDataControlSourceV1, + IpcLocation, }, leaks::Tracker, object::{Object, Version}, @@ -79,18 +78,14 @@ impl ZwlrDataControlManagerV1RequestHandler for ZwlrDataControlManagerV1 { &seat.global, )); track!(self.client, dev); - seat.global.add_wlr_device(&dev); + seat.global.add_data_control_device(dev.clone()); self.client.add_client_obj(&dev)?; - match seat.global.get_selection() { - Some(s) => offer_source_to_wlr_device::(s, &dev), - _ => dev.send_selection(None), - } - if self.version >= PRIMARY_SELECTION_SINCE { - match seat.global.get_primary_selection() { - Some(s) => offer_source_to_wlr_device::(s, &dev), - _ => dev.send_primary_selection(None), - } - } + dev.clone() + .handle_new_source(IpcLocation::Clipboard, seat.global.get_selection()); + dev.clone().handle_new_source( + IpcLocation::PrimarySelection, + seat.global.get_primary_selection(), + ); Ok(()) } diff --git a/src/ifs/ipc/zwlr_data_control_offer_v1.rs b/src/ifs/ipc/data_control/zwlr_data_control_offer_v1.rs similarity index 93% rename from src/ifs/ipc/zwlr_data_control_offer_v1.rs rename to src/ifs/ipc/data_control/zwlr_data_control_offer_v1.rs index 6307edc1..7c0f9197 100644 --- a/src/ifs/ipc/zwlr_data_control_offer_v1.rs +++ b/src/ifs/ipc/data_control/zwlr_data_control_offer_v1.rs @@ -3,11 +3,12 @@ use { client::{Client, ClientError, ClientId}, ifs::{ ipc::{ - break_offer_loops, cancel_offer, destroy_data_offer, receive_data_offer, - zwlr_data_control_device_v1::{ + break_offer_loops, cancel_offer, + data_control::zwlr_data_control_device_v1::{ WlrClipboardIpc, WlrPrimarySelectionIpc, ZwlrDataControlDeviceV1, }, - DataOffer, DataOfferId, DynDataOffer, IpcLocation, OfferData, + destroy_data_offer, receive_data_offer, DataOffer, DataOfferId, DynDataOffer, + IpcLocation, OfferData, }, wl_seat::WlSeatGlobal, }, diff --git a/src/ifs/ipc/zwlr_data_control_source_v1.rs b/src/ifs/ipc/data_control/zwlr_data_control_source_v1.rs similarity index 94% rename from src/ifs/ipc/zwlr_data_control_source_v1.rs rename to src/ifs/ipc/data_control/zwlr_data_control_source_v1.rs index 0cfef704..08488bd9 100644 --- a/src/ifs/ipc/zwlr_data_control_source_v1.rs +++ b/src/ifs/ipc/data_control/zwlr_data_control_source_v1.rs @@ -3,10 +3,12 @@ use { client::{Client, ClientError}, ifs::{ ipc::{ - add_data_source_mime_type, break_source_loops, cancel_offers, destroy_data_source, - detach_seat, offer_source_to_x, + add_data_source_mime_type, break_source_loops, cancel_offers, + data_control::zwlr_data_control_device_v1::{ + WlrClipboardIpc, WlrPrimarySelectionIpc, + }, + destroy_data_source, detach_seat, offer_source_to_x, x_data_device::{XClipboardIpc, XIpcDevice, XPrimarySelectionIpc}, - zwlr_data_control_device_v1::{WlrClipboardIpc, WlrPrimarySelectionIpc}, DataSource, DynDataSource, IpcLocation, SourceData, }, wl_seat::WlSeatGlobal, diff --git a/src/ifs/wl_seat.rs b/src/ifs/wl_seat.rs index 0b88f732..96dd6b06 100644 --- a/src/ifs/wl_seat.rs +++ b/src/ifs/wl_seat.rs @@ -32,18 +32,17 @@ use { ifs::{ ext_idle_notification_v1::ExtIdleNotificationV1, ipc::{ - self, offer_source_to_regular_client, offer_source_to_wlr_device, + self, + data_control::{DataControlDeviceId, DynDataControlDevice}, + offer_source_to_regular_client, wl_data_device::{ClipboardIpc, WlDataDevice}, wl_data_source::WlDataSource, x_data_device::{XClipboardIpc, XIpcDevice, XIpcDeviceId, XPrimarySelectionIpc}, - zwlr_data_control_device_v1::{ - WlrClipboardIpc, WlrPrimarySelectionIpc, ZwlrDataControlDeviceV1, - }, zwp_primary_selection_device_v1::{ PrimarySelectionIpc, ZwpPrimarySelectionDeviceV1, }, zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1, - DynDataSource, IpcError, + DynDataSource, IpcError, IpcLocation, }, wl_output::WlOutputGlobal, wl_seat::{ @@ -88,8 +87,8 @@ use { }, wire::{ wl_seat::*, ExtIdleNotificationV1Id, WlDataDeviceId, WlKeyboardId, WlPointerId, - WlSeatId, WlTouchId, XdgPopupId, ZwlrDataControlDeviceV1Id, - ZwpPrimarySelectionDeviceV1Id, ZwpRelativePointerV1Id, ZwpTextInputV3Id, + WlSeatId, WlTouchId, XdgPopupId, ZwpPrimarySelectionDeviceV1Id, ZwpRelativePointerV1Id, + ZwpTextInputV3Id, }, wire_ei::EiSeatId, xkbcommon::{DynKeyboardState, KeyboardState, KeymapId, XkbKeymap, XkbState}, @@ -167,8 +166,7 @@ pub struct WlSeatGlobal { AHashMap>, >, >, - wlr_data_devices: - CopyHashMap<(ClientId, ZwlrDataControlDeviceV1Id), Rc>, + data_control_devices: CopyHashMap>, repeat_rate: Cell<(i32, i32)>, seat_kb_map: CloneCell>, seat_xkb_state: CloneCell>>, @@ -267,7 +265,7 @@ impl WlSeatGlobal { constraint: Default::default(), idle_notifications: Default::default(), last_input_usec: Cell::new(state.now_usec()), - wlr_data_devices: Default::default(), + data_control_devices: Default::default(), text_inputs: Default::default(), text_input: Default::default(), input_method: Default::default(), @@ -383,13 +381,12 @@ impl WlSeatGlobal { } } - pub fn add_wlr_device(&self, device: &Rc) { - self.wlr_data_devices - .set((device.client.id, device.id), device.clone()); + pub fn add_data_control_device(&self, device: Rc) { + self.data_control_devices.set(device.id(), device.clone()); } - pub fn remove_wlr_device(&self, device: &ZwlrDataControlDeviceV1) { - self.wlr_data_devices.remove(&(device.client.id, device.id)); + pub fn remove_data_control_device(&self, device: &dyn DynDataControlDevice) { + self.data_control_devices.remove(&device.id()); } pub fn get_output(&self) -> Rc { @@ -711,15 +708,15 @@ impl WlSeatGlobal { } } - fn set_selection_( + fn set_selection_( self: &Rc, field: &CloneCell>>, src: Option>, + location: IpcLocation, ) -> Result<(), WlSeatError> where T: ipc::IterableIpcVtable, X: ipc::IpcVtable, - W: ipc::WlrIpcVtable, S: DynDataSource, { if let (Some(new), Some(old)) = (&src, &field.get()) { @@ -738,10 +735,10 @@ impl WlSeatGlobal { self.offer_selection_to_client::(src.clone().map(|v| v as Rc<_>), &client); // client.flush(); } - W::for_each_device(self, |device| match &src { - Some(src) => offer_source_to_wlr_device::(src.clone(), device), - _ => W::send_selection(device, None), - }); + let dyn_source = src.map(|s| s as Rc); + for dd in self.data_control_devices.lock().values() { + dd.clone().handle_new_source(location, dyn_source.clone()); + } Ok(()) } @@ -825,9 +822,10 @@ impl WlSeatGlobal { self: &Rc, selection: Option>, ) -> Result<(), WlSeatError> { - self.set_selection_::( + self.set_selection_::( &self.selection, selection, + IpcLocation::Clipboard, ) } @@ -871,9 +869,10 @@ impl WlSeatGlobal { self: &Rc, selection: Option>, ) -> Result<(), WlSeatError> { - self.set_selection_::( + self.set_selection_::( &self.primary_selection, selection, + IpcLocation::PrimarySelection, ) } @@ -910,7 +909,7 @@ impl WlSeatGlobal { self.bindings.borrow_mut().clear(); self.data_devices.borrow_mut().clear(); self.primary_selection_devices.borrow_mut().clear(); - self.wlr_data_devices.clear(); + self.data_control_devices.clear(); self.cursor_user_group.detach(); self.selection.set(None); self.primary_selection.set(None); diff --git a/src/ifs/wl_seat/event_handling.rs b/src/ifs/wl_seat/event_handling.rs index 54a7407e..159dd676 100644 --- a/src/ifs/wl_seat/event_handling.rs +++ b/src/ifs/wl_seat/event_handling.rs @@ -12,7 +12,6 @@ use { offer_source_to_regular_client, wl_data_device::{ClipboardIpc, WlDataDevice}, x_data_device::{XClipboardIpc, XPrimarySelectionIpc}, - zwlr_data_control_device_v1::ZwlrDataControlDeviceV1, zwp_primary_selection_device_v1::{ PrimarySelectionIpc, ZwpPrimarySelectionDeviceV1, }, @@ -1037,17 +1036,6 @@ impl WlSeatGlobal { } } - pub fn for_each_wlr_data_device(&self, ver: Version, mut f: C) - where - C: FnMut(&Rc), - { - for dd in self.wlr_data_devices.lock().values() { - if dd.version >= ver { - f(dd); - } - } - } - fn surface_pointer_frame(&self, surface: &WlSurface) { self.surface_pointer_event(POINTER_FRAME_SINCE_VERSION, surface, |p| p.send_frame()); } diff --git a/src/state.rs b/src/state.rs index 142a9531..6a958b72 100644 --- a/src/state.rs +++ b/src/state.rs @@ -35,7 +35,10 @@ use { ifs::{ ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1, ext_session_lock_v1::ExtSessionLockV1, - ipc::{x_data_device::XIpcDeviceIds, DataOfferIds, DataSourceIds}, + ipc::{ + data_control::DataControlDeviceIds, x_data_device::XIpcDeviceIds, DataOfferIds, + DataSourceIds, + }, jay_render_ctx::JayRenderCtx, jay_screencast::JayScreencast, jay_seat_events::JaySeatEvents, @@ -224,6 +227,7 @@ pub struct State { pub toplevels: CopyHashMap>, pub const_40hz_latch: EventSource, pub tray_item_ids: TrayItemIds, + pub data_control_device_ids: DataControlDeviceIds, } // impl Drop for State { From be1462d0ef4a8e6c4bea1e5d830285d02a28f7a1 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Tue, 8 Oct 2024 16:00:40 +0200 Subject: [PATCH 4/5] ipc: move data control logic out of wlr code --- src/ifs/ipc/data_control.rs | 1 + src/ifs/ipc/data_control/private.rs | 426 ++++++++++++++++++ .../zwlr_data_control_device_v1.rs | 228 ++-------- .../zwlr_data_control_offer_v1.rs | 81 +--- .../zwlr_data_control_source_v1.rs | 89 ++-- 5 files changed, 520 insertions(+), 305 deletions(-) create mode 100644 src/ifs/ipc/data_control/private.rs diff --git a/src/ifs/ipc/data_control.rs b/src/ifs/ipc/data_control.rs index 976f8a1d..20c3418d 100644 --- a/src/ifs/ipc/data_control.rs +++ b/src/ifs/ipc/data_control.rs @@ -3,6 +3,7 @@ use { std::rc::Rc, }; +mod private; pub mod zwlr_data_control_device_v1; pub mod zwlr_data_control_manager_v1; pub mod zwlr_data_control_offer_v1; diff --git a/src/ifs/ipc/data_control/private.rs b/src/ifs/ipc/data_control/private.rs new file mode 100644 index 00000000..629765f7 --- /dev/null +++ b/src/ifs/ipc/data_control/private.rs @@ -0,0 +1,426 @@ +use { + crate::{ + client::{Client, ClientError, ClientId, WaylandObject, WaylandObjectLookup}, + ifs::{ + ipc::{ + cancel_offer, cancel_offers, + data_control::{DataControlDeviceId, DynDataControlDevice}, + detach_seat, offer_source_to_data_control_device, offer_source_to_x, + x_data_device::{XClipboardIpc, XIpcDevice, XPrimarySelectionIpc}, + DataOffer, DataOfferId, DataSource, DeviceData, DynDataOffer, DynDataSource, + IpcLocation, IpcVtable, OfferData, Role, SourceData, + }, + wl_seat::WlSeatGlobal, + }, + object::{ObjectId, Version}, + }, + std::{cell::Cell, marker::PhantomData, rc::Rc}, + uapi::OwnedFd, +}; + +struct ClipboardCore(PhantomData); +struct PrimarySelectionCore(PhantomData); +struct DataControlIpcImpl(PhantomData); + +type Device = ::Device; +type Offer = ::Offer; +type Source = ::Source; +type SourceId = ::SourceId; + +pub trait DataControlIpc: Sized + 'static { + const PRIMARY_SELECTION_SINCE: Version; + + type Device: DataControlDevice; + type OfferId: From; + type Offer: DataControlOffer; + type SourceId: WaylandObjectLookup; + type Source: DataControlSource; + + fn create_offer(id: Self::OfferId, data: DataControlOfferData) -> Rc; +} + +pub struct DataControlDeviceData { + pub data_control_device_id: DataControlDeviceId, + pub client: Rc, + pub version: Version, + pub seat: Rc, + pub clipboard_data: DeviceData, + pub primary_selection_data: DeviceData, +} + +pub trait DataControlDevice: WaylandObject { + type Ipc: DataControlIpc; + + fn data(&self) -> &DataControlDeviceData; + + fn send_data_offer(&self, offer: &Rc>); + + fn send_selection(&self, offer: Option<&Rc>>); + + fn send_primary_selection(&self, offer: Option<&Rc>>); +} + +pub struct DataControlOfferData { + pub offer_id: DataOfferId, + pub client: Rc, + pub device: Rc, + pub data: OfferData, + pub location: IpcLocation, +} + +pub trait DataControlOffer: WaylandObject { + type Ipc: DataControlIpc; + + fn data(&self) -> &DataControlOfferData; + + fn send_offer(&self, mime_type: &str); +} + +pub struct DataControlSourceData { + pub data: SourceData, + pub version: Version, + pub location: Cell, + pub used: Cell, +} + +pub trait DataControlSource: WaylandObject { + type Ipc: DataControlIpc; + + fn data(&self) -> &DataControlSourceData; + + fn send_cancelled(&self); + + fn send_send(&self, mime_type: &str, fd: Rc); +} + +impl DynDataControlDevice for T { + fn id(&self) -> DataControlDeviceId { + self.data().data_control_device_id + } + + fn handle_new_source( + self: Rc, + location: IpcLocation, + source: Option>, + ) { + if location == IpcLocation::PrimarySelection + && self.data().version < T::Ipc::PRIMARY_SELECTION_SINCE + { + return; + } + match location { + IpcLocation::Clipboard => match source { + Some(src) => { + offer_source_to_data_control_device::>(src, &self); + } + _ => self.send_selection(None), + }, + IpcLocation::PrimarySelection => match source { + Some(src) => { + offer_source_to_data_control_device::>(src, &self); + } + _ => self.send_primary_selection(None), + }, + } + } +} + +type Clipboard = DataControlIpcImpl>; +type PrimarySelection = DataControlIpcImpl>; + +pub trait DataControlLocationIpc { + type Ipc: DataControlIpc; + const LOCATION: IpcLocation; + + fn loc_get_device_data(dd: &Device) -> &DeviceData>; + + fn loc_send_selection(dd: &Device, offer: Option<&Rc>>); + + fn loc_unset(seat: &Rc); +} + +impl DataControlLocationIpc for ClipboardCore { + type Ipc = T; + const LOCATION: IpcLocation = IpcLocation::Clipboard; + + fn loc_get_device_data(dd: &Device) -> &DeviceData> { + &dd.data().clipboard_data + } + + fn loc_send_selection(dd: &Device, offer: Option<&Rc>>) { + dd.send_selection(offer) + } + + fn loc_unset(seat: &Rc) { + seat.unset_selection() + } +} + +impl DataControlLocationIpc for PrimarySelectionCore { + type Ipc = T; + const LOCATION: IpcLocation = IpcLocation::PrimarySelection; + + fn loc_get_device_data(dd: &Device) -> &DeviceData> { + &dd.data().primary_selection_data + } + + fn loc_send_selection(dd: &Device, offer: Option<&Rc>>) { + dd.send_primary_selection(offer) + } + + fn loc_unset(seat: &Rc) { + seat.unset_primary_selection() + } +} + +impl IpcVtable for DataControlIpcImpl { + type Device = Device; + type Source = Source; + type Offer = Offer; + + fn get_device_data(dd: &Self::Device) -> &DeviceData { + T::loc_get_device_data(dd) + } + + fn get_device_seat(dd: &Self::Device) -> Rc { + dd.data().seat.clone() + } + + fn create_offer( + device: &Rc, + offer_data: OfferData, + ) -> Result, ClientError> { + let data = device.data(); + let offer = DataControlOfferData { + offer_id: data.client.state.data_offer_ids.next(), + client: data.client.clone(), + device: device.clone(), + data: offer_data, + location: T::LOCATION, + }; + let rc = T::Ipc::create_offer(data.client.new_id()?, offer); + data.client.add_server_obj(&rc); + Ok(rc) + } + + fn send_selection(dd: &Self::Device, offer: Option<&Rc>) { + T::loc_send_selection(dd, offer) + } + + fn send_offer(dd: &Self::Device, offer: &Rc) { + dd.send_data_offer(offer); + } + + fn unset(seat: &Rc, _role: Role) { + T::loc_unset(seat) + } + + fn device_client(dd: &Rc) -> &Rc { + &dd.data().client + } +} + +impl DataSource for T { + fn send_cancelled(&self, _seat: &Rc) { + self.send_cancelled(); + } +} + +impl DynDataSource for T { + fn source_data(&self) -> &SourceData { + &self.data().data + } + + fn send_send(&self, mime_type: &str, fd: Rc) { + self.send_send(mime_type, fd); + } + + fn offer_to_x(self: Rc, dd: &Rc) { + match self.data().location.get() { + IpcLocation::Clipboard => offer_source_to_x::(self, dd), + IpcLocation::PrimarySelection => offer_source_to_x::(self, dd), + } + } + + fn detach_seat(&self, seat: &Rc) { + detach_seat(self, seat) + } + + fn cancel_unprivileged_offers(&self) { + cancel_offers(self, false) + } +} + +impl DataOffer for T { + type Device = Device; + + fn offer_data(&self) -> &OfferData { + &self.data().data + } +} + +impl DynDataOffer for T { + fn offer_id(&self) -> DataOfferId { + self.data().offer_id + } + + fn client_id(&self) -> ClientId { + self.data().client.id + } + + fn send_offer(&self, mime_type: &str) { + self.send_offer(mime_type); + } + + fn cancel(&self) { + match self.data().location { + IpcLocation::Clipboard => cancel_offer::>(self), + IpcLocation::PrimarySelection => cancel_offer::>(self), + } + } + + fn get_seat(&self) -> Rc { + self.data().device.data().seat.clone() + } + + fn is_privileged(&self) -> bool { + true + } +} + +pub mod logic { + use { + crate::{ + client::ClientError, + ifs::{ + ipc::{ + add_data_source_mime_type, break_device_loops, break_offer_loops, + break_source_loops, + data_control::private::{ + Clipboard, DataControlDevice, DataControlOffer, DataControlSource, + PrimarySelection, Source, SourceId, + }, + destroy_data_device, destroy_data_offer, destroy_data_source, + receive_data_offer, IpcLocation, + }, + wl_seat::WlSeatError, + }, + }, + std::rc::Rc, + thiserror::Error, + uapi::OwnedFd, + }; + + pub fn data_device_break_loops(d: &D) { + break_device_loops::>(d); + break_device_loops::>(d); + d.data().seat.remove_data_control_device(d); + } + + fn use_source( + device: &D, + source: Option>, + location: IpcLocation, + ) -> Result>>, DataControlError> { + if let Some(source) = source { + let src = device.data().client.lookup(source)?; + if src.data().used.replace(true) { + return Err(DataControlError::AlreadyUsed); + } + src.data().location.set(location); + Ok(Some(src)) + } else { + Ok(None) + } + } + + pub fn device_set_selection( + d: &D, + source: Option>, + ) -> Result<(), DataControlError> { + let src = use_source(d, source, IpcLocation::Clipboard)?; + d.data().seat.set_selection(src)?; + Ok(()) + } + + pub fn device_destroy(d: &D) -> Result<(), DataControlError> { + destroy_data_device::>(d); + destroy_data_device::>(d); + d.data().seat.remove_data_control_device(d); + d.data().client.remove_obj(d)?; + Ok(()) + } + + pub fn device_set_primary_selection( + d: &D, + source: Option>, + ) -> Result<(), DataControlError> { + let src = use_source(d, source, IpcLocation::PrimarySelection)?; + d.data().seat.set_primary_selection(src)?; + Ok(()) + } + + pub fn data_source_offer( + s: &S, + mime_type: &str, + ) -> Result<(), DataControlError> { + if s.data().used.get() { + return Err(DataControlError::AlreadyUsed); + } + add_data_source_mime_type::>(s, mime_type); + Ok(()) + } + + pub fn data_source_destroy(s: &S) -> Result<(), DataControlError> { + match s.data().location.get() { + IpcLocation::Clipboard => destroy_data_source::>(s), + IpcLocation::PrimarySelection => destroy_data_source::>(s), + } + s.data().data.client.remove_obj(s)?; + Ok(()) + } + + pub fn data_source_break_loops(s: &S) { + match s.data().location.get() { + IpcLocation::Clipboard => break_source_loops::>(s), + IpcLocation::PrimarySelection => break_source_loops::>(s), + } + } + + pub fn data_offer_receive(o: &O, mime_type: &str, fd: Rc) { + match o.data().location { + IpcLocation::Clipboard => receive_data_offer::>(o, mime_type, fd), + IpcLocation::PrimarySelection => { + receive_data_offer::>(o, mime_type, fd) + } + } + } + + pub fn data_offer_destroy(o: &O) -> Result<(), DataControlError> { + match o.data().location { + IpcLocation::Clipboard => destroy_data_offer::>(o), + IpcLocation::PrimarySelection => destroy_data_offer::>(o), + } + o.data().client.remove_obj(o)?; + Ok(()) + } + + pub fn data_offer_break_loops(o: &O) { + match o.data().location { + IpcLocation::Clipboard => break_offer_loops::>(o), + IpcLocation::PrimarySelection => break_offer_loops::>(o), + } + } + + #[derive(Debug, Error)] + pub enum DataControlError { + #[error(transparent)] + ClientError(Box), + #[error(transparent)] + WlSeatError(Box), + #[error("The source has already been used")] + AlreadyUsed, + } + efrom!(DataControlError, ClientError); + efrom!(DataControlError, WlSeatError); +} diff --git a/src/ifs/ipc/data_control/zwlr_data_control_device_v1.rs b/src/ifs/ipc/data_control/zwlr_data_control_device_v1.rs index 4ab57159..2c12aedf 100644 --- a/src/ifs/ipc/data_control/zwlr_data_control_device_v1.rs +++ b/src/ifs/ipc/data_control/zwlr_data_control_device_v1.rs @@ -1,21 +1,16 @@ use { crate::{ - client::{Client, ClientError}, + client::Client, ifs::{ - ipc::{ - break_device_loops, - data_control::{ - zwlr_data_control_device_v1::private::{ - WlrClipboardIpcCore, WlrIpcImpl, WlrPrimarySelectionIpcCore, - }, - zwlr_data_control_offer_v1::ZwlrDataControlOfferV1, - zwlr_data_control_source_v1::ZwlrDataControlSourceV1, - DataControlDeviceId, DynDataControlDevice, + ipc::data_control::{ + private::{ + logic::{self, DataControlError}, + DataControlDevice, DataControlDeviceData, DataControlIpc, DataControlOfferData, }, - destroy_data_device, offer_source_to_data_control_device, DeviceData, - DynDataSource, IpcLocation, IpcVtable, OfferData, Role, + zwlr_data_control_offer_v1::ZwlrDataControlOfferV1, + zwlr_data_control_source_v1::ZwlrDataControlSourceV1, }, - wl_seat::{WlSeatError, WlSeatGlobal}, + wl_seat::WlSeatGlobal, }, leaks::Tracker, object::{Object, Version}, @@ -32,12 +27,7 @@ pub const PRIMARY_SELECTION_SINCE: Version = Version(2); pub struct ZwlrDataControlDeviceV1 { pub id: ZwlrDataControlDeviceV1Id, - pub data_control_device_id: DataControlDeviceId, - pub client: Rc, - pub version: Version, - pub seat: Rc, - pub clipboard_data: DeviceData, - pub primary_selection_data: DeviceData, + pub data: DataControlDeviceData, pub tracker: Tracker, } @@ -50,18 +40,20 @@ impl ZwlrDataControlDeviceV1 { ) -> Self { Self { id, - data_control_device_id: client.state.data_control_device_ids.next(), - client: client.clone(), - version, - seat: seat.clone(), - clipboard_data: Default::default(), - primary_selection_data: Default::default(), + data: DataControlDeviceData { + data_control_device_id: client.state.data_control_device_ids.next(), + client: client.clone(), + version, + seat: seat.clone(), + clipboard_data: Default::default(), + primary_selection_data: Default::default(), + }, tracker: Default::default(), } } pub fn send_data_offer(&self, offer: &Rc) { - self.client.event(DataOffer { + self.data.client.event(DataOffer { self_id: self.id, id: offer.id, }) @@ -71,7 +63,7 @@ impl ZwlrDataControlDeviceV1 { let id = offer .map(|o| o.id) .unwrap_or(ZwlrDataControlOfferV1Id::NONE); - self.client.event(Selection { + self.data.client.event(Selection { self_id: self.id, id, }) @@ -81,44 +73,23 @@ impl ZwlrDataControlDeviceV1 { let id = offer .map(|o| o.id) .unwrap_or(ZwlrDataControlOfferV1Id::NONE); - self.client.event(PrimarySelection { + self.data.client.event(PrimarySelection { self_id: self.id, id, }) } - - fn use_source( - &self, - source: ZwlrDataControlSourceV1Id, - location: IpcLocation, - ) -> Result>, ZwlrDataControlDeviceV1Error> { - if source.is_none() { - Ok(None) - } else { - let src = self.client.lookup(source)?; - if src.used.replace(true) { - return Err(ZwlrDataControlDeviceV1Error::AlreadyUsed); - } - src.location.set(location); - Ok(Some(src)) - } - } } impl ZwlrDataControlDeviceV1RequestHandler for ZwlrDataControlDeviceV1 { type Error = ZwlrDataControlDeviceV1Error; fn set_selection(&self, req: SetSelection, _slf: &Rc) -> Result<(), Self::Error> { - let src = self.use_source(req.source, IpcLocation::Clipboard)?; - self.seat.set_selection(src)?; + logic::device_set_selection(self, req.source.is_some().then_some(req.source))?; Ok(()) } fn destroy(&self, _req: Destroy, _slf: &Rc) -> Result<(), Self::Error> { - destroy_data_device::(self); - destroy_data_device::(self); - self.seat.remove_data_control_device(self); - self.client.remove_obj(self)?; + logic::device_destroy(self)?; Ok(()) } @@ -127,157 +98,60 @@ impl ZwlrDataControlDeviceV1RequestHandler for ZwlrDataControlDeviceV1 { req: SetPrimarySelection, _slf: &Rc, ) -> Result<(), Self::Error> { - let src = self.use_source(req.source, IpcLocation::PrimarySelection)?; - self.seat.set_primary_selection(src)?; + logic::device_set_primary_selection(self, req.source.is_some().then_some(req.source))?; Ok(()) } } -mod private { - use std::marker::PhantomData; - - pub struct WlrClipboardIpcCore; - pub struct WlrPrimarySelectionIpcCore; - pub struct WlrIpcImpl(PhantomData); -} -pub type WlrClipboardIpc = WlrIpcImpl; -pub type WlrPrimarySelectionIpc = WlrIpcImpl; - -trait WlrIpc { - const LOCATION: IpcLocation; - - fn wlr_get_device_data(dd: &ZwlrDataControlDeviceV1) -> &DeviceData; - - fn wlr_send_selection(dd: &ZwlrDataControlDeviceV1, offer: Option<&Rc>); - - fn wlr_unset(seat: &Rc); -} - -impl WlrIpc for WlrClipboardIpcCore { - const LOCATION: IpcLocation = IpcLocation::Clipboard; - - fn wlr_get_device_data(dd: &ZwlrDataControlDeviceV1) -> &DeviceData { - &dd.clipboard_data - } - - fn wlr_send_selection( - dd: &ZwlrDataControlDeviceV1, - offer: Option<&Rc>, - ) { - dd.send_selection(offer) - } - - fn wlr_unset(seat: &Rc) { - seat.unset_selection() - } -} - -impl WlrIpc for WlrPrimarySelectionIpcCore { - const LOCATION: IpcLocation = IpcLocation::PrimarySelection; +pub struct WlrDataControlIpc; - fn wlr_get_device_data(dd: &ZwlrDataControlDeviceV1) -> &DeviceData { - &dd.primary_selection_data - } - - fn wlr_send_selection( - dd: &ZwlrDataControlDeviceV1, - offer: Option<&Rc>, - ) { - dd.send_primary_selection(offer) - } - - fn wlr_unset(seat: &Rc) { - seat.unset_primary_selection() - } -} - -impl IpcVtable for WlrIpcImpl { +impl DataControlIpc for WlrDataControlIpc { + const PRIMARY_SELECTION_SINCE: Version = PRIMARY_SELECTION_SINCE; type Device = ZwlrDataControlDeviceV1; - type Source = ZwlrDataControlSourceV1; + type OfferId = ZwlrDataControlOfferV1Id; type Offer = ZwlrDataControlOfferV1; + type SourceId = ZwlrDataControlSourceV1Id; + type Source = ZwlrDataControlSourceV1; - fn get_device_data(dd: &Self::Device) -> &DeviceData { - T::wlr_get_device_data(dd) - } - - fn get_device_seat(dd: &Self::Device) -> Rc { - dd.seat.clone() - } - - fn create_offer( - device: &Rc, - offer_data: OfferData, - ) -> Result, ClientError> { + fn create_offer(id: Self::OfferId, data: DataControlOfferData) -> Rc { let rc = Rc::new(ZwlrDataControlOfferV1 { - id: device.client.new_id()?, - offer_id: device.client.state.data_offer_ids.next(), - client: device.client.clone(), - device: device.clone(), - data: offer_data, - location: T::LOCATION, + id, + data, tracker: Default::default(), }); - track!(device.client, rc); - device.client.add_server_obj(&rc); - Ok(rc) - } - - fn send_selection(dd: &Self::Device, offer: Option<&Rc>) { - T::wlr_send_selection(dd, offer) + track!(rc.data.client, rc); + rc } +} - fn send_offer(dd: &Self::Device, offer: &Rc) { - dd.send_data_offer(offer); - } +impl DataControlDevice for ZwlrDataControlDeviceV1 { + type Ipc = WlrDataControlIpc; - fn unset(seat: &Rc, _role: Role) { - T::wlr_unset(seat) + fn data(&self) -> &DataControlDeviceData { + &self.data } - fn device_client(dd: &Rc) -> &Rc { - &dd.client + fn send_data_offer(&self, offer: &Rc<::Offer>) { + self.send_data_offer(offer) } -} -impl DynDataControlDevice for ZwlrDataControlDeviceV1 { - fn id(&self) -> DataControlDeviceId { - self.data_control_device_id + fn send_selection(&self, offer: Option<&Rc<::Offer>>) { + self.send_selection(offer) } - fn handle_new_source( - self: Rc, - location: IpcLocation, - source: Option>, - ) { - match location { - IpcLocation::Clipboard => match source { - Some(src) => offer_source_to_data_control_device::(src, &self), - _ => self.send_selection(None), - }, - IpcLocation::PrimarySelection => { - if self.version >= PRIMARY_SELECTION_SINCE { - match source { - Some(src) => offer_source_to_data_control_device::( - src, &self, - ), - _ => self.send_primary_selection(None), - } - } - } - } + fn send_primary_selection(&self, offer: Option<&Rc<::Offer>>) { + self.send_primary_selection(offer) } } object_base! { self = ZwlrDataControlDeviceV1; - version = self.version; + version = self.data.version; } impl Object for ZwlrDataControlDeviceV1 { fn break_loops(&self) { - break_device_loops::(self); - break_device_loops::(self); - self.seat.remove_data_control_device(self); + logic::data_device_break_loops(self); } } @@ -286,11 +160,5 @@ simple_add_obj!(ZwlrDataControlDeviceV1); #[derive(Debug, Error)] pub enum ZwlrDataControlDeviceV1Error { #[error(transparent)] - ClientError(Box), - #[error(transparent)] - WlSeatError(Box), - #[error("The source has already been used")] - AlreadyUsed, + DataControlError(#[from] DataControlError), } -efrom!(ZwlrDataControlDeviceV1Error, ClientError); -efrom!(ZwlrDataControlDeviceV1Error, WlSeatError); diff --git a/src/ifs/ipc/data_control/zwlr_data_control_offer_v1.rs b/src/ifs/ipc/data_control/zwlr_data_control_offer_v1.rs index 7c0f9197..df9a61a8 100644 --- a/src/ifs/ipc/data_control/zwlr_data_control_offer_v1.rs +++ b/src/ifs/ipc/data_control/zwlr_data_control_offer_v1.rs @@ -1,16 +1,11 @@ use { crate::{ - client::{Client, ClientError, ClientId}, - ifs::{ - ipc::{ - break_offer_loops, cancel_offer, - data_control::zwlr_data_control_device_v1::{ - WlrClipboardIpc, WlrPrimarySelectionIpc, ZwlrDataControlDeviceV1, - }, - destroy_data_offer, receive_data_offer, DataOffer, DataOfferId, DynDataOffer, - IpcLocation, OfferData, + ifs::ipc::data_control::{ + private::{ + logic::{self, DataControlError}, + DataControlOffer, DataControlOfferData, }, - wl_seat::WlSeatGlobal, + zwlr_data_control_device_v1::WlrDataControlIpc, }, leaks::Tracker, object::Object, @@ -22,54 +17,25 @@ use { pub struct ZwlrDataControlOfferV1 { pub id: ZwlrDataControlOfferV1Id, - pub offer_id: DataOfferId, - pub client: Rc, - pub device: Rc, - pub data: OfferData, - pub location: IpcLocation, + pub data: DataControlOfferData, pub tracker: Tracker, } -impl DataOffer for ZwlrDataControlOfferV1 { - type Device = ZwlrDataControlDeviceV1; +impl DataControlOffer for ZwlrDataControlOfferV1 { + type Ipc = WlrDataControlIpc; - fn offer_data(&self) -> &OfferData { + fn data(&self) -> &DataControlOfferData { &self.data } -} - -impl DynDataOffer for ZwlrDataControlOfferV1 { - fn offer_id(&self) -> DataOfferId { - self.offer_id - } - - fn client_id(&self) -> ClientId { - self.client.id - } fn send_offer(&self, mime_type: &str) { - ZwlrDataControlOfferV1::send_offer(self, mime_type) - } - - fn cancel(&self) { - match self.location { - IpcLocation::Clipboard => cancel_offer::(self), - IpcLocation::PrimarySelection => cancel_offer::(self), - } - } - - fn get_seat(&self) -> Rc { - self.device.seat.clone() - } - - fn is_privileged(&self) -> bool { - true + self.send_offer(mime_type); } } impl ZwlrDataControlOfferV1 { pub fn send_offer(&self, mime_type: &str) { - self.client.event(Offer { + self.data.client.event(Offer { self_id: self.id, mime_type, }) @@ -80,38 +46,24 @@ impl ZwlrDataControlOfferV1RequestHandler for ZwlrDataControlOfferV1 { type Error = ZwlrDataControlOfferV1Error; fn receive(&self, req: Receive, _slf: &Rc) -> Result<(), Self::Error> { - match self.location { - IpcLocation::Clipboard => { - receive_data_offer::(self, req.mime_type, req.fd) - } - IpcLocation::PrimarySelection => { - receive_data_offer::(self, req.mime_type, req.fd) - } - } + logic::data_offer_receive(self, req.mime_type, req.fd); Ok(()) } fn destroy(&self, _req: Destroy, _slf: &Rc) -> Result<(), Self::Error> { - match self.location { - IpcLocation::Clipboard => destroy_data_offer::(self), - IpcLocation::PrimarySelection => destroy_data_offer::(self), - } - self.client.remove_obj(self)?; + logic::data_offer_destroy(self)?; Ok(()) } } object_base! { self = ZwlrDataControlOfferV1; - version = self.device.version; + version = self.data.device.data.version; } impl Object for ZwlrDataControlOfferV1 { fn break_loops(&self) { - match self.location { - IpcLocation::Clipboard => break_offer_loops::(self), - IpcLocation::PrimarySelection => break_offer_loops::(self), - } + logic::data_offer_break_loops(self); } } @@ -120,6 +72,5 @@ simple_add_obj!(ZwlrDataControlOfferV1); #[derive(Debug, Error)] pub enum ZwlrDataControlOfferV1Error { #[error(transparent)] - ClientError(Box), + DataControlError(#[from] DataControlError), } -efrom!(ZwlrDataControlOfferV1Error, ClientError); diff --git a/src/ifs/ipc/data_control/zwlr_data_control_source_v1.rs b/src/ifs/ipc/data_control/zwlr_data_control_source_v1.rs index 08488bd9..2186bd9a 100644 --- a/src/ifs/ipc/data_control/zwlr_data_control_source_v1.rs +++ b/src/ifs/ipc/data_control/zwlr_data_control_source_v1.rs @@ -1,17 +1,15 @@ use { crate::{ - client::{Client, ClientError}, - ifs::{ - ipc::{ - add_data_source_mime_type, break_source_loops, cancel_offers, - data_control::zwlr_data_control_device_v1::{ - WlrClipboardIpc, WlrPrimarySelectionIpc, + client::Client, + ifs::ipc::{ + data_control::{ + private::{ + logic::{self, DataControlError}, + DataControlSource, DataControlSourceData, }, - destroy_data_source, detach_seat, offer_source_to_x, - x_data_device::{XClipboardIpc, XIpcDevice, XPrimarySelectionIpc}, - DataSource, DynDataSource, IpcLocation, SourceData, + zwlr_data_control_device_v1::WlrDataControlIpc, }, - wl_seat::WlSeatGlobal, + IpcLocation, SourceData, }, leaks::Tracker, object::{Object, Version}, @@ -24,41 +22,23 @@ use { pub struct ZwlrDataControlSourceV1 { pub id: ZwlrDataControlSourceV1Id, - pub data: SourceData, - pub version: Version, - pub location: Cell, - pub used: Cell, + pub data: DataControlSourceData, pub tracker: Tracker, } -impl DataSource for ZwlrDataControlSourceV1 { - fn send_cancelled(&self, _seat: &Rc) { - ZwlrDataControlSourceV1::send_cancelled(self); - } -} +impl DataControlSource for ZwlrDataControlSourceV1 { + type Ipc = WlrDataControlIpc; -impl DynDataSource for ZwlrDataControlSourceV1 { - fn source_data(&self) -> &SourceData { + fn data(&self) -> &DataControlSourceData { &self.data } - fn send_send(&self, mime_type: &str, fd: Rc) { - ZwlrDataControlSourceV1::send_send(&self, mime_type, fd); + fn send_cancelled(&self) { + self.send_cancelled(); } - fn offer_to_x(self: Rc, dd: &Rc) { - match self.location.get() { - IpcLocation::Clipboard => offer_source_to_x::(self, dd), - IpcLocation::PrimarySelection => offer_source_to_x::(self, dd), - } - } - - fn detach_seat(&self, seat: &Rc) { - detach_seat(self, seat) - } - - fn cancel_unprivileged_offers(&self) { - cancel_offers(self, false) + fn send_send(&self, mime_type: &str, fd: Rc) { + self.send_send(mime_type, fd); } } @@ -66,16 +46,18 @@ impl ZwlrDataControlSourceV1 { pub fn new(id: ZwlrDataControlSourceV1Id, client: &Rc, version: Version) -> Self { Self { id, + data: DataControlSourceData { + data: SourceData::new(client), + version, + location: Cell::new(IpcLocation::Clipboard), + used: Cell::new(false), + }, tracker: Default::default(), - data: SourceData::new(client), - version, - location: Cell::new(IpcLocation::Clipboard), - used: Cell::new(false), } } pub fn send_send(&self, mime_type: &str, fd: Rc) { - self.data.client.event(Send { + self.data.data.client.event(Send { self_id: self.id, mime_type, fd, @@ -83,7 +65,7 @@ impl ZwlrDataControlSourceV1 { } pub fn send_cancelled(&self) { - self.data.client.event(Cancelled { self_id: self.id }) + self.data.data.client.event(Cancelled { self_id: self.id }) } } @@ -91,34 +73,24 @@ impl ZwlrDataControlSourceV1RequestHandler for ZwlrDataControlSourceV1 { type Error = ZwlrDataControlSourceV1Error; fn offer(&self, req: Offer, _slf: &Rc) -> Result<(), Self::Error> { - if self.used.get() { - return Err(ZwlrDataControlSourceV1Error::AlreadyUsed); - } - add_data_source_mime_type::(self, req.mime_type); + logic::data_source_offer(self, req.mime_type)?; Ok(()) } fn destroy(&self, _req: Destroy, _slf: &Rc) -> Result<(), Self::Error> { - match self.location.get() { - IpcLocation::Clipboard => destroy_data_source::(self), - IpcLocation::PrimarySelection => destroy_data_source::(self), - } - self.data.client.remove_obj(self)?; + logic::data_source_destroy(self)?; Ok(()) } } object_base! { self = ZwlrDataControlSourceV1; - version = self.version; + version = self.data.version; } impl Object for ZwlrDataControlSourceV1 { fn break_loops(&self) { - match self.location.get() { - IpcLocation::Clipboard => break_source_loops::(self), - IpcLocation::PrimarySelection => break_source_loops::(self), - } + logic::data_source_break_loops(self); } } @@ -131,8 +103,5 @@ dedicated_add_obj!( #[derive(Debug, Error)] pub enum ZwlrDataControlSourceV1Error { #[error(transparent)] - ClientError(Box), - #[error("The source has already been used")] - AlreadyUsed, + DataControlError(#[from] DataControlError), } -efrom!(ZwlrDataControlSourceV1Error, ClientError); From 4abbe94995cd79736049d75d140b0c9edfe00ecd Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Tue, 8 Oct 2024 16:07:31 +0200 Subject: [PATCH 5/5] ipc: implement ext-data-control --- docs/features.md | 1 + release-notes.md | 2 + src/client/objects.rs | 10 +- src/globals.rs | 6 +- src/ifs/ipc/data_control.rs | 4 + .../ext_data_control_device_v1.rs | 158 ++++++++++++++++++ .../ext_data_control_manager_v1.rs | 134 +++++++++++++++ .../data_control/ext_data_control_offer_v1.rs | 76 +++++++++ .../ext_data_control_source_v1.rs | 107 ++++++++++++ wire/ext_data_control_device_v1.txt | 30 ++++ wire/ext_data_control_manager_v1.txt | 14 ++ wire/ext_data_control_offer_v1.txt | 16 ++ wire/ext_data_control_source_v1.txt | 20 +++ 13 files changed, 575 insertions(+), 3 deletions(-) create mode 100644 src/ifs/ipc/data_control/ext_data_control_device_v1.rs create mode 100644 src/ifs/ipc/data_control/ext_data_control_manager_v1.rs create mode 100644 src/ifs/ipc/data_control/ext_data_control_offer_v1.rs create mode 100644 src/ifs/ipc/data_control/ext_data_control_source_v1.rs create mode 100644 wire/ext_data_control_device_v1.txt create mode 100644 wire/ext_data_control_manager_v1.txt create mode 100644 wire/ext_data_control_offer_v1.txt create mode 100644 wire/ext_data_control_source_v1.txt diff --git a/docs/features.md b/docs/features.md index fbb83d60..a726196d 100644 --- a/docs/features.md +++ b/docs/features.md @@ -140,6 +140,7 @@ Jay supports the following wayland protocols: | Global | Version | Privileged | |------------------------------------------------------|:----------------|---------------| +| ext_data_control_manager_v1 | 1 | Yes | | ext_foreign_toplevel_image_capture_source_manager_v1 | 1 | | | ext_foreign_toplevel_list_v1 | 1 | Yes | | ext_idle_notifier_v1 | 1 | Yes | diff --git a/release-notes.md b/release-notes.md index bdc758e7..442abccf 100644 --- a/release-notes.md +++ b/release-notes.md @@ -1,5 +1,7 @@ # Unreleased +- Add support fo ext-data-control-v1. + # 1.7.0 (2024-10-25) - Various bugfixes. diff --git a/src/client/objects.rs b/src/client/objects.rs index 1ebd3ada..65122be3 100644 --- a/src/client/objects.rs +++ b/src/client/objects.rs @@ -6,7 +6,10 @@ use { ext_image_capture_source_v1::ExtImageCaptureSourceV1, ext_image_copy::ext_image_copy_capture_session_v1::ExtImageCopyCaptureSessionV1, ipc::{ - data_control::zwlr_data_control_source_v1::ZwlrDataControlSourceV1, + data_control::{ + ext_data_control_source_v1::ExtDataControlSourceV1, + zwlr_data_control_source_v1::ZwlrDataControlSourceV1, + }, wl_data_source::WlDataSource, zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1, }, @@ -35,7 +38,7 @@ use { copyhashmap::{CopyHashMap, Locked}, }, wire::{ - ExtForeignToplevelHandleV1Id, ExtImageCaptureSourceV1Id, + ExtDataControlSourceV1Id, ExtForeignToplevelHandleV1Id, ExtImageCaptureSourceV1Id, ExtImageCopyCaptureSessionV1Id, JayOutputId, JayScreencastId, JayToplevelId, JayWorkspaceId, WlBufferId, WlDataSourceId, WlOutputId, WlPointerId, WlRegionId, WlRegistryId, WlSeatId, WlSurfaceId, WpDrmLeaseConnectorV1Id, @@ -78,6 +81,7 @@ pub struct Objects { CopyHashMap>, pub ext_copy_sessions: CopyHashMap>, + pub ext_data_sources: CopyHashMap>, ids: RefCell>, } @@ -114,6 +118,7 @@ impl Objects { image_capture_sources: Default::default(), foreign_toplevel_handles: Default::default(), ext_copy_sessions: Default::default(), + ext_data_sources: Default::default(), ids: RefCell::new(vec![]), } } @@ -154,6 +159,7 @@ impl Objects { self.image_capture_sources.clear(); self.foreign_toplevel_handles.clear(); self.ext_copy_sessions.clear(); + self.ext_data_sources.clear(); } pub fn id(&self, client_data: &Client) -> Result diff --git a/src/globals.rs b/src/globals.rs index b541cb1e..2cc15acb 100644 --- a/src/globals.rs +++ b/src/globals.rs @@ -10,7 +10,10 @@ use { ext_output_image_capture_source_manager_v1::ExtOutputImageCaptureSourceManagerV1Global, ext_session_lock_manager_v1::ExtSessionLockManagerV1Global, ipc::{ - data_control::zwlr_data_control_manager_v1::ZwlrDataControlManagerV1Global, + data_control::{ + ext_data_control_manager_v1::ExtDataControlManagerV1Global, + zwlr_data_control_manager_v1::ZwlrDataControlManagerV1Global, + }, wl_data_device_manager::WlDataDeviceManagerGlobal, zwp_primary_selection_device_manager_v1::ZwpPrimarySelectionDeviceManagerV1Global, }, @@ -207,6 +210,7 @@ impl Globals { add_singleton!(ExtImageCopyCaptureManagerV1Global); add_singleton!(WpFifoManagerV1Global); add_singleton!(WpCommitTimingManagerV1Global); + add_singleton!(ExtDataControlManagerV1Global); } pub fn add_backend_singletons(&self, backend: &Rc) { diff --git a/src/ifs/ipc/data_control.rs b/src/ifs/ipc/data_control.rs index 20c3418d..26fe691c 100644 --- a/src/ifs/ipc/data_control.rs +++ b/src/ifs/ipc/data_control.rs @@ -3,6 +3,10 @@ use { std::rc::Rc, }; +pub mod ext_data_control_device_v1; +pub mod ext_data_control_manager_v1; +pub mod ext_data_control_offer_v1; +pub mod ext_data_control_source_v1; mod private; pub mod zwlr_data_control_device_v1; pub mod zwlr_data_control_manager_v1; diff --git a/src/ifs/ipc/data_control/ext_data_control_device_v1.rs b/src/ifs/ipc/data_control/ext_data_control_device_v1.rs new file mode 100644 index 00000000..b498fb6a --- /dev/null +++ b/src/ifs/ipc/data_control/ext_data_control_device_v1.rs @@ -0,0 +1,158 @@ +use { + crate::{ + client::Client, + ifs::{ + ipc::data_control::{ + ext_data_control_offer_v1::ExtDataControlOfferV1, + ext_data_control_source_v1::ExtDataControlSourceV1, + private::{ + logic::{self, DataControlError}, + DataControlDevice, DataControlDeviceData, DataControlIpc, DataControlOfferData, + }, + }, + wl_seat::WlSeatGlobal, + }, + leaks::Tracker, + object::{Object, Version}, + wire::{ + ext_data_control_device_v1::*, ExtDataControlDeviceV1Id, ExtDataControlOfferV1Id, + ExtDataControlSourceV1Id, + }, + }, + std::rc::Rc, + thiserror::Error, +}; + +pub struct ExtDataControlDeviceV1 { + pub id: ExtDataControlDeviceV1Id, + pub data: DataControlDeviceData, + pub tracker: Tracker, +} + +impl ExtDataControlDeviceV1 { + pub fn new( + id: ExtDataControlDeviceV1Id, + client: &Rc, + version: Version, + seat: &Rc, + ) -> Self { + Self { + id, + data: DataControlDeviceData { + data_control_device_id: client.state.data_control_device_ids.next(), + client: client.clone(), + version, + seat: seat.clone(), + clipboard_data: Default::default(), + primary_selection_data: Default::default(), + }, + tracker: Default::default(), + } + } + + pub fn send_data_offer(&self, offer: &Rc) { + self.data.client.event(DataOffer { + self_id: self.id, + id: offer.id, + }) + } + + pub fn send_selection(&self, offer: Option<&Rc>) { + let id = offer.map(|o| o.id).unwrap_or(ExtDataControlOfferV1Id::NONE); + self.data.client.event(Selection { + self_id: self.id, + id, + }) + } + + pub fn send_primary_selection(&self, offer: Option<&Rc>) { + let id = offer.map(|o| o.id).unwrap_or(ExtDataControlOfferV1Id::NONE); + self.data.client.event(PrimarySelection { + self_id: self.id, + id, + }) + } +} + +impl ExtDataControlDeviceV1RequestHandler for ExtDataControlDeviceV1 { + type Error = ExtDataControlDeviceV1Error; + + fn set_selection(&self, req: SetSelection, _slf: &Rc) -> Result<(), Self::Error> { + logic::device_set_selection(self, req.source.is_some().then_some(req.source))?; + Ok(()) + } + + fn destroy(&self, _req: Destroy, _slf: &Rc) -> Result<(), Self::Error> { + logic::device_destroy(self)?; + Ok(()) + } + + fn set_primary_selection( + &self, + req: SetPrimarySelection, + _slf: &Rc, + ) -> Result<(), Self::Error> { + logic::device_set_primary_selection(self, req.source.is_some().then_some(req.source))?; + Ok(()) + } +} + +pub struct ExtDataControlIpc; + +impl DataControlIpc for ExtDataControlIpc { + const PRIMARY_SELECTION_SINCE: Version = Version(1); + type Device = ExtDataControlDeviceV1; + type OfferId = ExtDataControlOfferV1Id; + type Offer = ExtDataControlOfferV1; + type SourceId = ExtDataControlSourceV1Id; + type Source = ExtDataControlSourceV1; + + fn create_offer(id: Self::OfferId, data: DataControlOfferData) -> Rc { + let rc = Rc::new(ExtDataControlOfferV1 { + id, + data, + tracker: Default::default(), + }); + track!(rc.data.client, rc); + rc + } +} + +impl DataControlDevice for ExtDataControlDeviceV1 { + type Ipc = ExtDataControlIpc; + + fn data(&self) -> &DataControlDeviceData { + &self.data + } + + fn send_data_offer(&self, offer: &Rc<::Offer>) { + self.send_data_offer(offer) + } + + fn send_selection(&self, offer: Option<&Rc<::Offer>>) { + self.send_selection(offer) + } + + fn send_primary_selection(&self, offer: Option<&Rc<::Offer>>) { + self.send_primary_selection(offer) + } +} + +object_base! { + self = ExtDataControlDeviceV1; + version = self.data.version; +} + +impl Object for ExtDataControlDeviceV1 { + fn break_loops(&self) { + logic::data_device_break_loops(self); + } +} + +simple_add_obj!(ExtDataControlDeviceV1); + +#[derive(Debug, Error)] +pub enum ExtDataControlDeviceV1Error { + #[error(transparent)] + DataControlError(#[from] DataControlError), +} diff --git a/src/ifs/ipc/data_control/ext_data_control_manager_v1.rs b/src/ifs/ipc/data_control/ext_data_control_manager_v1.rs new file mode 100644 index 00000000..845d0d88 --- /dev/null +++ b/src/ifs/ipc/data_control/ext_data_control_manager_v1.rs @@ -0,0 +1,134 @@ +use { + crate::{ + client::{Client, ClientCaps, ClientError, CAP_DATA_CONTROL_MANAGER}, + globals::{Global, GlobalName}, + ifs::ipc::{ + data_control::{ + ext_data_control_device_v1::ExtDataControlDeviceV1, + ext_data_control_source_v1::ExtDataControlSourceV1, DynDataControlDevice, + }, + IpcLocation, + }, + leaks::Tracker, + object::{Object, Version}, + wire::{ext_data_control_manager_v1::*, ExtDataControlManagerV1Id}, + }, + std::rc::Rc, + thiserror::Error, +}; + +pub struct ExtDataControlManagerV1Global { + name: GlobalName, +} + +pub struct ExtDataControlManagerV1 { + pub id: ExtDataControlManagerV1Id, + pub client: Rc, + pub version: Version, + tracker: Tracker, +} + +impl ExtDataControlManagerV1Global { + pub fn new(name: GlobalName) -> Self { + Self { name } + } + + fn bind_( + self: Rc, + id: ExtDataControlManagerV1Id, + client: &Rc, + version: Version, + ) -> Result<(), ExtDataControlManagerV1Error> { + let obj = Rc::new(ExtDataControlManagerV1 { + id, + client: client.clone(), + version, + tracker: Default::default(), + }); + track!(client, obj); + client.add_client_obj(&obj)?; + Ok(()) + } +} + +impl ExtDataControlManagerV1RequestHandler for ExtDataControlManagerV1 { + type Error = ExtDataControlManagerV1Error; + + fn create_data_source( + &self, + req: CreateDataSource, + _slf: &Rc, + ) -> Result<(), Self::Error> { + let res = Rc::new(ExtDataControlSourceV1::new( + req.id, + &self.client, + self.version, + )); + track!(self.client, res); + self.client.add_client_obj(&res)?; + Ok(()) + } + + fn get_data_device(&self, req: GetDataDevice, _slf: &Rc) -> Result<(), Self::Error> { + let seat = self.client.lookup(req.seat)?; + let dev = Rc::new(ExtDataControlDeviceV1::new( + req.id, + &self.client, + self.version, + &seat.global, + )); + track!(self.client, dev); + seat.global.add_data_control_device(dev.clone()); + self.client.add_client_obj(&dev)?; + dev.clone() + .handle_new_source(IpcLocation::Clipboard, seat.global.get_selection()); + dev.clone().handle_new_source( + IpcLocation::PrimarySelection, + seat.global.get_primary_selection(), + ); + Ok(()) + } + + fn destroy(&self, _req: Destroy, _slf: &Rc) -> Result<(), Self::Error> { + self.client.remove_obj(self)?; + Ok(()) + } +} + +global_base!( + ExtDataControlManagerV1Global, + ExtDataControlManagerV1, + ExtDataControlManagerV1Error +); + +impl Global for ExtDataControlManagerV1Global { + fn singleton(&self) -> bool { + true + } + + fn version(&self) -> u32 { + 1 + } + + fn required_caps(&self) -> ClientCaps { + CAP_DATA_CONTROL_MANAGER + } +} + +simple_add_global!(ExtDataControlManagerV1Global); + +object_base! { + self = ExtDataControlManagerV1; + version = self.version; +} + +impl Object for ExtDataControlManagerV1 {} + +simple_add_obj!(ExtDataControlManagerV1); + +#[derive(Debug, Error)] +pub enum ExtDataControlManagerV1Error { + #[error(transparent)] + ClientError(Box), +} +efrom!(ExtDataControlManagerV1Error, ClientError); diff --git a/src/ifs/ipc/data_control/ext_data_control_offer_v1.rs b/src/ifs/ipc/data_control/ext_data_control_offer_v1.rs new file mode 100644 index 00000000..f401b227 --- /dev/null +++ b/src/ifs/ipc/data_control/ext_data_control_offer_v1.rs @@ -0,0 +1,76 @@ +use { + crate::{ + ifs::ipc::data_control::{ + ext_data_control_device_v1::ExtDataControlIpc, + private::{ + logic::{self, DataControlError}, + DataControlOffer, DataControlOfferData, + }, + }, + leaks::Tracker, + object::Object, + wire::{ext_data_control_offer_v1::*, ExtDataControlOfferV1Id}, + }, + std::rc::Rc, + thiserror::Error, +}; + +pub struct ExtDataControlOfferV1 { + pub id: ExtDataControlOfferV1Id, + pub data: DataControlOfferData, + pub tracker: Tracker, +} + +impl DataControlOffer for ExtDataControlOfferV1 { + type Ipc = ExtDataControlIpc; + + fn data(&self) -> &DataControlOfferData { + &self.data + } + + fn send_offer(&self, mime_type: &str) { + self.send_offer(mime_type); + } +} + +impl ExtDataControlOfferV1 { + pub fn send_offer(&self, mime_type: &str) { + self.data.client.event(Offer { + self_id: self.id, + mime_type, + }) + } +} + +impl ExtDataControlOfferV1RequestHandler for ExtDataControlOfferV1 { + type Error = ExtDataControlOfferV1Error; + + fn receive(&self, req: Receive, _slf: &Rc) -> Result<(), Self::Error> { + logic::data_offer_receive(self, req.mime_type, req.fd); + Ok(()) + } + + fn destroy(&self, _req: Destroy, _slf: &Rc) -> Result<(), Self::Error> { + logic::data_offer_destroy(self)?; + Ok(()) + } +} + +object_base! { + self = ExtDataControlOfferV1; + version = self.data.device.data.version; +} + +impl Object for ExtDataControlOfferV1 { + fn break_loops(&self) { + logic::data_offer_break_loops(self); + } +} + +simple_add_obj!(ExtDataControlOfferV1); + +#[derive(Debug, Error)] +pub enum ExtDataControlOfferV1Error { + #[error(transparent)] + DataControlError(#[from] DataControlError), +} diff --git a/src/ifs/ipc/data_control/ext_data_control_source_v1.rs b/src/ifs/ipc/data_control/ext_data_control_source_v1.rs new file mode 100644 index 00000000..fdc0ed1d --- /dev/null +++ b/src/ifs/ipc/data_control/ext_data_control_source_v1.rs @@ -0,0 +1,107 @@ +use { + crate::{ + client::Client, + ifs::ipc::{ + data_control::{ + ext_data_control_device_v1::ExtDataControlIpc, + private::{ + logic::{self, DataControlError}, + DataControlSource, DataControlSourceData, + }, + }, + IpcLocation, SourceData, + }, + leaks::Tracker, + object::{Object, Version}, + wire::{ext_data_control_source_v1::*, ExtDataControlSourceV1Id}, + }, + std::{cell::Cell, rc::Rc}, + thiserror::Error, + uapi::OwnedFd, +}; + +pub struct ExtDataControlSourceV1 { + pub id: ExtDataControlSourceV1Id, + pub data: DataControlSourceData, + pub tracker: Tracker, +} + +impl DataControlSource for ExtDataControlSourceV1 { + type Ipc = ExtDataControlIpc; + + fn data(&self) -> &DataControlSourceData { + &self.data + } + + fn send_cancelled(&self) { + self.send_cancelled(); + } + + fn send_send(&self, mime_type: &str, fd: Rc) { + self.send_send(mime_type, fd); + } +} + +impl ExtDataControlSourceV1 { + pub fn new(id: ExtDataControlSourceV1Id, client: &Rc, version: Version) -> Self { + Self { + id, + data: DataControlSourceData { + data: SourceData::new(client), + version, + location: Cell::new(IpcLocation::Clipboard), + used: Cell::new(false), + }, + tracker: Default::default(), + } + } + + pub fn send_send(&self, mime_type: &str, fd: Rc) { + self.data.data.client.event(Send { + self_id: self.id, + mime_type, + fd, + }) + } + + pub fn send_cancelled(&self) { + self.data.data.client.event(Cancelled { self_id: self.id }) + } +} + +impl ExtDataControlSourceV1RequestHandler for ExtDataControlSourceV1 { + type Error = ExtDataControlSourceV1Error; + + fn offer(&self, req: Offer, _slf: &Rc) -> Result<(), Self::Error> { + logic::data_source_offer(self, req.mime_type)?; + Ok(()) + } + + fn destroy(&self, _req: Destroy, _slf: &Rc) -> Result<(), Self::Error> { + logic::data_source_destroy(self)?; + Ok(()) + } +} + +object_base! { + self = ExtDataControlSourceV1; + version = self.data.version; +} + +impl Object for ExtDataControlSourceV1 { + fn break_loops(&self) { + logic::data_source_break_loops(self); + } +} + +dedicated_add_obj!( + ExtDataControlSourceV1, + ExtDataControlSourceV1Id, + ext_data_sources +); + +#[derive(Debug, Error)] +pub enum ExtDataControlSourceV1Error { + #[error(transparent)] + DataControlError(#[from] DataControlError), +} diff --git a/wire/ext_data_control_device_v1.txt b/wire/ext_data_control_device_v1.txt new file mode 100644 index 00000000..5652eeef --- /dev/null +++ b/wire/ext_data_control_device_v1.txt @@ -0,0 +1,30 @@ +# requests + +request set_selection { + source: id(ext_data_control_source_v1), +} + +request destroy { + +} + +request set_primary_selection { + source: id(ext_data_control_source_v1), +} + +# events + +event data_offer { + id: id(ext_data_control_offer_v1), +} + +event selection { + id: id(ext_data_control_offer_v1), +} + +event finished { +} + +event primary_selection { + id: id(ext_data_control_offer_v1), +} diff --git a/wire/ext_data_control_manager_v1.txt b/wire/ext_data_control_manager_v1.txt new file mode 100644 index 00000000..096b01fa --- /dev/null +++ b/wire/ext_data_control_manager_v1.txt @@ -0,0 +1,14 @@ +# requests + +request create_data_source { + id: id(ext_data_control_source_v1), +} + +request get_data_device { + id: id(ext_data_control_device_v1), + seat: id(wl_seat), +} + +request destroy { + +} diff --git a/wire/ext_data_control_offer_v1.txt b/wire/ext_data_control_offer_v1.txt new file mode 100644 index 00000000..e7dd04d4 --- /dev/null +++ b/wire/ext_data_control_offer_v1.txt @@ -0,0 +1,16 @@ +# requests + +request receive { + mime_type: str, + fd: fd, +} + +request destroy { + +} + +# events + +event offer { + mime_type: str, +} diff --git a/wire/ext_data_control_source_v1.txt b/wire/ext_data_control_source_v1.txt new file mode 100644 index 00000000..92689834 --- /dev/null +++ b/wire/ext_data_control_source_v1.txt @@ -0,0 +1,20 @@ +# requests + +request offer { + mime_type: str, +} + +request destroy { + +} + +# events + +event send { + mime_type: str, + fd: fd, +} + +event cancelled { + +}