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 5fade5f4..65122be3 100644 --- a/src/client/objects.rs +++ b/src/client/objects.rs @@ -6,7 +6,11 @@ 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::{ + ext_data_control_source_v1::ExtDataControlSourceV1, + zwlr_data_control_source_v1::ZwlrDataControlSourceV1, + }, + wl_data_source::WlDataSource, zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1, }, jay_output::JayOutput, @@ -34,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, @@ -77,6 +81,7 @@ pub struct Objects { CopyHashMap>, pub ext_copy_sessions: CopyHashMap>, + pub ext_data_sources: CopyHashMap>, ids: RefCell>, } @@ -113,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![]), } } @@ -153,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/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..2cc15acb 100644 --- a/src/globals.rs +++ b/src/globals.rs @@ -10,8 +10,11 @@ use { ext_output_image_capture_source_manager_v1::ExtOutputImageCaptureSourceManagerV1Global, ext_session_lock_manager_v1::ExtSessionLockManagerV1Global, ipc::{ + data_control::{ + ext_data_control_manager_v1::ExtDataControlManagerV1Global, + 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, @@ -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.rs b/src/ifs/ipc.rs index 0def2a61..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::{WlSeatError, 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; @@ -64,9 +56,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); @@ -139,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; @@ -152,11 +136,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, @@ -320,8 +299,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, @@ -356,31 +335,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_data_control_device(src: Rc, dd: &Rc) where - T: IpcVtable, - S: DynDataSource, + T: IpcVtable, { 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(); @@ -395,7 +372,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/data_control.rs b/src/ifs/ipc/data_control.rs new file mode 100644 index 00000000..26fe691c --- /dev/null +++ b/src/ifs/ipc/data_control.rs @@ -0,0 +1,26 @@ +use { + crate::ifs::ipc::{DynDataSource, IpcLocation}, + 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; +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/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/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 new file mode 100644 index 00000000..2c12aedf --- /dev/null +++ b/src/ifs/ipc/data_control/zwlr_data_control_device_v1.rs @@ -0,0 +1,164 @@ +use { + crate::{ + client::Client, + ifs::{ + ipc::data_control::{ + private::{ + logic::{self, DataControlError}, + DataControlDevice, DataControlDeviceData, DataControlIpc, DataControlOfferData, + }, + zwlr_data_control_offer_v1::ZwlrDataControlOfferV1, + zwlr_data_control_source_v1::ZwlrDataControlSourceV1, + }, + wl_seat::WlSeatGlobal, + }, + leaks::Tracker, + object::{Object, Version}, + wire::{ + zwlr_data_control_device_v1::*, ZwlrDataControlDeviceV1Id, ZwlrDataControlOfferV1Id, + ZwlrDataControlSourceV1Id, + }, + }, + std::rc::Rc, + thiserror::Error, +}; + +pub const PRIMARY_SELECTION_SINCE: Version = Version(2); + +pub struct ZwlrDataControlDeviceV1 { + pub id: ZwlrDataControlDeviceV1Id, + pub data: DataControlDeviceData, + pub tracker: Tracker, +} + +impl ZwlrDataControlDeviceV1 { + pub fn new( + id: ZwlrDataControlDeviceV1Id, + 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(ZwlrDataControlOfferV1Id::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(ZwlrDataControlOfferV1Id::NONE); + self.data.client.event(PrimarySelection { + self_id: self.id, + id, + }) + } +} + +impl ZwlrDataControlDeviceV1RequestHandler for ZwlrDataControlDeviceV1 { + type Error = ZwlrDataControlDeviceV1Error; + + 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 WlrDataControlIpc; + +impl DataControlIpc for WlrDataControlIpc { + const PRIMARY_SELECTION_SINCE: Version = PRIMARY_SELECTION_SINCE; + type Device = ZwlrDataControlDeviceV1; + type OfferId = ZwlrDataControlOfferV1Id; + type Offer = ZwlrDataControlOfferV1; + type SourceId = ZwlrDataControlSourceV1Id; + type Source = ZwlrDataControlSourceV1; + + fn create_offer(id: Self::OfferId, data: DataControlOfferData) -> Rc { + let rc = Rc::new(ZwlrDataControlOfferV1 { + id, + data, + tracker: Default::default(), + }); + track!(rc.data.client, rc); + rc + } +} + +impl DataControlDevice for ZwlrDataControlDeviceV1 { + type Ipc = WlrDataControlIpc; + + 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 = ZwlrDataControlDeviceV1; + version = self.data.version; +} + +impl Object for ZwlrDataControlDeviceV1 { + fn break_loops(&self) { + logic::data_device_break_loops(self); + } +} + +simple_add_obj!(ZwlrDataControlDeviceV1); + +#[derive(Debug, Error)] +pub enum ZwlrDataControlDeviceV1Error { + #[error(transparent)] + DataControlError(#[from] DataControlError), +} 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 83% 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 ad5ce34f..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,8 +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}, - zwlr_data_control_source_v1::ZwlrDataControlSourceV1, + data_control::{ + zwlr_data_control_device_v1::ZwlrDataControlDeviceV1, + zwlr_data_control_source_v1::ZwlrDataControlSourceV1, DynDataControlDevice, + }, + IpcLocation, }, leaks::Tracker, object::{Object, Version}, @@ -75,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) => s.offer_to_wlr_device(&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), - _ => 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/data_control/zwlr_data_control_offer_v1.rs b/src/ifs/ipc/data_control/zwlr_data_control_offer_v1.rs new file mode 100644 index 00000000..df9a61a8 --- /dev/null +++ b/src/ifs/ipc/data_control/zwlr_data_control_offer_v1.rs @@ -0,0 +1,76 @@ +use { + crate::{ + ifs::ipc::data_control::{ + private::{ + logic::{self, DataControlError}, + DataControlOffer, DataControlOfferData, + }, + zwlr_data_control_device_v1::WlrDataControlIpc, + }, + leaks::Tracker, + object::Object, + wire::{zwlr_data_control_offer_v1::*, ZwlrDataControlOfferV1Id}, + }, + std::rc::Rc, + thiserror::Error, +}; + +pub struct ZwlrDataControlOfferV1 { + pub id: ZwlrDataControlOfferV1Id, + pub data: DataControlOfferData, + pub tracker: Tracker, +} + +impl DataControlOffer for ZwlrDataControlOfferV1 { + type Ipc = WlrDataControlIpc; + + fn data(&self) -> &DataControlOfferData { + &self.data + } + + fn send_offer(&self, mime_type: &str) { + self.send_offer(mime_type); + } +} + +impl ZwlrDataControlOfferV1 { + pub fn send_offer(&self, mime_type: &str) { + self.data.client.event(Offer { + self_id: self.id, + mime_type, + }) + } +} + +impl ZwlrDataControlOfferV1RequestHandler for ZwlrDataControlOfferV1 { + type Error = ZwlrDataControlOfferV1Error; + + 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 = ZwlrDataControlOfferV1; + version = self.data.device.data.version; +} + +impl Object for ZwlrDataControlOfferV1 { + fn break_loops(&self) { + logic::data_offer_break_loops(self); + } +} + +simple_add_obj!(ZwlrDataControlOfferV1); + +#[derive(Debug, Error)] +pub enum ZwlrDataControlOfferV1Error { + #[error(transparent)] + DataControlError(#[from] DataControlError), +} 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 new file mode 100644 index 00000000..2186bd9a --- /dev/null +++ b/src/ifs/ipc/data_control/zwlr_data_control_source_v1.rs @@ -0,0 +1,107 @@ +use { + crate::{ + client::Client, + ifs::ipc::{ + data_control::{ + private::{ + logic::{self, DataControlError}, + DataControlSource, DataControlSourceData, + }, + zwlr_data_control_device_v1::WlrDataControlIpc, + }, + IpcLocation, SourceData, + }, + leaks::Tracker, + object::{Object, Version}, + wire::{zwlr_data_control_source_v1::*, ZwlrDataControlSourceV1Id}, + }, + std::{cell::Cell, rc::Rc}, + thiserror::Error, + uapi::OwnedFd, +}; + +pub struct ZwlrDataControlSourceV1 { + pub id: ZwlrDataControlSourceV1Id, + pub data: DataControlSourceData, + pub tracker: Tracker, +} + +impl DataControlSource for ZwlrDataControlSourceV1 { + type Ipc = WlrDataControlIpc; + + 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 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(), + } + } + + 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 ZwlrDataControlSourceV1RequestHandler for ZwlrDataControlSourceV1 { + type Error = ZwlrDataControlSourceV1Error; + + 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 = ZwlrDataControlSourceV1; + version = self.data.version; +} + +impl Object for ZwlrDataControlSourceV1 { + fn break_loops(&self) { + logic::data_source_break_loops(self); + } +} + +dedicated_add_obj!( + ZwlrDataControlSourceV1, + ZwlrDataControlSourceV1Id, + zwlr_data_sources +); + +#[derive(Debug, Error)] +pub enum ZwlrDataControlSourceV1Error { + #[error(transparent)] + DataControlError(#[from] DataControlError), +} 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/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_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/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_device_v1.rs b/src/ifs/ipc/zwlr_data_control_device_v1.rs deleted file mode 100644 index cf62a678..00000000 --- a/src/ifs/ipc/zwlr_data_control_device_v1.rs +++ /dev/null @@ -1,302 +0,0 @@ -use { - crate::{ - client::{Client, ClientError}, - ifs::{ - ipc::{ - break_device_loops, destroy_data_device, - zwlr_data_control_device_v1::private::{ - WlrClipboardIpcCore, WlrIpcImpl, WlrPrimarySelectionIpcCore, - }, - zwlr_data_control_offer_v1::ZwlrDataControlOfferV1, - zwlr_data_control_source_v1::ZwlrDataControlSourceV1, - DeviceData, IpcLocation, IpcVtable, OfferData, Role, WlrIpcVtable, - }, - wl_seat::{WlSeatError, WlSeatGlobal}, - }, - leaks::Tracker, - object::{Object, Version}, - wire::{ - zwlr_data_control_device_v1::*, ZwlrDataControlDeviceV1Id, ZwlrDataControlOfferV1Id, - ZwlrDataControlSourceV1Id, - }, - }, - std::rc::Rc, - thiserror::Error, -}; - -pub const PRIMARY_SELECTION_SINCE: Version = Version(2); - -pub struct ZwlrDataControlDeviceV1 { - pub id: ZwlrDataControlDeviceV1Id, - pub client: Rc, - pub version: Version, - pub seat: Rc, - pub clipboard_data: DeviceData, - pub primary_selection_data: DeviceData, - pub tracker: Tracker, -} - -impl ZwlrDataControlDeviceV1 { - pub fn new( - id: ZwlrDataControlDeviceV1Id, - client: &Rc, - version: Version, - seat: &Rc, - ) -> Self { - Self { - id, - 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_id: self.id, - id: offer.id, - }) - } - - pub fn send_selection(&self, offer: Option<&Rc>) { - let id = offer - .map(|o| o.id) - .unwrap_or(ZwlrDataControlOfferV1Id::NONE); - self.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(ZwlrDataControlOfferV1Id::NONE); - self.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)?; - Ok(()) - } - - 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.client.remove_obj(self)?; - Ok(()) - } - - fn set_primary_selection( - &self, - req: SetPrimarySelection, - _slf: &Rc, - ) -> Result<(), Self::Error> { - let src = self.use_source(req.source, IpcLocation::PrimarySelection)?; - self.seat.set_primary_selection(src)?; - 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 MIN_VERSION: Version; - const LOCATION: IpcLocation; - - 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); -} - -impl WlrIpc for WlrClipboardIpcCore { - const MIN_VERSION: Version = Version::ALL; - const LOCATION: IpcLocation = IpcLocation::Clipboard; - - fn wlr_get_device_data(dd: &ZwlrDataControlDeviceV1) -> &DeviceData { - &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>, - ) { - dd.send_selection(offer) - } - - fn wlr_unset(seat: &Rc) { - seat.unset_selection() - } -} - -impl WlrIpc for WlrPrimarySelectionIpcCore { - const MIN_VERSION: Version = PRIMARY_SELECTION_SINCE; - const LOCATION: IpcLocation = IpcLocation::PrimarySelection; - - fn wlr_get_device_data(dd: &ZwlrDataControlDeviceV1) -> &DeviceData { - &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>, - ) { - dd.send_primary_selection(offer) - } - - fn wlr_unset(seat: &Rc) { - seat.unset_primary_selection() - } -} - -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; - type Offer = ZwlrDataControlOfferV1; - - 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 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, - ) -> Result, ClientError> { - 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, - 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) - } - - fn send_offer(dd: &Self::Device, offer: &Rc) { - dd.send_data_offer(offer); - } - - fn unset(seat: &Rc, _role: Role) { - T::wlr_unset(seat) - } - - fn device_client(dd: &Rc) -> &Rc { - &dd.client - } -} - -object_base! { - self = ZwlrDataControlDeviceV1; - version = self.version; -} - -impl Object for ZwlrDataControlDeviceV1 { - fn break_loops(&self) { - break_device_loops::(self); - break_device_loops::(self); - self.seat.remove_wlr_device(self); - } -} - -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, -} -efrom!(ZwlrDataControlDeviceV1Error, ClientError); -efrom!(ZwlrDataControlDeviceV1Error, WlSeatError); diff --git a/src/ifs/ipc/zwlr_data_control_offer_v1.rs b/src/ifs/ipc/zwlr_data_control_offer_v1.rs deleted file mode 100644 index 6307edc1..00000000 --- a/src/ifs/ipc/zwlr_data_control_offer_v1.rs +++ /dev/null @@ -1,124 +0,0 @@ -use { - crate::{ - client::{Client, ClientError, ClientId}, - ifs::{ - ipc::{ - break_offer_loops, cancel_offer, destroy_data_offer, receive_data_offer, - zwlr_data_control_device_v1::{ - WlrClipboardIpc, WlrPrimarySelectionIpc, ZwlrDataControlDeviceV1, - }, - DataOffer, DataOfferId, DynDataOffer, IpcLocation, OfferData, - }, - wl_seat::WlSeatGlobal, - }, - leaks::Tracker, - object::Object, - wire::{zwlr_data_control_offer_v1::*, ZwlrDataControlOfferV1Id}, - }, - std::rc::Rc, - thiserror::Error, -}; - -pub struct ZwlrDataControlOfferV1 { - pub id: ZwlrDataControlOfferV1Id, - pub offer_id: DataOfferId, - pub client: Rc, - pub device: Rc, - pub data: OfferData, - pub location: IpcLocation, - pub tracker: Tracker, -} - -impl DataOffer for ZwlrDataControlOfferV1 { - type Device = ZwlrDataControlDeviceV1; - - fn offer_data(&self) -> &OfferData { - &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 - } -} - -impl ZwlrDataControlOfferV1 { - pub fn send_offer(&self, mime_type: &str) { - self.client.event(Offer { - self_id: self.id, - mime_type, - }) - } -} - -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) - } - } - 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)?; - Ok(()) - } -} - -object_base! { - self = ZwlrDataControlOfferV1; - version = self.device.version; -} - -impl Object for ZwlrDataControlOfferV1 { - fn break_loops(&self) { - match self.location { - IpcLocation::Clipboard => break_offer_loops::(self), - IpcLocation::PrimarySelection => break_offer_loops::(self), - } - } -} - -simple_add_obj!(ZwlrDataControlOfferV1); - -#[derive(Debug, Error)] -pub enum ZwlrDataControlOfferV1Error { - #[error(transparent)] - ClientError(Box), -} -efrom!(ZwlrDataControlOfferV1Error, ClientError); diff --git a/src/ifs/ipc/zwlr_data_control_source_v1.rs b/src/ifs/ipc/zwlr_data_control_source_v1.rs deleted file mode 100644 index 16fadf5e..00000000 --- a/src/ifs/ipc/zwlr_data_control_source_v1.rs +++ /dev/null @@ -1,165 +0,0 @@ -use { - crate::{ - client::{Client, ClientError}, - 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, - x_data_device::{XClipboardIpc, XIpcDevice, XPrimarySelectionIpc}, - zwlr_data_control_device_v1::{ - WlrClipboardIpc, WlrPrimarySelectionIpc, ZwlrDataControlDeviceV1, - }, - zwp_primary_selection_device_v1::PrimarySelectionIpc, - DataSource, DynDataSource, IpcLocation, SourceData, - }, - wl_seat::WlSeatGlobal, - }, - leaks::Tracker, - object::{Object, Version}, - wire::{zwlr_data_control_source_v1::*, ZwlrDataControlSourceV1Id}, - }, - std::{cell::Cell, rc::Rc}, - thiserror::Error, - uapi::OwnedFd, -}; - -pub struct ZwlrDataControlSourceV1 { - pub id: ZwlrDataControlSourceV1Id, - pub data: SourceData, - pub version: Version, - pub location: Cell, - pub used: Cell, - pub tracker: Tracker, -} - -impl DataSource for ZwlrDataControlSourceV1 { - fn send_cancelled(&self, _seat: &Rc) { - ZwlrDataControlSourceV1::send_cancelled(self); - } -} - -impl DynDataSource for ZwlrDataControlSourceV1 { - fn source_data(&self) -> &SourceData { - &self.data - } - - fn send_send(&self, mime_type: &str, fd: Rc) { - 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) - } - } - } - - fn detach_seat(&self, seat: &Rc) { - detach_seat(self, seat) - } - - fn cancel_unprivileged_offers(&self) { - cancel_offers(self, false) - } -} - -impl ZwlrDataControlSourceV1 { - pub fn new(id: ZwlrDataControlSourceV1Id, client: &Rc, version: Version) -> Self { - Self { - id, - 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_id: self.id, - mime_type, - fd, - }) - } - - pub fn send_cancelled(&self) { - self.data.client.event(Cancelled { self_id: self.id }) - } -} - -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); - 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)?; - Ok(()) - } -} - -object_base! { - self = ZwlrDataControlSourceV1; - version = self.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), - } - } -} - -dedicated_add_obj!( - ZwlrDataControlSourceV1, - ZwlrDataControlSourceV1Id, - zwlr_data_sources -); - -#[derive(Debug, Error)] -pub enum ZwlrDataControlSourceV1Error { - #[error(transparent)] - ClientError(Box), - #[error("The source has already been used")] - AlreadyUsed, -} -efrom!(ZwlrDataControlSourceV1Error, ClientError); 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/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..96dd6b06 100644 --- a/src/ifs/wl_seat.rs +++ b/src/ifs/wl_seat.rs @@ -33,17 +33,16 @@ use { ext_idle_notification_v1::ExtIdleNotificationV1, ipc::{ 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) => src.clone().offer_to_wlr_device(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(()) } @@ -763,7 +760,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); }), @@ -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 0a8ded1d..159dd676 100644 --- a/src/ifs/wl_seat/event_handling.rs +++ b/src/ifs/wl_seat/event_handling.rs @@ -9,13 +9,12 @@ 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}, @@ -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()); } @@ -1445,7 +1433,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); 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 { 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(()); } 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 { + +}