From 7cbe5720c68047abf0f8523b1516f43939172890 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Sat, 30 Mar 2024 20:51:25 +0100 Subject: [PATCH 1/4] ipc: make source/offer ids type safe --- src/compositor.rs | 1 + src/ifs/ipc.rs | 10 +++++++--- src/ifs/ipc/wl_data_device.rs | 13 +++++-------- src/ifs/ipc/wl_data_offer.rs | 6 +++--- src/ifs/ipc/zwp_primary_selection_device_v1.rs | 14 +++++--------- src/ifs/ipc/zwp_primary_selection_offer_v1.rs | 4 ++-- src/ifs/wl_seat.rs | 2 +- src/state.rs | 4 +++- 8 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/compositor.rs b/src/compositor.rs index 4f30dc91..798cfe81 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -200,6 +200,7 @@ fn start_compositor2( config_file_id: NumCell::new(1), tracker: Default::default(), data_offer_ids: Default::default(), + data_source_ids: Default::default(), drm_dev_ids: Default::default(), ring: ring.clone(), lock: ScreenlockState { diff --git a/src/ifs/ipc.rs b/src/ifs/ipc.rs index 36993c9c..45cabda7 100644 --- a/src/ifs/ipc.rs +++ b/src/ifs/ipc.rs @@ -26,6 +26,9 @@ pub mod zwp_primary_selection_device_v1; pub mod zwp_primary_selection_offer_v1; pub mod zwp_primary_selection_source_v1; +linear_ids!(DataSourceIds, DataSourceId, u64); +linear_ids!(DataOfferIds, DataOfferId, u64); + #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum Role { Selection, @@ -57,14 +60,13 @@ pub trait IpcVtable: Sized { ) -> Result, ClientError>; fn send_selection(dd: &Self::Device, offer: Option<&Rc>); fn send_cancelled(source: &Rc, seat: &Rc); - fn get_offer_id(offer: &Self::Offer) -> u64; + fn get_offer_id(offer: &Self::Offer) -> DataOfferId; fn send_offer(dd: &Self::Device, offer: &Rc); fn send_mime_type(offer: &Rc, mime_type: &str); fn unset(seat: &Rc, role: Role); fn send_send(src: &Rc, mime_type: &str, fd: Rc); fn remove_from_seat(device: &Self::Device); fn get_offer_seat(offer: &Self::Offer) -> Rc; - fn source_eq(left: &Self::Source, right: &Self::Source) -> bool; } pub struct DeviceData { @@ -108,7 +110,8 @@ const SOURCE_STATE_DROPPED_OR_CANCELLED: u32 = SOURCE_STATE_DROPPED | SOURCE_STA pub struct SourceData { pub seat: CloneCell>>, - offers: SmallMap, 1>, + pub id: DataSourceId, + offers: SmallMap, 1>, offer_client: Cell, mime_types: RefCell>, pub client: Rc, @@ -143,6 +146,7 @@ impl SourceData { fn new(client: &Rc, is_xwm: bool) -> Self { Self { seat: Default::default(), + id: client.state.data_source_ids.next(), offers: Default::default(), offer_client: Cell::new(client.id), mime_types: Default::default(), diff --git a/src/ifs/ipc/wl_data_device.rs b/src/ifs/ipc/wl_data_device.rs index 820209b2..2fb6bc87 100644 --- a/src/ifs/ipc/wl_data_device.rs +++ b/src/ifs/ipc/wl_data_device.rs @@ -5,7 +5,8 @@ use { ifs::{ ipc::{ break_device_loops, destroy_data_device, wl_data_offer::WlDataOffer, - wl_data_source::WlDataSource, DeviceData, IpcVtable, OfferData, Role, SourceData, + wl_data_source::WlDataSource, DataOfferId, DeviceData, IpcVtable, OfferData, Role, + SourceData, }, wl_seat::{WlSeatError, WlSeatGlobal}, wl_surface::{SurfaceRole, WlSurfaceError}, @@ -232,7 +233,7 @@ impl IpcVtable for ClipboardIpc { ) -> Result, ClientError> { let rc = Rc::new(WlDataOffer { id: client.new_id()?, - u64_id: client.state.data_offer_ids.fetch_add(1), + offer_id: client.state.data_offer_ids.next(), client: client.clone(), device: device.clone(), data: offer_data, @@ -250,8 +251,8 @@ impl IpcVtable for ClipboardIpc { source.send_cancelled(seat); } - fn get_offer_id(offer: &Self::Offer) -> u64 { - offer.u64_id + fn get_offer_id(offer: &Self::Offer) -> DataOfferId { + offer.offer_id } fn send_offer(dd: &Self::Device, offer: &Rc) { @@ -280,10 +281,6 @@ impl IpcVtable for ClipboardIpc { fn get_offer_seat(offer: &Self::Offer) -> Rc { offer.device.seat.clone() } - - fn source_eq(left: &Self::Source, right: &Self::Source) -> bool { - left as *const _ == right as *const _ - } } object_base! { diff --git a/src/ifs/ipc/wl_data_offer.rs b/src/ifs/ipc/wl_data_offer.rs index 83d16834..4c13fc4b 100644 --- a/src/ifs/ipc/wl_data_offer.rs +++ b/src/ifs/ipc/wl_data_offer.rs @@ -5,8 +5,8 @@ use { break_offer_loops, destroy_data_offer, receive_data_offer, wl_data_device::{ClipboardIpc, WlDataDevice}, wl_data_device_manager::DND_ALL, - OfferData, Role, OFFER_STATE_ACCEPTED, OFFER_STATE_DROPPED, OFFER_STATE_FINISHED, - SOURCE_STATE_FINISHED, + DataOfferId, OfferData, Role, OFFER_STATE_ACCEPTED, OFFER_STATE_DROPPED, + OFFER_STATE_FINISHED, SOURCE_STATE_FINISHED, }, leaks::Tracker, object::Object, @@ -32,7 +32,7 @@ const INVALID_OFFER: u32 = 3; pub struct WlDataOffer { pub id: WlDataOfferId, - pub u64_id: u64, + pub offer_id: DataOfferId, pub client: Rc, pub device: Rc, pub 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 9c470925..0c589d30 100644 --- a/src/ifs/ipc/zwp_primary_selection_device_v1.rs +++ b/src/ifs/ipc/zwp_primary_selection_device_v1.rs @@ -5,8 +5,8 @@ use { ipc::{ break_device_loops, destroy_data_device, zwp_primary_selection_offer_v1::ZwpPrimarySelectionOfferV1, - zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1, DeviceData, - IpcVtable, OfferData, Role, SourceData, + zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1, DataOfferId, + DeviceData, IpcVtable, OfferData, Role, SourceData, }, wl_seat::{WlSeatError, WlSeatGlobal}, }, @@ -179,7 +179,7 @@ impl IpcVtable for PrimarySelectionIpc { }; let rc = Rc::new(ZwpPrimarySelectionOfferV1 { id, - u64_id: client.state.data_offer_ids.fetch_add(1), + offer_id: client.state.data_offer_ids.next(), seat: device.seat.clone(), client: client.clone(), data: offer_data, @@ -197,8 +197,8 @@ impl IpcVtable for PrimarySelectionIpc { source.send_cancelled(); } - fn get_offer_id(offer: &Self::Offer) -> u64 { - offer.u64_id + fn get_offer_id(offer: &Self::Offer) -> DataOfferId { + offer.offer_id } fn send_offer(dd: &Self::Device, offer: &Rc) { @@ -224,10 +224,6 @@ impl IpcVtable for PrimarySelectionIpc { fn get_offer_seat(offer: &Self::Offer) -> Rc { offer.seat.clone() } - - fn source_eq(left: &Self::Source, right: &Self::Source) -> bool { - left as *const _ == right as *const _ - } } object_base! { diff --git a/src/ifs/ipc/zwp_primary_selection_offer_v1.rs b/src/ifs/ipc/zwp_primary_selection_offer_v1.rs index c70d6d82..41d4de02 100644 --- a/src/ifs/ipc/zwp_primary_selection_offer_v1.rs +++ b/src/ifs/ipc/zwp_primary_selection_offer_v1.rs @@ -4,7 +4,7 @@ use { ifs::{ ipc::{ break_offer_loops, destroy_data_offer, receive_data_offer, - zwp_primary_selection_device_v1::PrimarySelectionIpc, OfferData, + zwp_primary_selection_device_v1::PrimarySelectionIpc, DataOfferId, OfferData, }, wl_seat::WlSeatGlobal, }, @@ -20,7 +20,7 @@ use { pub struct ZwpPrimarySelectionOfferV1 { pub id: ZwpPrimarySelectionOfferV1Id, - pub u64_id: u64, + pub offer_id: DataOfferId, pub seat: Rc, pub client: Rc, pub data: OfferData, diff --git a/src/ifs/wl_seat.rs b/src/ifs/wl_seat.rs index e9c225ca..75b17516 100644 --- a/src/ifs/wl_seat.rs +++ b/src/ifs/wl_seat.rs @@ -715,7 +715,7 @@ impl WlSeatGlobal { src: Option>, ) -> Result<(), WlSeatError> { if let (Some(new), Some(old)) = (&src, &field.get()) { - if T::source_eq(old, new) { + if T::get_source_data(new).id == T::get_source_data(old).id { return Ok(()); } } diff --git a/src/state.rs b/src/state.rs index a6047fe4..89601ebe 100644 --- a/src/state.rs +++ b/src/state.rs @@ -26,6 +26,7 @@ use { ifs::{ ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1, ext_session_lock_v1::ExtSessionLockV1, + ipc::{DataOfferIds, DataSourceIds}, jay_render_ctx::JayRenderCtx, jay_seat_events::JaySeatEvents, jay_workspace_watcher::JayWorkspaceWatcher, @@ -149,7 +150,8 @@ pub struct State { pub config_dir: Option, pub config_file_id: NumCell, pub tracker: Tracker, - pub data_offer_ids: NumCell, + pub data_offer_ids: DataOfferIds, + pub data_source_ids: DataSourceIds, pub ring: Rc, pub lock: ScreenlockState, pub scales: RefCounted, From 4e9dacce1a2779925a4ee8ae24c22e53d49cba66 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Sat, 30 Mar 2024 22:47:25 +0100 Subject: [PATCH 2/4] ipc: use trait objects for source/offer --- src/ifs/ipc.rs | 186 +++++++++++++----- src/ifs/ipc/wl_data_device.rs | 61 ++---- src/ifs/ipc/wl_data_offer.rs | 74 +++++-- src/ifs/ipc/wl_data_source.rs | 53 ++++- .../ipc/zwp_primary_selection_device_v1.rs | 61 ++---- src/ifs/ipc/zwp_primary_selection_offer_v1.rs | 47 ++++- .../ipc/zwp_primary_selection_source_v1.rs | 40 +++- src/ifs/wl_seat.rs | 49 +++-- src/ifs/wl_seat/event_handling.rs | 43 ++-- src/ifs/wl_seat/pointer_owner.rs | 2 +- src/xwayland/xwm.rs | 22 +-- 11 files changed, 418 insertions(+), 220 deletions(-) diff --git a/src/ifs/ipc.rs b/src/ifs/ipc.rs index 45cabda7..e9f320dc 100644 --- a/src/ifs/ipc.rs +++ b/src/ifs/ipc.rs @@ -1,14 +1,18 @@ use { crate::{ client::{Client, ClientError, ClientId, WaylandObject}, + fixed::Fixed, ifs::wl_seat::{WlSeatError, WlSeatGlobal}, utils::{ bitflags::BitflagsExt, cell_ext::CellExt, clonecell::CloneCell, numcell::NumCell, smallmap::SmallMap, }, + wire::WlSurfaceId, }, ahash::AHashSet, + smallvec::SmallVec, std::{ + any, cell::{Cell, RefCell}, ops::Deref, rc::Rc, @@ -35,55 +39,122 @@ pub enum Role { Dnd, } +pub trait DataSource: DynDataSource { + fn send_cancelled(self: &Rc, seat: &Rc); +} + +pub trait DynDataSource: 'static { + fn source_data(&self) -> &SourceData; + fn send_send(self: Rc, mime_type: &str, fd: Rc); + fn offer_to(self: Rc, client: &Rc); + fn detach_seat(self: Rc, seat: &Rc); + fn cancel_offers(&self); + + fn send_target(&self, mime_type: Option<&str>) { + let _ = mime_type; + log::warn!( + "send_target called on data source of type {}", + any::type_name_of_val(self) + ) + } + fn send_dnd_finished(&self) { + log::warn!( + "send_dnd_finished called on data source of type {}", + any::type_name_of_val(self) + ) + } + fn update_selected_action(&self) { + log::warn!( + "update_selected_action called on data source of type {}", + any::type_name_of_val(self) + ) + } +} + +pub trait DataOffer: DynDataOffer { + type Device; + + fn offer_data(&self) -> &OfferData; +} + +pub trait DynDataOffer: 'static { + fn offer_id(&self) -> DataOfferId; + fn client_id(&self) -> ClientId; + fn send_offer(self: Rc, mime_type: &str); + fn destroy(&self); + fn cancel(&self); + fn get_seat(&self) -> Rc; + + fn send_action(&self, action: u32) { + let _ = action; + log::warn!( + "send_action called on data source of type {}", + any::type_name_of_val(self) + ) + } + fn send_enter(&self, surface: WlSurfaceId, x: Fixed, y: Fixed, serial: u32) { + let _ = surface; + let _ = x; + let _ = y; + let _ = serial; + log::warn!( + "send_enter called on data source of type {}", + any::type_name_of_val(self) + ) + } + fn send_source_actions(&self) { + log::warn!( + "send_source_actions called on data source of type {}", + any::type_name_of_val(self) + ) + } +} + +pub trait XIpcVtable: IpcVtable { + fn create_xwm_source(client: &Rc) -> Self::Source; + fn remove_from_seat(device: &Self::Device); +} + pub trait IpcVtable: Sized { type Device; - type Source; - type Offer: WaylandObject; + type Source: DataSource; + type Offer: DataOffer + WaylandObject; - fn get_device_data(dd: &Self::Device) -> &DeviceData; + fn get_device_data(dd: &Self::Device) -> &DeviceData; fn get_device_seat(dd: &Self::Device) -> Rc; - fn create_xwm_source(client: &Rc) -> Self::Source; fn set_seat_selection( seat: &Rc, source: &Rc, serial: Option, ) -> Result<(), WlSeatError>; - fn get_offer_data(offer: &Self::Offer) -> &OfferData; - fn get_source_data(src: &Self::Source) -> &SourceData; fn for_each_device(seat: &WlSeatGlobal, client: ClientId, f: C) where C: FnMut(&Rc); fn create_offer( client: &Rc, dd: &Rc, - data: OfferData, + data: OfferData, ) -> Result, ClientError>; fn send_selection(dd: &Self::Device, offer: Option<&Rc>); - fn send_cancelled(source: &Rc, seat: &Rc); - fn get_offer_id(offer: &Self::Offer) -> DataOfferId; fn send_offer(dd: &Self::Device, offer: &Rc); - fn send_mime_type(offer: &Rc, mime_type: &str); fn unset(seat: &Rc, role: Role); - fn send_send(src: &Rc, mime_type: &str, fd: Rc); - fn remove_from_seat(device: &Self::Device); - fn get_offer_seat(offer: &Self::Offer) -> Rc; } -pub struct DeviceData { - selection: CloneCell>>, - dnd: CloneCell>>, +pub struct DeviceData { + selection: CloneCell>>, + dnd: CloneCell>>, pub is_xwm: bool, } -pub struct OfferData { - device: CloneCell>>, - source: CloneCell>>, +pub struct OfferData { + device: CloneCell>>, + source: CloneCell>>, shared: Rc, pub is_xwm: bool, } -impl OfferData { - pub fn source(&self) -> Option> { +impl OfferData { + pub fn source(&self) -> Option> { self.source.get() } } @@ -108,11 +179,10 @@ const SOURCE_STATE_DROPPED: u32 = 1 << 3; const SOURCE_STATE_CANCELLED: u32 = 1 << 4; const SOURCE_STATE_DROPPED_OR_CANCELLED: u32 = SOURCE_STATE_DROPPED | SOURCE_STATE_CANCELLED; -pub struct SourceData { +pub struct SourceData { pub seat: CloneCell>>, pub id: DataSourceId, - offers: SmallMap, 1>, - offer_client: Cell, + offers: SmallMap, 1>, mime_types: RefCell>, pub client: Rc, state: NumCell, @@ -142,13 +212,12 @@ impl Default for SharedState { } } -impl SourceData { +impl SourceData { fn new(client: &Rc, is_xwm: bool) -> Self { Self { seat: Default::default(), id: client.state.data_source_ids.next(), offers: Default::default(), - offer_client: Cell::new(client.id), mime_types: Default::default(), client: client.clone(), state: NumCell::new(0), @@ -170,12 +239,12 @@ impl SourceData { } } -pub fn attach_seat( - src: &T::Source, +pub fn attach_seat( + src: &S, seat: &Rc, role: Role, ) -> Result<(), IpcError> { - let data = T::get_source_data(src); + let data = src.source_data(); let mut state = data.state.get(); if state.contains(SOURCE_STATE_USED) { return Err(IpcError::AlreadyAttached); @@ -197,26 +266,30 @@ pub fn attach_seat( } pub fn cancel_offers(src: &T::Source) { - let data = T::get_source_data(src); + let data = src.source_data(); while let Some((_, offer)) = data.offers.pop() { - let data = T::get_offer_data(&offer); - data.source.take(); - destroy_data_offer::(&offer); + offer.cancel(); } } +pub fn cancel_offer(offer: &T::Offer) { + let data = offer.offer_data(); + data.source.take(); + destroy_data_offer::(&offer); +} + pub fn detach_seat(src: &Rc, seat: &Rc) { - let data = T::get_source_data(src); + let data = src.source_data(); data.seat.set(None); cancel_offers::(src); if !data.state.get().contains(SOURCE_STATE_FINISHED) { - T::send_cancelled(src, seat); + src.send_cancelled(seat); } // data.client.flush(); } -pub fn offer_source_to(src: &Rc, client: &Rc) { - let data = T::get_source_data(src); +pub fn offer_source_to(src: &Rc, client: &Rc) { + let data = src.source_data(); let seat = match data.seat.get() { Some(a) => a, _ => { @@ -224,8 +297,7 @@ pub fn offer_source_to(src: &Rc, client: &Rc) { return; } }; - cancel_offers::(src); - data.offer_client.set(client.id); + src.cancel_offers(); let shared = data.shared.get(); shared.role.set(data.role.get()); T::for_each_device(&seat, client.id, |dd| { @@ -243,11 +315,11 @@ pub fn offer_source_to(src: &Rc, client: &Rc) { return; } }; - data.offers.insert(T::get_offer_id(&offer), offer.clone()); + data.offers.insert(offer.offer_id(), offer.clone()); let mt = data.mime_types.borrow_mut(); T::send_offer(dd, &offer); for mt in mt.deref() { - T::send_mime_type(&offer, mt); + offer.clone().send_offer(mt); } match data.role.get() { Role::Selection => { @@ -265,10 +337,10 @@ pub fn offer_source_to(src: &Rc, client: &Rc) { } pub fn add_data_source_mime_type(src: &T::Source, mime_type: &str) { - let data = T::get_source_data(src); + let data = src.source_data(); if data.mime_types.borrow_mut().insert(mime_type.to_string()) { for (_, offer) in &data.offers { - T::send_mime_type(&offer, mime_type); + offer.send_offer(mime_type); // let data = T::get_offer_data(&offer); // data.client.flush(); } @@ -276,14 +348,14 @@ pub fn add_data_source_mime_type(src: &T::Source, mime_type: &str) } pub fn destroy_data_source(src: &T::Source) { - let data = T::get_source_data(src); + let data = src.source_data(); if let Some(seat) = data.seat.take() { T::unset(&seat, data.role.get()); } } pub fn destroy_data_offer(offer: &T::Offer) { - let data = T::get_offer_data(offer); + let data = offer.offer_data(); if let Some(device) = data.device.take() { let device_data = T::get_device_data(&device); match data.shared.role.get() { @@ -297,8 +369,8 @@ pub fn destroy_data_offer(offer: &T::Offer) { } } if let Some(src) = data.source.take() { - let src_data = T::get_source_data(&src); - src_data.offers.remove(&T::get_offer_id(offer)); + let src_data = src.source_data(); + src_data.offers.remove(&offer.offer_id()); if src_data.offers.is_empty() && src_data.role.get() == Role::Dnd && data.shared.state.get().contains(OFFER_STATE_DROPPED) @@ -314,21 +386,27 @@ pub fn destroy_data_device(dd: &T::Device) { let data = T::get_device_data(dd); let offers = [data.selection.take(), data.dnd.take()]; for offer in offers.into_iter().flat_map(|o| o.into_iter()) { - T::get_offer_data(&offer).device.take(); + offer.offer_data().device.take(); destroy_data_offer::(&offer); } } fn break_source_loops(src: &T::Source) { - let data = T::get_source_data(src); - if data.offer_client.get() == data.client.id { - data.offers.take(); + let data = src.source_data(); + let mut remove = SmallVec::<[DataOfferId; 1]>::new(); + for (id, offer) in &data.offers { + if offer.client_id() == data.client.id { + remove.push(id); + } + } + while let Some(id) = remove.pop() { + data.offers.remove(&id); } destroy_data_source::(src); } fn break_offer_loops(offer: &T::Offer) { - let data = T::get_offer_data(offer); + let data = offer.offer_data(); data.device.set(None); destroy_data_offer::(offer); } @@ -341,9 +419,9 @@ fn break_device_loops(dd: &T::Device) { } pub fn receive_data_offer(offer: &T::Offer, mime_type: &str, fd: Rc) { - let data = T::get_offer_data(offer); + let data = offer.offer_data(); if let Some(src) = data.source.get() { - T::send_send(&src, mime_type, fd); + src.send_send(mime_type, fd); // let data = T::get_source_data(&src); // data.client.flush(); } diff --git a/src/ifs/ipc/wl_data_device.rs b/src/ifs/ipc/wl_data_device.rs index 2fb6bc87..a92ac640 100644 --- a/src/ifs/ipc/wl_data_device.rs +++ b/src/ifs/ipc/wl_data_device.rs @@ -5,8 +5,7 @@ use { ifs::{ ipc::{ break_device_loops, destroy_data_device, wl_data_offer::WlDataOffer, - wl_data_source::WlDataSource, DataOfferId, DeviceData, IpcVtable, OfferData, Role, - SourceData, + wl_data_source::WlDataSource, DeviceData, IpcVtable, OfferData, Role, XIpcVtable, }, wl_seat::{WlSeatError, WlSeatGlobal}, wl_surface::{SurfaceRole, WlSurfaceError}, @@ -19,7 +18,6 @@ use { }, std::rc::Rc, thiserror::Error, - uapi::OwnedFd, }; #[allow(dead_code)] @@ -30,7 +28,7 @@ pub struct WlDataDevice { pub client: Rc, pub version: u32, pub seat: Rc, - pub data: DeviceData, + pub data: DeviceData, pub tracker: Tracker, } @@ -171,7 +169,8 @@ impl WlDataDevice { } else { Some(self.client.lookup(req.source)?) }; - self.seat.set_selection(src, Some(req.serial))?; + self.seat + .set_wl_data_source_selection(src, Some(req.serial))?; Ok(()) } @@ -186,12 +185,22 @@ impl WlDataDevice { pub struct ClipboardIpc; +impl XIpcVtable for ClipboardIpc { + fn create_xwm_source(client: &Rc) -> Self::Source { + WlDataSource::new(WlDataSourceId::NONE, client, true, 3) + } + + fn remove_from_seat(device: &Self::Device) { + device.seat.remove_data_device(device); + } +} + impl IpcVtable for ClipboardIpc { type Device = WlDataDevice; type Source = WlDataSource; type Offer = WlDataOffer; - fn get_device_data(dd: &Self::Device) -> &DeviceData { + fn get_device_data(dd: &Self::Device) -> &DeviceData { &dd.data } @@ -199,24 +208,12 @@ impl IpcVtable for ClipboardIpc { dd.seat.clone() } - fn create_xwm_source(client: &Rc) -> Self::Source { - WlDataSource::new(WlDataSourceId::NONE, client, true, 3) - } - fn set_seat_selection( seat: &Rc, source: &Rc, serial: Option, ) -> Result<(), WlSeatError> { - seat.set_selection(Some(source.clone()), serial) - } - - fn get_offer_data(offer: &Self::Offer) -> &OfferData { - &offer.data - } - - fn get_source_data(src: &Self::Source) -> &SourceData { - &src.data + seat.set_wl_data_source_selection(Some(source.clone()), serial) } fn for_each_device(seat: &WlSeatGlobal, client: ClientId, f: C) @@ -229,7 +226,7 @@ impl IpcVtable for ClipboardIpc { fn create_offer( client: &Rc, device: &Rc, - offer_data: OfferData, + offer_data: OfferData, ) -> Result, ClientError> { let rc = Rc::new(WlDataOffer { id: client.new_id()?, @@ -247,40 +244,16 @@ impl IpcVtable for ClipboardIpc { dd.send_selection(offer); } - fn send_cancelled(source: &Rc, seat: &Rc) { - source.send_cancelled(seat); - } - - fn get_offer_id(offer: &Self::Offer) -> DataOfferId { - offer.offer_id - } - fn send_offer(dd: &Self::Device, offer: &Rc) { dd.send_data_offer(offer); } - fn send_mime_type(offer: &Rc, mime_type: &str) { - offer.send_offer(mime_type); - } - fn unset(seat: &Rc, role: Role) { match role { Role::Selection => seat.unset_selection(), Role::Dnd => seat.cancel_dnd(), } } - - fn send_send(src: &Rc, mime_type: &str, fd: Rc) { - src.send_send(mime_type, fd); - } - - fn remove_from_seat(device: &Self::Device) { - device.seat.remove_data_device(device); - } - - fn get_offer_seat(offer: &Self::Offer) -> Rc { - offer.device.seat.clone() - } } object_base! { diff --git a/src/ifs/ipc/wl_data_offer.rs b/src/ifs/ipc/wl_data_offer.rs index 4c13fc4b..7553dbff 100644 --- a/src/ifs/ipc/wl_data_offer.rs +++ b/src/ifs/ipc/wl_data_offer.rs @@ -1,12 +1,16 @@ use { crate::{ - client::{Client, ClientError}, - ifs::ipc::{ - break_offer_loops, destroy_data_offer, receive_data_offer, - wl_data_device::{ClipboardIpc, WlDataDevice}, - wl_data_device_manager::DND_ALL, - DataOfferId, OfferData, Role, OFFER_STATE_ACCEPTED, OFFER_STATE_DROPPED, - OFFER_STATE_FINISHED, SOURCE_STATE_FINISHED, + client::{Client, ClientError, ClientId}, + fixed::Fixed, + ifs::{ + ipc::{ + break_offer_loops, cancel_offer, destroy_data_offer, receive_data_offer, + wl_data_device::{ClipboardIpc, WlDataDevice}, + wl_data_device_manager::DND_ALL, + DataOffer, DataOfferId, DynDataOffer, OfferData, Role, OFFER_STATE_ACCEPTED, + OFFER_STATE_DROPPED, OFFER_STATE_FINISHED, SOURCE_STATE_FINISHED, + }, + wl_seat::WlSeatGlobal, }, leaks::Tracker, object::Object, @@ -14,7 +18,7 @@ use { bitflags::BitflagsExt, buffd::{MsgParser, MsgParserError}, }, - wire::{wl_data_offer::*, WlDataOfferId}, + wire::{wl_data_offer::*, WlDataOfferId, WlSurfaceId}, xwayland::XWaylandEvent, }, std::rc::Rc, @@ -35,15 +39,61 @@ pub struct WlDataOffer { pub offer_id: DataOfferId, pub client: Rc, pub device: Rc, - pub data: OfferData, + pub data: OfferData, pub tracker: Tracker, } +impl DataOffer for WlDataOffer { + type Device = WlDataDevice; + + fn offer_data(&self) -> &OfferData { + &self.data + } +} + +impl DynDataOffer for WlDataOffer { + fn offer_id(&self) -> DataOfferId { + self.offer_id + } + + fn client_id(&self) -> ClientId { + self.client.id + } + + fn send_action(&self, action: u32) { + WlDataOffer::send_action(self, action); + } + + fn send_offer(self: Rc, mime_type: &str) { + WlDataOffer::send_offer(&self, mime_type); + } + + fn destroy(&self) { + destroy_data_offer::(self); + } + + fn cancel(&self) { + cancel_offer::(self); + } + + fn send_enter(&self, surface: WlSurfaceId, x: Fixed, y: Fixed, serial: u32) { + self.device.send_enter(surface, x, y, self.id, serial); + } + + fn send_source_actions(&self) { + WlDataOffer::send_source_actions(self); + } + + fn get_seat(&self) -> Rc { + self.device.seat.clone() + } +} + impl WlDataOffer { pub fn send_offer(self: &Rc, mime_type: &str) { if self.data.is_xwm { if let Some(src) = self.data.source.get() { - if !src.data.is_xwm { + if !src.source_data().is_xwm { self.client.state.xwayland.queue.push( XWaylandEvent::ClipboardAddOfferMimeType( self.clone(), @@ -63,7 +113,7 @@ impl WlDataOffer { pub fn send_source_actions(&self) { if !self.data.is_xwm { if let Some(src) = self.data.source.get() { - if let Some(source_actions) = src.data.actions.get() { + if let Some(source_actions) = src.source_data().actions.get() { self.client.event(SourceActions { self_id: self.id, source_actions, @@ -134,7 +184,7 @@ impl WlDataOffer { } state |= OFFER_STATE_FINISHED; if let Some(src) = self.data.source.get() { - src.data.state.or_assign(SOURCE_STATE_FINISHED); + src.source_data().state.or_assign(SOURCE_STATE_FINISHED); src.send_dnd_finished(); } else { log::error!("no source"); diff --git a/src/ifs/ipc/wl_data_source.rs b/src/ifs/ipc/wl_data_source.rs index ff6d9d6b..74aa0948 100644 --- a/src/ifs/ipc/wl_data_source.rs +++ b/src/ifs/ipc/wl_data_source.rs @@ -4,11 +4,12 @@ use { ifs::{ ipc::{ add_data_source_mime_type, break_source_loops, cancel_offers, destroy_data_source, + detach_seat, offer_source_to, wl_data_device::ClipboardIpc, wl_data_device_manager::{DND_ALL, DND_NONE}, - wl_data_offer::WlDataOffer, - SharedState, SourceData, OFFER_STATE_ACCEPTED, OFFER_STATE_DROPPED, - SOURCE_STATE_CANCELLED, SOURCE_STATE_DROPPED, + DataSource, DynDataOffer, DynDataSource, SharedState, SourceData, + OFFER_STATE_ACCEPTED, OFFER_STATE_DROPPED, SOURCE_STATE_CANCELLED, + SOURCE_STATE_DROPPED, }, wl_seat::WlSeatGlobal, xdg_toplevel_drag_v1::XdgToplevelDragV1, @@ -36,12 +37,52 @@ const INVALID_SOURCE: u32 = 1; pub struct WlDataSource { pub id: WlDataSourceId, - pub data: SourceData, + pub data: SourceData, pub version: u32, pub tracker: Tracker, pub toplevel_drag: CloneCell>>, } +impl DataSource for WlDataSource { + fn send_cancelled(self: &Rc, seat: &Rc) { + WlDataSource::send_cancelled(self, seat); + } +} + +impl DynDataSource for WlDataSource { + fn source_data(&self) -> &SourceData { + &self.data + } + + fn send_send(self: Rc, mime_type: &str, fd: Rc) { + WlDataSource::send_send(&self, mime_type, fd); + } + + fn offer_to(self: Rc, client: &Rc) { + offer_source_to::(&self, client); + } + + fn detach_seat(self: Rc, seat: &Rc) { + detach_seat::(&self, seat); + } + + fn cancel_offers(&self) { + cancel_offers::(self); + } + + fn send_target(&self, mime_type: Option<&str>) { + WlDataSource::send_target(self, mime_type); + } + + fn send_dnd_finished(&self) { + WlDataSource::send_dnd_finished(self); + } + + fn update_selected_action(&self) { + WlDataSource::update_selected_action(self); + } +} + impl WlDataSource { pub fn new(id: WlDataSourceId, client: &Rc, is_xwm: bool, version: u32) -> Self { Self { @@ -97,9 +138,9 @@ impl WlDataSource { } } - pub fn for_each_data_offer(&self, mut f: C) { + pub fn for_each_data_offer(&self, mut f: C) { for (_, offer) in &self.data.offers { - f(&offer); + f(&*offer); } } diff --git a/src/ifs/ipc/zwp_primary_selection_device_v1.rs b/src/ifs/ipc/zwp_primary_selection_device_v1.rs index 0c589d30..b9a09176 100644 --- a/src/ifs/ipc/zwp_primary_selection_device_v1.rs +++ b/src/ifs/ipc/zwp_primary_selection_device_v1.rs @@ -5,8 +5,8 @@ use { ipc::{ break_device_loops, destroy_data_device, zwp_primary_selection_offer_v1::ZwpPrimarySelectionOfferV1, - zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1, DataOfferId, - DeviceData, IpcVtable, OfferData, Role, SourceData, + zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1, DeviceData, + IpcVtable, OfferData, Role, XIpcVtable, }, wl_seat::{WlSeatError, WlSeatGlobal}, }, @@ -21,7 +21,6 @@ use { }, std::rc::Rc, thiserror::Error, - uapi::OwnedFd, }; pub struct ZwpPrimarySelectionDeviceV1 { @@ -29,7 +28,7 @@ pub struct ZwpPrimarySelectionDeviceV1 { pub client: Rc, pub version: u32, pub seat: Rc, - data: DeviceData, + data: DeviceData, pub tracker: Tracker, } @@ -112,7 +111,7 @@ impl ZwpPrimarySelectionDeviceV1 { } else { Some(self.client.lookup(req.source)?) }; - self.seat.set_primary_selection(src, Some(req.serial))?; + self.seat.set_zwp_primary_selection(src, Some(req.serial))?; Ok(()) } @@ -127,12 +126,22 @@ impl ZwpPrimarySelectionDeviceV1 { pub struct PrimarySelectionIpc; +impl XIpcVtable for PrimarySelectionIpc { + fn create_xwm_source(client: &Rc) -> Self::Source { + ZwpPrimarySelectionSourceV1::new(ZwpPrimarySelectionSourceV1Id::NONE, client, true) + } + + fn remove_from_seat(device: &Self::Device) { + device.seat.remove_primary_selection_device(device); + } +} + impl IpcVtable for PrimarySelectionIpc { type Device = ZwpPrimarySelectionDeviceV1; type Source = ZwpPrimarySelectionSourceV1; type Offer = ZwpPrimarySelectionOfferV1; - fn get_device_data(dd: &Self::Device) -> &DeviceData { + fn get_device_data(dd: &Self::Device) -> &DeviceData { &dd.data } @@ -140,24 +149,12 @@ impl IpcVtable for PrimarySelectionIpc { dd.seat.clone() } - fn create_xwm_source(client: &Rc) -> Self::Source { - ZwpPrimarySelectionSourceV1::new(ZwpPrimarySelectionSourceV1Id::NONE, client, true) - } - fn set_seat_selection( seat: &Rc, source: &Rc, serial: Option, ) -> Result<(), WlSeatError> { - seat.set_primary_selection(Some(source.clone()), serial) - } - - fn get_offer_data(offer: &Self::Offer) -> &OfferData { - &offer.data - } - - fn get_source_data(src: &Self::Source) -> &SourceData { - &src.data + seat.set_zwp_primary_selection(Some(source.clone()), serial) } fn for_each_device(seat: &WlSeatGlobal, client: ClientId, f: C) @@ -170,7 +167,7 @@ impl IpcVtable for PrimarySelectionIpc { fn create_offer( client: &Rc, device: &Rc, - offer_data: OfferData, + offer_data: OfferData, ) -> Result, ClientError> { let id = if device.data.is_xwm { ZwpPrimarySelectionOfferV1Id::NONE @@ -193,37 +190,13 @@ impl IpcVtable for PrimarySelectionIpc { dd.send_selection(offer); } - fn send_cancelled(source: &Rc, _seat: &Rc) { - source.send_cancelled(); - } - - fn get_offer_id(offer: &Self::Offer) -> DataOfferId { - offer.offer_id - } - fn send_offer(dd: &Self::Device, offer: &Rc) { dd.send_data_offer(offer); } - fn send_mime_type(offer: &Rc, mime_type: &str) { - offer.send_offer(mime_type); - } - fn unset(seat: &Rc, _role: Role) { seat.unset_primary_selection(); } - - fn send_send(src: &Rc, mime_type: &str, fd: Rc) { - src.send_send(mime_type, fd); - } - - fn remove_from_seat(device: &Self::Device) { - device.seat.remove_primary_selection_device(device); - } - - fn get_offer_seat(offer: &Self::Offer) -> Rc { - offer.seat.clone() - } } object_base! { diff --git a/src/ifs/ipc/zwp_primary_selection_offer_v1.rs b/src/ifs/ipc/zwp_primary_selection_offer_v1.rs index 41d4de02..54fba396 100644 --- a/src/ifs/ipc/zwp_primary_selection_offer_v1.rs +++ b/src/ifs/ipc/zwp_primary_selection_offer_v1.rs @@ -1,10 +1,13 @@ use { crate::{ - client::{Client, ClientError}, + client::{Client, ClientError, ClientId}, ifs::{ ipc::{ - break_offer_loops, destroy_data_offer, receive_data_offer, - zwp_primary_selection_device_v1::PrimarySelectionIpc, DataOfferId, OfferData, + break_offer_loops, cancel_offer, destroy_data_offer, receive_data_offer, + zwp_primary_selection_device_v1::{ + PrimarySelectionIpc, ZwpPrimarySelectionDeviceV1, + }, + DataOffer, DataOfferId, DynDataOffer, OfferData, }, wl_seat::WlSeatGlobal, }, @@ -23,15 +26,49 @@ pub struct ZwpPrimarySelectionOfferV1 { pub offer_id: DataOfferId, pub seat: Rc, pub client: Rc, - pub data: OfferData, + pub data: OfferData, pub tracker: Tracker, } +impl DataOffer for ZwpPrimarySelectionOfferV1 { + type Device = ZwpPrimarySelectionDeviceV1; + + fn offer_data(&self) -> &OfferData { + &self.data + } +} + +impl DynDataOffer for ZwpPrimarySelectionOfferV1 { + fn offer_id(&self) -> DataOfferId { + self.offer_id + } + + fn client_id(&self) -> ClientId { + self.client.id + } + + fn send_offer(self: Rc, mime_type: &str) { + ZwpPrimarySelectionOfferV1::send_offer(&self, mime_type); + } + + fn destroy(&self) { + destroy_data_offer::(self); + } + + fn cancel(&self) { + cancel_offer::(self); + } + + fn get_seat(&self) -> Rc { + self.seat.clone() + } +} + impl ZwpPrimarySelectionOfferV1 { pub fn send_offer(self: &Rc, mime_type: &str) { if self.data.is_xwm { if let Some(src) = self.data.source.get() { - if !src.data.is_xwm { + if !src.source_data().is_xwm { self.client.state.xwayland.queue.push( XWaylandEvent::PrimarySelectionAddOfferMimeType( self.clone(), diff --git a/src/ifs/ipc/zwp_primary_selection_source_v1.rs b/src/ifs/ipc/zwp_primary_selection_source_v1.rs index a563f9fe..a657882b 100644 --- a/src/ifs/ipc/zwp_primary_selection_source_v1.rs +++ b/src/ifs/ipc/zwp_primary_selection_source_v1.rs @@ -1,9 +1,13 @@ use { crate::{ client::{Client, ClientError}, - ifs::ipc::{ - add_data_source_mime_type, break_source_loops, destroy_data_source, - zwp_primary_selection_device_v1::PrimarySelectionIpc, SourceData, + ifs::{ + ipc::{ + add_data_source_mime_type, break_source_loops, cancel_offers, destroy_data_source, + detach_seat, offer_source_to, zwp_primary_selection_device_v1::PrimarySelectionIpc, + DataSource, DynDataSource, SourceData, + }, + wl_seat::WlSeatGlobal, }, leaks::Tracker, object::Object, @@ -18,10 +22,38 @@ use { pub struct ZwpPrimarySelectionSourceV1 { pub id: ZwpPrimarySelectionSourceV1Id, - pub data: SourceData, + pub data: SourceData, pub tracker: Tracker, } +impl DataSource for ZwpPrimarySelectionSourceV1 { + fn send_cancelled(self: &Rc, _seat: &Rc) { + ZwpPrimarySelectionSourceV1::send_cancelled(self); + } +} + +impl DynDataSource for ZwpPrimarySelectionSourceV1 { + fn source_data(&self) -> &SourceData { + &self.data + } + + fn send_send(self: Rc, mime_type: &str, fd: Rc) { + ZwpPrimarySelectionSourceV1::send_send(&self, mime_type, fd) + } + + fn offer_to(self: Rc, client: &Rc) { + offer_source_to::(&self, client); + } + + fn detach_seat(self: Rc, seat: &Rc) { + detach_seat::(&self, seat); + } + + fn cancel_offers(&self) { + cancel_offers::(self); + } +} + impl ZwpPrimarySelectionSourceV1 { pub fn new(id: ZwpPrimarySelectionSourceV1Id, client: &Rc, is_xwm: bool) -> Self { Self { diff --git a/src/ifs/wl_seat.rs b/src/ifs/wl_seat.rs index 75b17516..1a3d6dc7 100644 --- a/src/ifs/wl_seat.rs +++ b/src/ifs/wl_seat.rs @@ -26,7 +26,7 @@ use { PrimarySelectionIpc, ZwpPrimarySelectionDeviceV1, }, zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1, - IpcError, + DynDataSource, IpcError, }, wl_seat::{ kb_owner::KbOwnerHolder, @@ -143,9 +143,9 @@ pub struct WlSeatGlobal { kb_state: RefCell, cursor: CloneCell>>, tree_changed: Rc, - selection: CloneCell>>, + selection: CloneCell>>, selection_serial: Cell, - primary_selection: CloneCell>>, + primary_selection: CloneCell>>, primary_selection_serial: Cell, pointer_owner: PointerOwnerHolder, kb_owner: KbOwnerHolder, @@ -709,25 +709,26 @@ impl WlSeatGlobal { } } - fn set_selection_( + fn set_selection_( self: &Rc, - field: &CloneCell>>, - src: Option>, + field: &CloneCell>>, + src: Option>, ) -> Result<(), WlSeatError> { if let (Some(new), Some(old)) = (&src, &field.get()) { - if T::get_source_data(new).id == T::get_source_data(old).id { + if new.source_data().id == old.source_data().id { return Ok(()); } } if let Some(new) = &src { - ipc::attach_seat::(new, self, ipc::Role::Selection)?; + ipc::attach_seat(&**new, self, ipc::Role::Selection)?; } - if let Some(old) = field.set(src.clone()) { - ipc::detach_seat::(&old, self); + let src_dyn = src.clone().map(|s| s as Rc); + if let Some(old) = field.set(src_dyn) { + old.detach_seat(self); } if let Some(client) = self.keyboard_node.get().node_client() { match src { - Some(src) => ipc::offer_source_to::(&src, &client), + Some(src) => ipc::offer_source_to::(&src, &client), _ => T::for_each_device(self, client.id, |device| { T::send_selection(device, None); }), @@ -756,10 +757,10 @@ impl WlSeatGlobal { } pub fn unset_selection(self: &Rc) { - let _ = self.set_selection(None, None); + let _ = self.set_wl_data_source_selection(None, None); } - pub fn set_selection( + pub fn set_wl_data_source_selection( self: &Rc, selection: Option>, serial: Option, @@ -772,7 +773,14 @@ impl WlSeatGlobal { return Err(WlSeatError::OfferHasDrag); } } - self.set_selection_::(&self.selection, selection) + self.set_selection(selection) + } + + pub fn set_selection( + self: &Rc, + selection: Option>, + ) -> Result<(), WlSeatError> { + self.set_selection_::(&self.selection, selection) } pub fn may_modify_selection(&self, client: &Rc, serial: u32) -> bool { @@ -795,10 +803,10 @@ impl WlSeatGlobal { } pub fn unset_primary_selection(self: &Rc) { - let _ = self.set_primary_selection(None, None); + let _ = self.set_zwp_primary_selection(None, None); } - pub fn set_primary_selection( + pub fn set_zwp_primary_selection( self: &Rc, selection: Option>, serial: Option, @@ -806,7 +814,14 @@ impl WlSeatGlobal { if let Some(serial) = serial { self.primary_selection_serial.set(serial); } - self.set_selection_::(&self.primary_selection, selection) + self.set_primary_selection(selection) + } + + pub fn set_primary_selection( + self: &Rc, + selection: Option>, + ) -> Result<(), WlSeatError> { + self.set_selection_::(&self.primary_selection, selection) } pub fn reload_known_cursor(&self) { diff --git a/src/ifs/wl_seat/event_handling.rs b/src/ifs/wl_seat/event_handling.rs index 8ba365c4..57748764 100644 --- a/src/ifs/wl_seat/event_handling.rs +++ b/src/ifs/wl_seat/event_handling.rs @@ -1,15 +1,13 @@ use { crate::{ backend::{ConnectorId, InputEvent, KeyState, AXIS_120}, - client::{Client, ClientId}, + client::ClientId, fixed::Fixed, ifs::{ ipc, ipc::{ wl_data_device::{ClipboardIpc, WlDataDevice}, - zwp_primary_selection_device_v1::{ - PrimarySelectionIpc, ZwpPrimarySelectionDeviceV1, - }, + zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1, }, wl_seat::{ wl_keyboard::{self, WlKeyboard}, @@ -27,7 +25,7 @@ use { }, state::DeviceHandlerData, tree::{Direction, FloatNode, Node, ToplevelNode}, - utils::{bitflags::BitflagsExt, clonecell::CloneCell, smallmap::SmallMap}, + utils::{bitflags::BitflagsExt, smallmap::SmallMap}, wire::WlDataOfferId, xkbcommon::{ModifierState, XKB_KEY_DOWN, XKB_KEY_UP}, }, @@ -429,19 +427,6 @@ impl WlSeatGlobal { self.kb_owner.set_kb_node(self, node); } - fn offer_selection( - &self, - field: &CloneCell>>, - client: &Rc, - ) { - match field.get() { - Some(sel) => ipc::offer_source_to::(&sel, client), - None => T::for_each_device(self, client.id, |dd| { - T::send_selection(dd, None); - }), - } - } - fn for_each_seat(&self, ver: u32, client: ClientId, mut f: C) where C: FnMut(&Rc), @@ -757,8 +742,22 @@ impl WlSeatGlobal { }); if self.keyboard_node.get().node_client_id() != Some(surface.client.id) { - self.offer_selection::(&self.selection, &surface.client); - self.offer_selection::(&self.primary_selection, &surface.client); + match self.selection.get() { + Some(sel) => sel.offer_to(&surface.client), + None => { + self.for_each_data_device(0, surface.client.id, |dd| { + dd.send_selection(None); + }); + } + } + match self.primary_selection.get() { + Some(sel) => sel.offer_to(&surface.client), + None => { + self.for_each_primary_selection_device(0, surface.client.id, |dd| { + dd.send_selection(None); + }); + } + } } } } @@ -820,9 +819,9 @@ impl WlSeatGlobal { serial: u32, ) { if let Some(src) = &dnd.src { - ipc::offer_source_to::(src, &surface.client); + ipc::offer_source_to::(src, &surface.client); src.for_each_data_offer(|offer| { - offer.device.send_enter(surface.id, x, y, offer.id, serial); + offer.send_enter(surface.id, x, y, serial); offer.send_source_actions(); }) } else if surface.client.id == dnd.client.id { diff --git a/src/ifs/wl_seat/pointer_owner.rs b/src/ifs/wl_seat/pointer_owner.rs index ddd9ecf0..91c82e5d 100644 --- a/src/ifs/wl_seat/pointer_owner.rs +++ b/src/ifs/wl_seat/pointer_owner.rs @@ -371,7 +371,7 @@ impl PointerOwner for GrabPointerOwner { icon.set_dnd_icon_seat(seat.id, Some(seat)); } if let Some(new) = &src { - ipc::attach_seat::(new, seat, ipc::Role::Dnd)?; + ipc::attach_seat(&**new, seat, ipc::Role::Dnd)?; if let Some(drag) = new.toplevel_drag.get() { drag.start_drag(); } diff --git a/src/xwayland/xwm.rs b/src/xwayland/xwm.rs index ba661c0e..9b9a8d25 100644 --- a/src/xwayland/xwm.rs +++ b/src/xwayland/xwm.rs @@ -12,7 +12,7 @@ use { zwp_primary_selection_device_v1::{ PrimarySelectionIpc, ZwpPrimarySelectionDeviceV1, }, - IpcVtable, + DataOffer, DynDataOffer, DynDataSource, IpcVtable, XIpcVtable, }, wl_seat::{SeatId, WlSeatGlobal}, wl_surface::{ @@ -181,7 +181,7 @@ impl Default for SelectionData { } } -impl SelectionData { +impl SelectionData { fn destroy(&self) { for (_, offer) in self.offers.lock().drain() { destroy_data_offer::(&offer.offer); @@ -202,7 +202,7 @@ impl SelectionData { fn seat_removed(&self, id: SeatId) { if let Some(offer) = self.active_offer.get() { - if T::get_offer_seat(&offer.offer).id() == id { + if offer.offer.get_seat().id() == id { self.active_offer.take(); } } @@ -657,7 +657,7 @@ impl Wm { offer: Rc, mt: String, ) { - let seat = T::get_offer_seat(&offer); + let seat = offer.get_seat(); let enhanced = match sd.offers.get(&seat.id()) { Some(r) if !rc_eq(&r.offer, &offer) => { return; @@ -678,15 +678,15 @@ impl Wm { } async fn dd_set_offer(&mut self, sd: &SelectionData, offer: Rc) { - let seat = T::get_offer_seat(&offer); + let seat = offer.get_seat(); let mut mime_types = vec![]; if let Some(offer) = sd.offers.remove(&seat.id()) { destroy_data_offer::(&offer.offer); mime_types = mem::take(offer.mime_types.borrow_mut().deref_mut()); } - match T::get_offer_data(&offer).source() { + match offer.offer_data().source() { None => return, - Some(s) if T::get_source_data(&s).is_xwm => return, + Some(s) if s.source_data().is_xwm => return, _ => {} } sd.offers.set( @@ -801,7 +801,7 @@ impl Wm { mime_type: String, fd: Rc, ) { - let seat = match T::get_source_data(src).seat.get() { + let seat = match src.source_data().seat.get() { Some(s) => s, _ => return, }; @@ -839,7 +839,7 @@ impl Wm { } fn dd_cancel_source(&mut self, sd: &SelectionData, src: &Rc) { - if let Some(seat) = T::get_source_data(src).seat.get() { + if let Some(seat) = src.source_data().seat.get() { if let Some(cur) = sd.sources.get(&seat.id()) { if rc_eq(src, &cur) { sd.sources.remove(&seat.id()); @@ -1508,7 +1508,7 @@ impl Wm { } } - async fn handle_xfixes_selection_notify_( + async fn handle_xfixes_selection_notify_( &mut self, sd: &SelectionData, event: &XfixesSelectionNotify, @@ -1663,7 +1663,7 @@ impl Wm { } } - async fn handle_selection_notify_( + async fn handle_selection_notify_( &mut self, sd: &SelectionData, event: &SelectionNotify, From 8bca8b0e861cb4da1f7dfc04ab8d5357bcc938e0 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Sun, 31 Mar 2024 18:42:23 +0200 Subject: [PATCH 3/4] ipc: create separate offers/sources for X --- src/compositor.rs | 1 + src/ifs/ipc.rs | 156 +++++---- src/ifs/ipc/wl_data_device.rs | 126 +++----- src/ifs/ipc/wl_data_device_manager.rs | 3 +- src/ifs/ipc/wl_data_offer.rs | 52 +-- src/ifs/ipc/wl_data_source.rs | 106 +++--- src/ifs/ipc/x_data_device.rs | 134 ++++++++ src/ifs/ipc/x_data_offer.rs | 70 ++++ src/ifs/ipc/x_data_source.rs | 79 +++++ ...zwp_primary_selection_device_manager_v1.rs | 7 +- .../ipc/zwp_primary_selection_device_v1.rs | 94 ++---- src/ifs/ipc/zwp_primary_selection_offer_v1.rs | 28 +- .../ipc/zwp_primary_selection_source_v1.rs | 67 ++-- src/ifs/wl_seat.rs | 65 +++- src/ifs/wl_seat/event_handling.rs | 35 +- src/ifs/wl_seat/pointer_owner.rs | 6 +- src/state.rs | 3 +- src/xwayland.rs | 45 ++- src/xwayland/xwm.rs | 305 ++++++++++-------- 19 files changed, 830 insertions(+), 552 deletions(-) create mode 100644 src/ifs/ipc/x_data_device.rs create mode 100644 src/ifs/ipc/x_data_offer.rs create mode 100644 src/ifs/ipc/x_data_source.rs diff --git a/src/compositor.rs b/src/compositor.rs index 798cfe81..98e07650 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -191,6 +191,7 @@ fn start_compositor2( enabled: Cell::new(true), handler: Default::default(), queue: Default::default(), + ipc_device_ids: Default::default(), }, acceptor: Default::default(), serial: Default::default(), diff --git a/src/ifs/ipc.rs b/src/ifs/ipc.rs index e9f320dc..478d01a7 100644 --- a/src/ifs/ipc.rs +++ b/src/ifs/ipc.rs @@ -1,8 +1,11 @@ use { crate::{ - client::{Client, ClientError, ClientId, WaylandObject}, + client::{Client, ClientError, ClientId}, fixed::Fixed, - ifs::wl_seat::{WlSeatError, WlSeatGlobal}, + ifs::{ + ipc::x_data_device::XIpcDevice, + wl_seat::{WlSeatError, WlSeatGlobal}, + }, utils::{ bitflags::BitflagsExt, cell_ext::CellExt, clonecell::CloneCell, numcell::NumCell, smallmap::SmallMap, @@ -25,6 +28,9 @@ pub mod wl_data_device; pub mod wl_data_device_manager; pub mod wl_data_offer; pub mod wl_data_source; +pub mod x_data_device; +pub mod x_data_offer; +pub mod x_data_source; pub mod zwp_primary_selection_device_manager_v1; pub mod zwp_primary_selection_device_v1; pub mod zwp_primary_selection_offer_v1; @@ -33,6 +39,12 @@ pub mod zwp_primary_selection_source_v1; linear_ids!(DataSourceIds, DataSourceId, u64); linear_ids!(DataOfferIds, DataOfferId, u64); +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum IpcLocation { + Clipboard, + PrimarySelection, +} + #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum Role { Selection, @@ -40,14 +52,15 @@ pub enum Role { } pub trait DataSource: DynDataSource { - fn send_cancelled(self: &Rc, seat: &Rc); + fn send_cancelled(&self, seat: &Rc); } pub trait DynDataSource: 'static { fn source_data(&self) -> &SourceData; - fn send_send(self: Rc, mime_type: &str, fd: Rc); - fn offer_to(self: Rc, client: &Rc); - fn detach_seat(self: Rc, seat: &Rc); + 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 detach_seat(&self, seat: &Rc); fn cancel_offers(&self); fn send_target(&self, mime_type: Option<&str>) { @@ -80,7 +93,7 @@ pub trait DataOffer: DynDataOffer { pub trait DynDataOffer: 'static { fn offer_id(&self) -> DataOfferId; fn client_id(&self) -> ClientId; - fn send_offer(self: Rc, mime_type: &str); + fn send_offer(&self, mime_type: &str); fn destroy(&self); fn cancel(&self); fn get_seat(&self) -> Rc; @@ -110,15 +123,18 @@ pub trait DynDataOffer: 'static { } } -pub trait XIpcVtable: IpcVtable { - fn create_xwm_source(client: &Rc) -> Self::Source; - fn remove_from_seat(device: &Self::Device); +pub trait IterableIpcVtable: IpcVtable { + fn for_each_device(seat: &WlSeatGlobal, client: ClientId, f: C) + where + C: FnMut(&Rc); } pub trait IpcVtable: Sized { + const LOCATION: IpcLocation; + type Device; type Source: DataSource; - type Offer: DataOffer + WaylandObject; + type Offer: DataOffer; fn get_device_data(dd: &Self::Device) -> &DeviceData; fn get_device_seat(dd: &Self::Device) -> Rc; @@ -127,36 +143,34 @@ pub trait IpcVtable: Sized { source: &Rc, serial: Option, ) -> Result<(), WlSeatError>; - fn for_each_device(seat: &WlSeatGlobal, client: ClientId, f: C) - where - C: FnMut(&Rc); fn create_offer( - client: &Rc, dd: &Rc, data: OfferData, ) -> Result, ClientError>; fn send_selection(dd: &Self::Device, offer: Option<&Rc>); fn send_offer(dd: &Self::Device, offer: &Rc); fn unset(seat: &Rc, role: Role); + fn device_client(dd: &Rc) -> &Rc; } pub struct DeviceData { selection: CloneCell>>, dnd: CloneCell>>, - pub is_xwm: bool, +} + +impl Default for DeviceData { + fn default() -> Self { + Self { + selection: Default::default(), + dnd: Default::default(), + } + } } pub struct OfferData { device: CloneCell>>, source: CloneCell>>, shared: Rc, - pub is_xwm: bool, -} - -impl OfferData { - pub fn source(&self) -> Option> { - self.source.get() - } } #[derive(Debug, Error)] @@ -189,7 +203,6 @@ pub struct SourceData { actions: Cell>, role: Cell, shared: CloneCell>, - pub is_xwm: bool, } struct SharedState { @@ -213,7 +226,7 @@ impl Default for SharedState { } impl SourceData { - fn new(client: &Rc, is_xwm: bool) -> Self { + pub fn new(client: &Rc) -> Self { Self { seat: Default::default(), id: client.state.data_source_ids.next(), @@ -224,7 +237,6 @@ impl SourceData { actions: Cell::new(None), role: Cell::new(Role::Selection), shared: Default::default(), - is_xwm, } } @@ -265,7 +277,7 @@ pub fn attach_seat( Ok(()) } -pub fn cancel_offers(src: &T::Source) { +pub fn cancel_offers(src: &S) { let data = src.source_data(); while let Some((_, offer)) = data.offers.pop() { offer.cancel(); @@ -278,17 +290,68 @@ pub fn cancel_offer(offer: &T::Offer) { destroy_data_offer::(&offer); } -pub fn detach_seat(src: &Rc, seat: &Rc) { +pub fn detach_seat(src: &S, seat: &Rc) { let data = src.source_data(); data.seat.set(None); - cancel_offers::(src); + cancel_offers(src); if !data.state.get().contains(SOURCE_STATE_FINISHED) { src.send_cancelled(seat); } // data.client.flush(); } -pub fn offer_source_to(src: &Rc, client: &Rc) { +fn offer_source_to_device( + src: &Rc, + dd: &Rc, + data: &SourceData, + shared: Rc, +) { + let device_data = T::get_device_data(dd); + let offer_data = OfferData { + device: CloneCell::new(Some(dd.clone())), + source: CloneCell::new(Some(src.clone())), + shared: shared.clone(), + }; + let offer = match T::create_offer(dd, offer_data) { + Ok(o) => o, + Err(e) => { + T::device_client(dd).error(e); + return; + } + }; + data.offers.insert(offer.offer_id(), offer.clone()); + let mt = data.mime_types.borrow_mut(); + T::send_offer(dd, &offer); + for mt in mt.deref() { + offer.clone().send_offer(mt); + } + match data.role.get() { + Role::Selection => { + T::send_selection(dd, Some(&offer)); + device_data.selection.set(Some(offer.clone())); + } + Role::Dnd => { + device_data.dnd.set(Some(offer.clone())); + } + } +} + +fn offer_source_to_x(src: &Rc, dd: &Rc) +where + T: IpcVtable, + S: DynDataSource, +{ + let data = src.source_data(); + src.cancel_offers(); + let shared = data.shared.get(); + shared.role.set(data.role.get()); + offer_source_to_device::(src, dd, data, shared); +} + +fn offer_source_to_regular_client( + src: &Rc, + client: &Rc, +) { let data = src.source_data(); let seat = match data.seat.get() { Some(a) => a, @@ -301,38 +364,7 @@ pub fn offer_source_to(src: &Rc, client: &Rc< let shared = data.shared.get(); shared.role.set(data.role.get()); T::for_each_device(&seat, client.id, |dd| { - let device_data = T::get_device_data(dd); - let offer_data = OfferData { - device: CloneCell::new(Some(dd.clone())), - source: CloneCell::new(Some(src.clone())), - shared: shared.clone(), - is_xwm: device_data.is_xwm, - }; - let offer = match T::create_offer(client, dd, offer_data) { - Ok(o) => o, - Err(e) => { - client.error(e); - return; - } - }; - data.offers.insert(offer.offer_id(), offer.clone()); - let mt = data.mime_types.borrow_mut(); - T::send_offer(dd, &offer); - for mt in mt.deref() { - offer.clone().send_offer(mt); - } - match data.role.get() { - Role::Selection => { - T::send_selection(dd, Some(&offer)); - device_data.selection.set(Some(offer.clone())); - } - Role::Dnd => { - device_data.dnd.set(Some(offer.clone())); - } - } - if !device_data.is_xwm { - client.add_server_obj(&offer); - } + offer_source_to_device::(src, dd, data, shared.clone()); }); } diff --git a/src/ifs/ipc/wl_data_device.rs b/src/ifs/ipc/wl_data_device.rs index a92ac640..6b7ef840 100644 --- a/src/ifs/ipc/wl_data_device.rs +++ b/src/ifs/ipc/wl_data_device.rs @@ -5,7 +5,8 @@ use { ifs::{ ipc::{ break_device_loops, destroy_data_device, wl_data_offer::WlDataOffer, - wl_data_source::WlDataSource, DeviceData, IpcVtable, OfferData, Role, XIpcVtable, + wl_data_source::WlDataSource, DeviceData, IpcLocation, IpcVtable, + IterableIpcVtable, OfferData, Role, }, wl_seat::{WlSeatError, WlSeatGlobal}, wl_surface::{SurfaceRole, WlSurfaceError}, @@ -13,8 +14,7 @@ use { leaks::Tracker, object::Object, utils::buffd::{MsgParser, MsgParserError}, - wire::{wl_data_device::*, WlDataDeviceId, WlDataOfferId, WlDataSourceId, WlSurfaceId}, - xwayland::XWaylandEvent, + wire::{wl_data_device::*, WlDataDeviceId, WlDataOfferId, WlSurfaceId}, }, std::rc::Rc, thiserror::Error, @@ -38,60 +38,34 @@ impl WlDataDevice { client: &Rc, version: u32, seat: &Rc, - is_xwm: bool, ) -> Self { Self { id, client: client.clone(), version, seat: seat.clone(), - data: DeviceData { - selection: Default::default(), - dnd: Default::default(), - is_xwm, - }, + data: Default::default(), tracker: Default::default(), } } pub fn send_data_offer(&self, offer: &Rc) { - if self.data.is_xwm { - self.client - .state - .xwayland - .queue - .push(XWaylandEvent::ClipboardSetOffer(offer.clone())); - } else { - self.client.event(DataOffer { - self_id: self.id, - id: offer.id, - }) - } + self.client.event(DataOffer { + self_id: self.id, + id: offer.id, + }) } pub fn send_selection(&self, offer: Option<&Rc>) { - if self.data.is_xwm { - self.client - .state - .xwayland - .queue - .push(XWaylandEvent::ClipboardSetSelection( - self.seat.id(), - offer.cloned(), - )); - } else { - let id = offer.map(|o| o.id).unwrap_or(WlDataOfferId::NONE); - self.client.event(Selection { - self_id: self.id, - id, - }) - } + let id = offer.map(|o| o.id).unwrap_or(WlDataOfferId::NONE); + self.client.event(Selection { + self_id: self.id, + id, + }) } pub fn send_leave(&self) { - if !self.data.is_xwm { - self.client.event(Leave { self_id: self.id }) - } + self.client.event(Leave { self_id: self.id }) } pub fn send_enter( @@ -102,33 +76,27 @@ impl WlDataDevice { offer: WlDataOfferId, serial: u32, ) { - if !self.data.is_xwm { - self.client.event(Enter { - self_id: self.id, - serial, - surface, - x, - y, - id: offer, - }) - } + self.client.event(Enter { + self_id: self.id, + serial, + surface, + x, + y, + id: offer, + }) } pub fn send_motion(&self, time_usec: u64, x: Fixed, y: Fixed) { - if !self.data.is_xwm { - self.client.event(Motion { - self_id: self.id, - time: (time_usec / 1000) as _, - x, - y, - }) - } + self.client.event(Motion { + self_id: self.id, + time: (time_usec / 1000) as _, + x, + y, + }) } pub fn send_drop(&self) { - if !self.data.is_xwm { - self.client.event(Drop { self_id: self.id }) - } + self.client.event(Drop { self_id: self.id }) } fn start_drag(&self, parser: MsgParser<'_, '_>) -> Result<(), WlDataDeviceError> { @@ -185,17 +153,18 @@ impl WlDataDevice { pub struct ClipboardIpc; -impl XIpcVtable for ClipboardIpc { - fn create_xwm_source(client: &Rc) -> Self::Source { - WlDataSource::new(WlDataSourceId::NONE, client, true, 3) - } - - fn remove_from_seat(device: &Self::Device) { - device.seat.remove_data_device(device); +impl IterableIpcVtable for ClipboardIpc { + fn for_each_device(seat: &WlSeatGlobal, client: ClientId, f: C) + where + C: FnMut(&Rc), + { + seat.for_each_data_device(0, client, f); } } impl IpcVtable for ClipboardIpc { + const LOCATION: IpcLocation = IpcLocation::Clipboard; + type Device = WlDataDevice; type Source = WlDataSource; type Offer = WlDataOffer; @@ -216,27 +185,20 @@ impl IpcVtable for ClipboardIpc { seat.set_wl_data_source_selection(Some(source.clone()), serial) } - fn for_each_device(seat: &WlSeatGlobal, client: ClientId, f: C) - where - C: FnMut(&Rc), - { - seat.for_each_data_device(0, client, f); - } - fn create_offer( - client: &Rc, device: &Rc, offer_data: OfferData, ) -> Result, ClientError> { let rc = Rc::new(WlDataOffer { - id: client.new_id()?, - offer_id: client.state.data_offer_ids.next(), - client: client.clone(), + id: device.client.new_id()?, + offer_id: device.client.state.data_offer_ids.next(), + client: device.client.clone(), device: device.clone(), data: offer_data, tracker: Default::default(), }); - track!(client, rc); + track!(device.client, rc); + device.client.add_server_obj(&rc); Ok(rc) } @@ -254,6 +216,10 @@ impl IpcVtable for ClipboardIpc { Role::Dnd => seat.cancel_dnd(), } } + + fn device_client(dd: &Rc) -> &Rc { + &dd.client + } } object_base! { diff --git a/src/ifs/ipc/wl_data_device_manager.rs b/src/ifs/ipc/wl_data_device_manager.rs index 5feaa5dd..226dd8e9 100644 --- a/src/ifs/ipc/wl_data_device_manager.rs +++ b/src/ifs/ipc/wl_data_device_manager.rs @@ -61,7 +61,7 @@ impl WlDataDeviceManager { parser: MsgParser<'_, '_>, ) -> Result<(), WlDataDeviceManagerError> { let req: CreateDataSource = self.client.parse(self, parser)?; - let res = Rc::new(WlDataSource::new(req.id, &self.client, false, self.version)); + let res = Rc::new(WlDataSource::new(req.id, &self.client, self.version)); track!(self.client, res); self.client.add_client_obj(&res)?; Ok(()) @@ -78,7 +78,6 @@ impl WlDataDeviceManager { &self.client, self.version, &seat.global, - false, )); track!(self.client, dev); seat.global.add_data_device(&dev); diff --git a/src/ifs/ipc/wl_data_offer.rs b/src/ifs/ipc/wl_data_offer.rs index 7553dbff..17078882 100644 --- a/src/ifs/ipc/wl_data_offer.rs +++ b/src/ifs/ipc/wl_data_offer.rs @@ -19,7 +19,6 @@ use { buffd::{MsgParser, MsgParserError}, }, wire::{wl_data_offer::*, WlDataOfferId, WlSurfaceId}, - xwayland::XWaylandEvent, }, std::rc::Rc, thiserror::Error, @@ -64,8 +63,8 @@ impl DynDataOffer for WlDataOffer { WlDataOffer::send_action(self, action); } - fn send_offer(self: Rc, mime_type: &str) { - WlDataOffer::send_offer(&self, mime_type); + fn send_offer(&self, mime_type: &str) { + WlDataOffer::send_offer(self, mime_type); } fn destroy(&self) { @@ -90,46 +89,29 @@ impl DynDataOffer for WlDataOffer { } impl WlDataOffer { - pub fn send_offer(self: &Rc, mime_type: &str) { - if self.data.is_xwm { - if let Some(src) = self.data.source.get() { - if !src.source_data().is_xwm { - self.client.state.xwayland.queue.push( - XWaylandEvent::ClipboardAddOfferMimeType( - self.clone(), - mime_type.to_string(), - ), - ); - } - } - } else { - self.client.event(Offer { - self_id: self.id, - mime_type, - }) - } + pub fn send_offer(&self, mime_type: &str) { + self.client.event(Offer { + self_id: self.id, + mime_type, + }) } pub fn send_source_actions(&self) { - if !self.data.is_xwm { - if let Some(src) = self.data.source.get() { - if let Some(source_actions) = src.source_data().actions.get() { - self.client.event(SourceActions { - self_id: self.id, - source_actions, - }) - } + if let Some(src) = self.data.source.get() { + if let Some(source_actions) = src.source_data().actions.get() { + self.client.event(SourceActions { + self_id: self.id, + source_actions, + }) } } } pub fn send_action(&self, dnd_action: u32) { - if !self.data.is_xwm { - self.client.event(Action { - self_id: self.id, - dnd_action, - }) - } + self.client.event(Action { + self_id: self.id, + dnd_action, + }) } fn accept(&self, parser: MsgParser<'_, '_>) -> Result<(), WlDataOfferError> { diff --git a/src/ifs/ipc/wl_data_source.rs b/src/ifs/ipc/wl_data_source.rs index 74aa0948..d8b790a3 100644 --- a/src/ifs/ipc/wl_data_source.rs +++ b/src/ifs/ipc/wl_data_source.rs @@ -4,9 +4,10 @@ use { ifs::{ ipc::{ add_data_source_mime_type, break_source_loops, cancel_offers, destroy_data_source, - detach_seat, offer_source_to, + detach_seat, offer_source_to_regular_client, offer_source_to_x, wl_data_device::ClipboardIpc, wl_data_device_manager::{DND_ALL, DND_NONE}, + x_data_device::{XClipboardIpc, XIpcDevice}, DataSource, DynDataOffer, DynDataSource, SharedState, SourceData, OFFER_STATE_ACCEPTED, OFFER_STATE_DROPPED, SOURCE_STATE_CANCELLED, SOURCE_STATE_DROPPED, @@ -23,7 +24,6 @@ use { clonecell::CloneCell, }, wire::{wl_data_source::*, WlDataSourceId}, - xwayland::XWaylandEvent, }, std::rc::Rc, thiserror::Error, @@ -44,7 +44,7 @@ pub struct WlDataSource { } impl DataSource for WlDataSource { - fn send_cancelled(self: &Rc, seat: &Rc) { + fn send_cancelled(&self, seat: &Rc) { WlDataSource::send_cancelled(self, seat); } } @@ -54,20 +54,24 @@ impl DynDataSource for WlDataSource { &self.data } - fn send_send(self: Rc, mime_type: &str, fd: Rc) { - WlDataSource::send_send(&self, mime_type, fd); + fn send_send(&self, mime_type: &str, fd: Rc) { + WlDataSource::send_send(self, mime_type, fd); } - fn offer_to(self: Rc, client: &Rc) { - offer_source_to::(&self, client); + fn offer_to_regular_client(self: Rc, client: &Rc) { + offer_source_to_regular_client::(&self, client); } - fn detach_seat(self: Rc, seat: &Rc) { - detach_seat::(&self, seat); + fn offer_to_x(self: Rc, dd: &Rc) { + offer_source_to_x::(&self, dd); + } + + fn detach_seat(&self, seat: &Rc) { + detach_seat(self, seat); } fn cancel_offers(&self) { - cancel_offers::(self); + cancel_offers(self); } fn send_target(&self, mime_type: Option<&str>) { @@ -84,11 +88,11 @@ impl DynDataSource for WlDataSource { } impl WlDataSource { - pub fn new(id: WlDataSourceId, client: &Rc, is_xwm: bool, version: u32) -> Self { + pub fn new(id: WlDataSourceId, client: &Rc, version: u32) -> Self { Self { id, tracker: Default::default(), - data: SourceData::new(client, is_xwm), + data: SourceData::new(client), version, toplevel_drag: Default::default(), } @@ -108,7 +112,7 @@ impl WlDataSource { self.data.shared.set(Rc::new(SharedState::default())); self.send_target(None); self.send_action(DND_NONE); - cancel_offers::(self); + cancel_offers(self); } pub fn update_selected_action(&self) { @@ -159,74 +163,44 @@ impl WlDataSource { shared.state.or_assign(OFFER_STATE_DROPPED); } - pub fn send_cancelled(self: &Rc, seat: &Rc) { - if self.data.is_xwm { - self.data - .client - .state - .xwayland - .queue - .push(XWaylandEvent::ClipboardCancelSource(self.clone())); - } else { - self.data.state.or_assign(SOURCE_STATE_CANCELLED); - if let Some(drag) = self.toplevel_drag.take() { - drag.finish_drag(seat); - } - self.data.client.event(Cancelled { self_id: self.id }) + pub fn send_cancelled(&self, seat: &Rc) { + self.data.state.or_assign(SOURCE_STATE_CANCELLED); + if let Some(drag) = self.toplevel_drag.take() { + drag.finish_drag(seat); } + self.data.client.event(Cancelled { self_id: self.id }) } - pub fn send_send(self: &Rc, mime_type: &str, fd: Rc) { - if self.data.is_xwm { - self.data - .client - .state - .xwayland - .queue - .push(XWaylandEvent::ClipboardSendSource( - self.clone(), - mime_type.to_string(), - fd, - )); - } else { - self.data.client.event(Send { - self_id: self.id, - mime_type, - fd, - }) - } + 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_target(&self, mime_type: Option<&str>) { - if !self.data.is_xwm { - self.data.client.event(Target { - self_id: self.id, - mime_type, - }) - } + self.data.client.event(Target { + self_id: self.id, + mime_type, + }) } pub fn send_dnd_finished(&self) { - if !self.data.is_xwm { - self.data.client.event(DndFinished { self_id: self.id }) - } + self.data.client.event(DndFinished { self_id: self.id }) } pub fn send_action(&self, dnd_action: u32) { - if !self.data.is_xwm { - self.data.client.event(Action { - self_id: self.id, - dnd_action, - }) - } + self.data.client.event(Action { + self_id: self.id, + dnd_action, + }) } pub fn send_dnd_drop_performed(&self) { - if !self.data.is_xwm { - self.data - .client - .event(DndDropPerformed { self_id: self.id }) - } + self.data + .client + .event(DndDropPerformed { self_id: self.id }) } fn offer(&self, parser: MsgParser<'_, '_>) -> Result<(), WlDataSourceError> { diff --git a/src/ifs/ipc/x_data_device.rs b/src/ifs/ipc/x_data_device.rs new file mode 100644 index 00000000..e9f6e4a0 --- /dev/null +++ b/src/ifs/ipc/x_data_device.rs @@ -0,0 +1,134 @@ +use { + crate::{ + client::{Client, ClientError}, + ifs::{ + ipc::{ + x_data_offer::XDataOffer, x_data_source::XDataSource, DeviceData, IpcLocation, + IpcVtable, OfferData, Role, + }, + wl_seat::{WlSeatError, WlSeatGlobal}, + }, + state::State, + xwayland::XWaylandEvent, + }, + std::rc::Rc, + XWaylandEvent::IpcSetOffer, +}; + +linear_ids!(XIpcDeviceIds, XIpcDeviceId, u64); + +pub struct XIpcDevice { + pub id: XIpcDeviceId, + pub clipboard: DeviceData, + pub primary_selection: DeviceData, + pub seat: Rc, + pub state: Rc, + pub client: Rc, +} + +#[derive(Default)] +pub struct XClipboardIpc; + +#[derive(Default)] +pub struct XPrimarySelectionIpc; + +pub trait XIpc { + const LOCATION: IpcLocation; + + fn x_unset(seat: &Rc); + + fn x_device_data(dd: &XIpcDevice) -> &DeviceData; +} + +impl XIpc for XClipboardIpc { + const LOCATION: IpcLocation = IpcLocation::Clipboard; + + fn x_unset(seat: &Rc) { + seat.unset_selection(); + } + + fn x_device_data(dd: &XIpcDevice) -> &DeviceData { + &dd.clipboard + } +} + +impl XIpc for XPrimarySelectionIpc { + const LOCATION: IpcLocation = IpcLocation::PrimarySelection; + + fn x_unset(seat: &Rc) { + seat.unset_primary_selection(); + } + + fn x_device_data(dd: &XIpcDevice) -> &DeviceData { + &dd.primary_selection + } +} + +impl IpcVtable for T { + const LOCATION: IpcLocation = T::LOCATION; + type Device = XIpcDevice; + type Source = XDataSource; + type Offer = XDataOffer; + + fn get_device_data(dd: &Self::Device) -> &DeviceData { + T::x_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> { + 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, + ) -> Result, ClientError> { + debug_assert!(dd.client.is_xwayland); + let rc = Rc::new(XDataOffer { + offer_id: dd.state.data_offer_ids.next(), + device: dd.clone(), + data, + tracker: Default::default(), + location: T::LOCATION, + }); + track!(dd.client, rc); + Ok(rc) + } + + fn send_selection(dd: &Self::Device, offer: Option<&Rc>) { + dd.state + .xwayland + .queue + .push(XWaylandEvent::IpcSetSelection { + seat: dd.seat.id(), + location: T::LOCATION, + offer: offer.cloned(), + }); + } + + fn send_offer(dd: &Self::Device, offer: &Rc) { + dd.state.xwayland.queue.push(IpcSetOffer { + location: T::LOCATION, + seat: dd.seat.id(), + offer: offer.clone(), + }); + } + + fn unset(seat: &Rc, _role: Role) { + T::x_unset(seat) + } + + fn device_client(dd: &Rc) -> &Rc { + &dd.client + } +} diff --git a/src/ifs/ipc/x_data_offer.rs b/src/ifs/ipc/x_data_offer.rs new file mode 100644 index 00000000..7b9887dc --- /dev/null +++ b/src/ifs/ipc/x_data_offer.rs @@ -0,0 +1,70 @@ +use { + crate::{ + client::ClientId, + ifs::{ + ipc::{ + cancel_offer, destroy_data_offer, + x_data_device::{XClipboardIpc, XIpcDevice, XPrimarySelectionIpc}, + DataOffer, DataOfferId, DynDataOffer, IpcLocation, OfferData, + }, + wl_seat::WlSeatGlobal, + }, + leaks::Tracker, + xwayland::XWaylandEvent, + }, + std::rc::Rc, + XWaylandEvent::IpcAddOfferMimeType, +}; + +pub struct XDataOffer { + pub offer_id: DataOfferId, + pub device: Rc, + pub data: OfferData, + pub tracker: Tracker, + pub location: IpcLocation, +} + +impl DataOffer for XDataOffer { + type Device = XIpcDevice; + + fn offer_data(&self) -> &OfferData { + &self.data + } +} + +impl DynDataOffer for XDataOffer { + fn offer_id(&self) -> DataOfferId { + self.offer_id + } + + fn client_id(&self) -> ClientId { + self.device.client.id + } + + fn send_offer(&self, mime_type: &str) { + self.device.state.xwayland.queue.push(IpcAddOfferMimeType { + location: self.location, + seat: self.device.seat.id(), + offer: self.offer_id, + mime_type: mime_type.to_string(), + }) + } + + fn destroy(&self) { + match self.location { + IpcLocation::Clipboard => destroy_data_offer::(self), + IpcLocation::PrimarySelection => destroy_data_offer::(self), + } + } + + 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() + } +} diff --git a/src/ifs/ipc/x_data_source.rs b/src/ifs/ipc/x_data_source.rs new file mode 100644 index 00000000..cf2af861 --- /dev/null +++ b/src/ifs/ipc/x_data_source.rs @@ -0,0 +1,79 @@ +use { + crate::{ + client::Client, + ifs::{ + ipc::{ + cancel_offers, detach_seat, offer_source_to_regular_client, + wl_data_device::ClipboardIpc, x_data_device::XIpcDevice, + zwp_primary_selection_device_v1::PrimarySelectionIpc, DataSource, DynDataSource, + IpcLocation, SourceData, + }, + wl_seat::WlSeatGlobal, + }, + state::State, + xwayland::XWaylandEvent::{IpcCancelSource, IpcSendSource, IpcSetSelection}, + }, + std::rc::Rc, + uapi::OwnedFd, +}; + +pub struct XDataSource { + pub state: Rc, + pub device: Rc, + pub data: SourceData, + pub location: IpcLocation, +} + +impl DataSource for XDataSource { + fn send_cancelled(&self, seat: &Rc) { + self.state.xwayland.queue.push(IpcCancelSource { + location: self.location, + seat: seat.id(), + source: self.data.id, + }); + } +} + +impl DynDataSource for XDataSource { + fn source_data(&self) -> &SourceData { + &self.data + } + + fn send_send(&self, mime_type: &str, fd: Rc) { + self.state.xwayland.queue.push(IpcSendSource { + location: self.location, + seat: self.device.seat.id(), + source: self.data.id, + mime_type: mime_type.to_string(), + fd, + }); + } + + 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_offers(); + self.state.xwayland.queue.push(IpcSetSelection { + location: self.location, + seat: self.device.seat.id(), + offer: None, + }); + } + + fn detach_seat(&self, seat: &Rc) { + detach_seat(self, seat); + } + + fn cancel_offers(&self) { + cancel_offers(self) + } +} diff --git a/src/ifs/ipc/zwp_primary_selection_device_manager_v1.rs b/src/ifs/ipc/zwp_primary_selection_device_manager_v1.rs index 1b33871b..fd1768ae 100644 --- a/src/ifs/ipc/zwp_primary_selection_device_manager_v1.rs +++ b/src/ifs/ipc/zwp_primary_selection_device_manager_v1.rs @@ -55,11 +55,7 @@ impl ZwpPrimarySelectionDeviceManagerV1 { parser: MsgParser<'_, '_>, ) -> Result<(), ZwpPrimarySelectionDeviceManagerV1Error> { let req: CreateSource = self.client.parse(self, parser)?; - let res = Rc::new(ZwpPrimarySelectionSourceV1::new( - req.id, - &self.client, - false, - )); + let res = Rc::new(ZwpPrimarySelectionSourceV1::new(req.id, &self.client)); track!(self.client, res); self.client.add_client_obj(&res)?; Ok(()) @@ -76,7 +72,6 @@ impl ZwpPrimarySelectionDeviceManagerV1 { &self.client, self.version, &seat.global, - false, )); track!(self.client, dev); seat.global.add_primary_selection_device(&dev); diff --git a/src/ifs/ipc/zwp_primary_selection_device_v1.rs b/src/ifs/ipc/zwp_primary_selection_device_v1.rs index b9a09176..c73814a5 100644 --- a/src/ifs/ipc/zwp_primary_selection_device_v1.rs +++ b/src/ifs/ipc/zwp_primary_selection_device_v1.rs @@ -6,7 +6,7 @@ use { break_device_loops, destroy_data_device, zwp_primary_selection_offer_v1::ZwpPrimarySelectionOfferV1, zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1, DeviceData, - IpcVtable, OfferData, Role, XIpcVtable, + IpcLocation, IpcVtable, IterableIpcVtable, OfferData, Role, }, wl_seat::{WlSeatError, WlSeatGlobal}, }, @@ -15,9 +15,8 @@ use { utils::buffd::{MsgParser, MsgParserError}, wire::{ zwp_primary_selection_device_v1::*, ZwpPrimarySelectionDeviceV1Id, - ZwpPrimarySelectionOfferV1Id, ZwpPrimarySelectionSourceV1Id, + ZwpPrimarySelectionOfferV1Id, }, - xwayland::XWaylandEvent, }, std::rc::Rc, thiserror::Error, @@ -38,56 +37,32 @@ impl ZwpPrimarySelectionDeviceV1 { client: &Rc, version: u32, seat: &Rc, - is_xwm: bool, ) -> Self { Self { id, client: client.clone(), version, seat: seat.clone(), - data: DeviceData { - selection: Default::default(), - dnd: Default::default(), - is_xwm, - }, + data: Default::default(), tracker: Default::default(), } } pub fn send_data_offer(&self, offer: &Rc) { - if self.data.is_xwm { - self.client - .state - .xwayland - .queue - .push(XWaylandEvent::PrimarySelectionSetOffer(offer.clone())); - } else { - self.client.event(DataOffer { - self_id: self.id, - offer: offer.id, - }) - } + self.client.event(DataOffer { + self_id: self.id, + offer: offer.id, + }) } pub fn send_selection(&self, offer: Option<&Rc>) { - if self.data.is_xwm { - self.client - .state - .xwayland - .queue - .push(XWaylandEvent::PrimarySelectionSetSelection( - self.seat.id(), - offer.cloned(), - )); - } else { - let id = offer - .map(|o| o.id) - .unwrap_or(ZwpPrimarySelectionOfferV1Id::NONE); - self.client.event(Selection { - self_id: self.id, - id, - }) - } + let id = offer + .map(|o| o.id) + .unwrap_or(ZwpPrimarySelectionOfferV1Id::NONE); + self.client.event(Selection { + self_id: self.id, + id, + }) } fn set_selection( @@ -126,17 +101,18 @@ impl ZwpPrimarySelectionDeviceV1 { pub struct PrimarySelectionIpc; -impl XIpcVtable for PrimarySelectionIpc { - fn create_xwm_source(client: &Rc) -> Self::Source { - ZwpPrimarySelectionSourceV1::new(ZwpPrimarySelectionSourceV1Id::NONE, client, true) - } - - fn remove_from_seat(device: &Self::Device) { - device.seat.remove_primary_selection_device(device); +impl IterableIpcVtable for PrimarySelectionIpc { + fn for_each_device(seat: &WlSeatGlobal, client: ClientId, f: C) + where + C: FnMut(&Rc), + { + seat.for_each_primary_selection_device(0, client, f) } } impl IpcVtable for PrimarySelectionIpc { + const LOCATION: IpcLocation = IpcLocation::PrimarySelection; + type Device = ZwpPrimarySelectionDeviceV1; type Source = ZwpPrimarySelectionSourceV1; type Offer = ZwpPrimarySelectionOfferV1; @@ -157,32 +133,20 @@ impl IpcVtable for PrimarySelectionIpc { seat.set_zwp_primary_selection(Some(source.clone()), serial) } - fn for_each_device(seat: &WlSeatGlobal, client: ClientId, f: C) - where - C: FnMut(&Rc), - { - seat.for_each_primary_selection_device(0, client, f) - } - fn create_offer( - client: &Rc, device: &Rc, offer_data: OfferData, ) -> Result, ClientError> { - let id = if device.data.is_xwm { - ZwpPrimarySelectionOfferV1Id::NONE - } else { - client.new_id()? - }; let rc = Rc::new(ZwpPrimarySelectionOfferV1 { - id, - offer_id: client.state.data_offer_ids.next(), + id: device.client.new_id()?, + offer_id: device.client.state.data_offer_ids.next(), seat: device.seat.clone(), - client: client.clone(), + client: device.client.clone(), data: offer_data, tracker: Default::default(), }); - track!(client, rc); + track!(device.client, rc); + device.client.add_server_obj(&rc); Ok(rc) } @@ -197,6 +161,10 @@ impl IpcVtable for PrimarySelectionIpc { fn unset(seat: &Rc, _role: Role) { seat.unset_primary_selection(); } + + fn device_client(dd: &Rc) -> &Rc { + &dd.client + } } object_base! { diff --git a/src/ifs/ipc/zwp_primary_selection_offer_v1.rs b/src/ifs/ipc/zwp_primary_selection_offer_v1.rs index 54fba396..d6a7caa3 100644 --- a/src/ifs/ipc/zwp_primary_selection_offer_v1.rs +++ b/src/ifs/ipc/zwp_primary_selection_offer_v1.rs @@ -15,7 +15,6 @@ use { object::Object, utils::buffd::{MsgParser, MsgParserError}, wire::{zwp_primary_selection_offer_v1::*, ZwpPrimarySelectionOfferV1Id}, - xwayland::XWaylandEvent, }, std::rc::Rc, thiserror::Error, @@ -47,8 +46,8 @@ impl DynDataOffer for ZwpPrimarySelectionOfferV1 { self.client.id } - fn send_offer(self: Rc, mime_type: &str) { - ZwpPrimarySelectionOfferV1::send_offer(&self, mime_type); + fn send_offer(&self, mime_type: &str) { + ZwpPrimarySelectionOfferV1::send_offer(self, mime_type); } fn destroy(&self) { @@ -65,24 +64,11 @@ impl DynDataOffer for ZwpPrimarySelectionOfferV1 { } impl ZwpPrimarySelectionOfferV1 { - pub fn send_offer(self: &Rc, mime_type: &str) { - if self.data.is_xwm { - if let Some(src) = self.data.source.get() { - if !src.source_data().is_xwm { - self.client.state.xwayland.queue.push( - XWaylandEvent::PrimarySelectionAddOfferMimeType( - self.clone(), - mime_type.to_string(), - ), - ); - } - } - } else { - self.client.event(Offer { - self_id: self.id, - mime_type, - }) - } + pub fn send_offer(&self, mime_type: &str) { + self.client.event(Offer { + self_id: self.id, + mime_type, + }) } fn receive(&self, parser: MsgParser<'_, '_>) -> Result<(), ZwpPrimarySelectionOfferV1Error> { diff --git a/src/ifs/ipc/zwp_primary_selection_source_v1.rs b/src/ifs/ipc/zwp_primary_selection_source_v1.rs index a657882b..d58861cd 100644 --- a/src/ifs/ipc/zwp_primary_selection_source_v1.rs +++ b/src/ifs/ipc/zwp_primary_selection_source_v1.rs @@ -4,7 +4,9 @@ use { ifs::{ ipc::{ add_data_source_mime_type, break_source_loops, cancel_offers, destroy_data_source, - detach_seat, offer_source_to, zwp_primary_selection_device_v1::PrimarySelectionIpc, + detach_seat, offer_source_to_regular_client, offer_source_to_x, + x_data_device::{XIpcDevice, XPrimarySelectionIpc}, + zwp_primary_selection_device_v1::PrimarySelectionIpc, DataSource, DynDataSource, SourceData, }, wl_seat::WlSeatGlobal, @@ -13,7 +15,6 @@ use { object::Object, utils::buffd::{MsgParser, MsgParserError}, wire::{zwp_primary_selection_source_v1::*, ZwpPrimarySelectionSourceV1Id}, - xwayland::XWaylandEvent, }, std::rc::Rc, thiserror::Error, @@ -27,7 +28,7 @@ pub struct ZwpPrimarySelectionSourceV1 { } impl DataSource for ZwpPrimarySelectionSourceV1 { - fn send_cancelled(self: &Rc, _seat: &Rc) { + fn send_cancelled(&self, _seat: &Rc) { ZwpPrimarySelectionSourceV1::send_cancelled(self); } } @@ -37,64 +38,46 @@ impl DynDataSource for ZwpPrimarySelectionSourceV1 { &self.data } - fn send_send(self: Rc, mime_type: &str, fd: Rc) { - ZwpPrimarySelectionSourceV1::send_send(&self, mime_type, fd) + fn send_send(&self, mime_type: &str, fd: Rc) { + ZwpPrimarySelectionSourceV1::send_send(self, mime_type, fd) } - fn offer_to(self: Rc, client: &Rc) { - offer_source_to::(&self, client); + fn offer_to_regular_client(self: Rc, client: &Rc) { + offer_source_to_regular_client::(&self, client); } - fn detach_seat(self: Rc, seat: &Rc) { - detach_seat::(&self, seat); + fn offer_to_x(self: Rc, dd: &Rc) { + offer_source_to_x::(&self, dd); + } + + fn detach_seat(&self, seat: &Rc) { + detach_seat(self, seat); } fn cancel_offers(&self) { - cancel_offers::(self); + cancel_offers(self); } } impl ZwpPrimarySelectionSourceV1 { - pub fn new(id: ZwpPrimarySelectionSourceV1Id, client: &Rc, is_xwm: bool) -> Self { + pub fn new(id: ZwpPrimarySelectionSourceV1Id, client: &Rc) -> Self { Self { id, - data: SourceData::new(client, is_xwm), + data: SourceData::new(client), tracker: Default::default(), } } - pub fn send_cancelled(self: &Rc) { - if self.data.is_xwm { - self.data - .client - .state - .xwayland - .queue - .push(XWaylandEvent::PrimarySelectionCancelSource(self.clone())); - } else { - self.data.client.event(Cancelled { self_id: self.id }); - } + pub fn send_cancelled(&self) { + self.data.client.event(Cancelled { self_id: self.id }); } - pub fn send_send(self: &Rc, mime_type: &str, fd: Rc) { - if self.data.is_xwm { - self.data - .client - .state - .xwayland - .queue - .push(XWaylandEvent::PrimarySelectionSendSource( - self.clone(), - mime_type.to_string(), - fd, - )); - } else { - self.data.client.event(Send { - self_id: self.id, - mime_type, - fd, - }) - } + pub fn send_send(&self, mime_type: &str, fd: Rc) { + self.data.client.event(Send { + self_id: self.id, + mime_type, + fd, + }) } fn offer(&self, parser: MsgParser<'_, '_>) -> Result<(), ZwpPrimarySelectionSourceV1Error> { diff --git a/src/ifs/wl_seat.rs b/src/ifs/wl_seat.rs index 1a3d6dc7..b305433d 100644 --- a/src/ifs/wl_seat.rs +++ b/src/ifs/wl_seat.rs @@ -22,6 +22,7 @@ use { self, wl_data_device::{ClipboardIpc, WlDataDevice}, wl_data_source::WlDataSource, + x_data_device::{XClipboardIpc, XIpcDevice, XIpcDeviceId, XPrimarySelectionIpc}, zwp_primary_selection_device_v1::{ PrimarySelectionIpc, ZwpPrimarySelectionDeviceV1, }, @@ -58,6 +59,7 @@ use { linkedlist::LinkedNode, numcell::NumCell, rc_eq::rc_eq, + smallmap::SmallMap, transform_ext::TransformExt, }, wire::{ @@ -108,7 +110,7 @@ pub struct DroppedDnd { impl Drop for DroppedDnd { fn drop(&mut self) { if let Some(src) = self.dnd.src.take() { - ipc::detach_seat::(&src, &self.dnd.seat); + ipc::detach_seat(&*src, &self.dnd.seat); } } } @@ -131,6 +133,7 @@ pub struct WlSeatGlobal { keyboard_node: CloneCell>, pressed_keys: RefCell>, bindings: RefCell>>>, + x_data_devices: SmallMap, 1>, data_devices: RefCell>>>, primary_selection_devices: RefCell< AHashMap< @@ -190,6 +193,7 @@ impl WlSeatGlobal { keyboard_node: CloneCell::new(state.root.clone()), pressed_keys: RefCell::new(Default::default()), bindings: Default::default(), + x_data_devices: Default::default(), data_devices: RefCell::new(Default::default()), primary_selection_devices: RefCell::new(Default::default()), repeat_rate: Cell::new((25, 250)), @@ -349,6 +353,20 @@ impl WlSeatGlobal { .insert(device.id, device.clone()); } + pub fn set_x_data_device(&self, device: &Rc) { + self.x_data_devices.insert(device.id, device.clone()); + } + + pub fn unset_x_data_device(&self, id: XIpcDeviceId) { + self.x_data_devices.remove(&id); + } + + pub fn for_each_x_data_device(&self, mut f: impl FnMut(&Rc)) { + for (_, dev) in &self.x_data_devices { + f(&dev); + } + } + pub fn remove_data_device(&self, device: &WlDataDevice) { let mut dd = self.data_devices.borrow_mut(); if let Entry::Occupied(mut e) = dd.entry(device.client.id) { @@ -709,11 +727,16 @@ impl WlSeatGlobal { } } - fn set_selection_( + fn set_selection_( self: &Rc, field: &CloneCell>>, src: Option>, - ) -> Result<(), WlSeatError> { + ) -> Result<(), WlSeatError> + where + T: ipc::IterableIpcVtable, + X: ipc::IpcVtable, + S: DynDataSource, + { if let (Some(new), Some(old)) = (&src, &field.get()) { if new.source_data().id == old.source_data().id { return Ok(()); @@ -727,15 +750,36 @@ impl WlSeatGlobal { old.detach_seat(self); } if let Some(client) = self.keyboard_node.get().node_client() { - match src { - Some(src) => ipc::offer_source_to::(&src, &client), + self.offer_selection_to_client::(src.map(|v| v as Rc<_>), &client); + // client.flush(); + } + Ok(()) + } + + fn offer_selection_to_client( + &self, + selection: Option>, + client: &Rc, + ) where + T: ipc::IterableIpcVtable, + X: ipc::IpcVtable, + { + if let Some(src) = &selection { + src.cancel_offers(); + } + if client.is_xwayland { + self.for_each_x_data_device(|dd| match &selection { + Some(src) => src.clone().offer_to_x(&dd), + _ => X::send_selection(&dd, None), + }); + } else { + match selection { + Some(src) => src.offer_to_regular_client(client), _ => T::for_each_device(self, client.id, |device| { T::send_selection(device, None); }), } - // client.flush(); } - Ok(()) } pub fn start_drag( @@ -780,7 +824,7 @@ impl WlSeatGlobal { self: &Rc, selection: Option>, ) -> Result<(), WlSeatError> { - self.set_selection_::(&self.selection, selection) + self.set_selection_::(&self.selection, selection) } pub fn may_modify_selection(&self, client: &Rc, serial: u32) -> bool { @@ -821,7 +865,10 @@ impl WlSeatGlobal { self: &Rc, selection: Option>, ) -> Result<(), WlSeatError> { - self.set_selection_::(&self.primary_selection, selection) + self.set_selection_::( + &self.primary_selection, + selection, + ) } pub fn reload_known_cursor(&self) { diff --git a/src/ifs/wl_seat/event_handling.rs b/src/ifs/wl_seat/event_handling.rs index 57748764..67263026 100644 --- a/src/ifs/wl_seat/event_handling.rs +++ b/src/ifs/wl_seat/event_handling.rs @@ -4,10 +4,13 @@ use { client::ClientId, fixed::Fixed, ifs::{ - ipc, ipc::{ wl_data_device::{ClipboardIpc, WlDataDevice}, - zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1, + x_data_device::{XClipboardIpc, XPrimarySelectionIpc}, + zwp_primary_selection_device_v1::{ + PrimarySelectionIpc, ZwpPrimarySelectionDeviceV1, + }, + DynDataSource, }, wl_seat::{ wl_keyboard::{self, WlKeyboard}, @@ -742,22 +745,14 @@ impl WlSeatGlobal { }); if self.keyboard_node.get().node_client_id() != Some(surface.client.id) { - match self.selection.get() { - Some(sel) => sel.offer_to(&surface.client), - None => { - self.for_each_data_device(0, surface.client.id, |dd| { - dd.send_selection(None); - }); - } - } - match self.primary_selection.get() { - Some(sel) => sel.offer_to(&surface.client), - None => { - self.for_each_primary_selection_device(0, surface.client.id, |dd| { - dd.send_selection(None); - }); - } - } + self.offer_selection_to_client::( + self.selection.get(), + &surface.client, + ); + self.offer_selection_to_client::( + self.primary_selection.get(), + &surface.client, + ); } } } @@ -819,7 +814,9 @@ impl WlSeatGlobal { serial: u32, ) { if let Some(src) = &dnd.src { - ipc::offer_source_to::(src, &surface.client); + if !surface.client.is_xwayland { + src.clone().offer_to_regular_client(&surface.client); + } src.for_each_data_offer(|offer| { offer.send_enter(surface.id, x, y, serial); offer.send_source_actions(); diff --git a/src/ifs/wl_seat/pointer_owner.rs b/src/ifs/wl_seat/pointer_owner.rs index 91c82e5d..7d4490c2 100644 --- a/src/ifs/wl_seat/pointer_owner.rs +++ b/src/ifs/wl_seat/pointer_owner.rs @@ -4,7 +4,7 @@ use { fixed::Fixed, ifs::{ ipc, - ipc::{wl_data_device::ClipboardIpc, wl_data_source::WlDataSource}, + ipc::wl_data_source::WlDataSource, wl_seat::{ wl_pointer::PendingScroll, Dnd, DroppedDnd, WlSeatError, WlSeatGlobal, CHANGE_CURSOR_MOVED, @@ -456,7 +456,7 @@ impl PointerOwner for DndPointerOwner { target.node_seat_state().remove_dnd_target(seat); if !should_drop { if let Some(src) = &self.dnd.src { - ipc::detach_seat::(src, seat); + ipc::detach_seat(&**src, seat); } } if let Some(icon) = self.icon.get() { @@ -527,7 +527,7 @@ impl PointerOwner for DndPointerOwner { target.node_on_dnd_leave(&self.dnd); target.node_seat_state().remove_dnd_target(seat); if let Some(src) = &self.dnd.src { - ipc::detach_seat::(src, seat); + ipc::detach_seat(&**src, seat); } if let Some(icon) = self.icon.get() { icon.set_dnd_icon_seat(seat.id(), None); diff --git a/src/state.rs b/src/state.rs index 89601ebe..b18b6399 100644 --- a/src/state.rs +++ b/src/state.rs @@ -26,7 +26,7 @@ use { ifs::{ ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1, ext_session_lock_v1::ExtSessionLockV1, - ipc::{DataOfferIds, DataSourceIds}, + ipc::{x_data_device::XIpcDeviceIds, DataOfferIds, DataSourceIds}, jay_render_ctx::JayRenderCtx, jay_seat_events::JaySeatEvents, jay_workspace_watcher::JayWorkspaceWatcher, @@ -198,6 +198,7 @@ pub struct XWaylandState { pub enabled: Cell, pub handler: RefCell>>, pub queue: Rc>, + pub ipc_device_ids: XIpcDeviceIds, } pub struct IdleState { diff --git a/src/xwayland.rs b/src/xwayland.rs index e98199f8..79e5a398 100644 --- a/src/xwayland.rs +++ b/src/xwayland.rs @@ -7,11 +7,7 @@ use { compositor::DISPLAY, forker::{ForkerError, ForkerProxy}, ifs::{ - ipc::{ - wl_data_offer::WlDataOffer, wl_data_source::WlDataSource, - zwp_primary_selection_offer_v1::ZwpPrimarySelectionOfferV1, - zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1, - }, + ipc::{x_data_offer::XDataOffer, DataOfferId, DataSourceId, IpcLocation}, wl_seat::SeatId, wl_surface::x_surface::xwindow::{Xwindow, XwindowData}, }, @@ -237,15 +233,32 @@ pub enum XWaylandEvent { #[allow(dead_code)] SeatChanged, - PrimarySelectionCancelSource(Rc), - PrimarySelectionSendSource(Rc, String, Rc), - PrimarySelectionSetOffer(Rc), - PrimarySelectionSetSelection(SeatId, Option>), - PrimarySelectionAddOfferMimeType(Rc, String), - - ClipboardCancelSource(Rc), - ClipboardSendSource(Rc, String, Rc), - ClipboardSetOffer(Rc), - ClipboardSetSelection(SeatId, Option>), - ClipboardAddOfferMimeType(Rc, String), + IpcCancelSource { + location: IpcLocation, + seat: SeatId, + source: DataSourceId, + }, + IpcSendSource { + location: IpcLocation, + seat: SeatId, + source: DataSourceId, + mime_type: String, + fd: Rc, + }, + IpcSetOffer { + location: IpcLocation, + seat: SeatId, + offer: Rc, + }, + IpcSetSelection { + location: IpcLocation, + seat: SeatId, + offer: Option>, + }, + IpcAddOfferMimeType { + location: IpcLocation, + seat: SeatId, + offer: DataOfferId, + mime_type: String, + }, } diff --git a/src/xwayland/xwm.rs b/src/xwayland/xwm.rs index 9b9a8d25..e28f7724 100644 --- a/src/xwayland/xwm.rs +++ b/src/xwayland/xwm.rs @@ -8,11 +8,11 @@ use { ipc::{ add_data_source_mime_type, destroy_data_device, destroy_data_offer, destroy_data_source, receive_data_offer, - wl_data_device::{ClipboardIpc, WlDataDevice}, - zwp_primary_selection_device_v1::{ - PrimarySelectionIpc, ZwpPrimarySelectionDeviceV1, - }, - DataOffer, DynDataOffer, DynDataSource, IpcVtable, XIpcVtable, + x_data_device::{XClipboardIpc, XIpc, XIpcDevice, XPrimarySelectionIpc}, + x_data_offer::XDataOffer, + x_data_source::XDataSource, + DataOfferId, DataSourceId, DynDataOffer, DynDataSource, IpcLocation, IpcVtable, + SourceData, }, wl_seat::{SeatId, WlSeatGlobal}, wl_surface::{ @@ -30,7 +30,7 @@ use { copyhashmap::CopyHashMap, errorfmt::ErrorFmt, linkedlist::LinkedList, numcell::NumCell, oserror::OsError, rc_eq::rc_eq, }, - wire::{WlDataDeviceId, WlSurfaceId, ZwpPrimarySelectionDeviceV1Id}, + wire::WlSurfaceId, wire_xcon::{ ChangeProperty, ChangeWindowAttributes, ClientMessage, CompositeRedirectSubwindows, ConfigureNotify, ConfigureRequest, ConfigureWindow, ConfigureWindowValues, @@ -67,6 +67,7 @@ use { std::{ borrow::Cow, cell::{Cell, RefCell}, + marker::PhantomData, mem::{self}, ops::{Deref, DerefMut}, rc::Rc, @@ -151,47 +152,30 @@ atoms! { XdndTypeList, } -struct EnhancedOffer { - offer: Rc, +struct EnhancedOffer { + offer: Rc, mime_types: RefCell>, active: Cell, } -struct SelectionData { - devices: CopyHashMap>, - sources: CopyHashMap>, - offers: CopyHashMap>>, - active_offer: CloneCell>>>, +#[derive(Default)] +struct SelectionData { + sources: CopyHashMap>, + offers: CopyHashMap>, + active_offer: CloneCell>>, win: Cell, selection: Cell, pending_transfers: RefCell>, + _phantom: PhantomData, } -impl Default for SelectionData { - fn default() -> Self { - Self { - devices: Default::default(), - sources: Default::default(), - offers: Default::default(), - active_offer: Default::default(), - win: Cell::new(0), - selection: Cell::new(0), - pending_transfers: RefCell::new(vec![]), - } - } -} - -impl SelectionData { +impl SelectionData { fn destroy(&self) { for (_, offer) in self.offers.lock().drain() { destroy_data_offer::(&offer.offer); } self.active_offer.take(); self.destroy_sources(); - for (_, device) in self.devices.lock().drain() { - destroy_data_device::(&device); - T::remove_from_seat(&device); - } } fn destroy_sources(&self) { @@ -208,14 +192,14 @@ impl SelectionData { } self.offers.remove(&id); self.sources.remove(&id); - self.devices.remove(&id); } } #[derive(Default)] pub struct XwmShared { - data: SelectionData, - primary_selection: SelectionData, + devices: CopyHashMap>, + data: SelectionData, + primary_selection: SelectionData, transfers: CopyHashMap>, } @@ -223,6 +207,11 @@ impl Drop for XwmShared { fn drop(&mut self) { self.data.destroy(); self.primary_selection.destroy(); + for (_, device) in self.devices.lock().drain() { + destroy_data_device::(&device); + destroy_data_device::(&device); + device.seat.unset_x_data_device(device.id); + } self.transfers.clear(); } } @@ -557,27 +546,19 @@ impl Wm { for seat in removed_seats { self.shared.data.seat_removed(seat); self.shared.primary_selection.seat_removed(seat); + self.shared.devices.remove(&seat); } for seat in new_seats { - let dd = Rc::new(WlDataDevice::new( - WlDataDeviceId::NONE, - &self.client, - 1, - &seat, - true, - )); - seat.add_data_device(&dd); - self.shared.data.devices.set(seat.id(), dd); - - let dd = Rc::new(ZwpPrimarySelectionDeviceV1::new( - ZwpPrimarySelectionDeviceV1Id::NONE, - &self.client, - 1, - &seat, - true, - )); - seat.add_primary_selection_device(&dd); - self.shared.primary_selection.devices.set(seat.id(), dd); + let dd = Rc::new(XIpcDevice { + id: self.state.xwayland.ipc_device_ids.next(), + clipboard: Default::default(), + primary_selection: Default::default(), + seat: seat.clone(), + state: self.state.clone(), + client: self.client.clone(), + }); + seat.set_x_data_device(&dd); + self.shared.devices.set(seat.id(), dd.clone()); } self.known_seats = current_seats; } @@ -611,55 +592,121 @@ impl Wm { XWaylandEvent::ActivateRoot => self.activate_window(None, Initiator::Wayland).await, XWaylandEvent::Close(window) => self.close_window(&window).await, XWaylandEvent::SeatChanged => self.seats_changed(), - XWaylandEvent::PrimarySelectionCancelSource(src) => { - self.dd_cancel_source(&self.shared.clone().primary_selection, &src) - } - XWaylandEvent::PrimarySelectionSendSource(src, mime_type, fd) => { - self.dd_send_source(&self.shared.clone().primary_selection, &src, mime_type, fd) - .await; - } - XWaylandEvent::PrimarySelectionSetOffer(offer) => { - self.dd_set_offer(&self.shared.clone().primary_selection, offer) - .await; - } - XWaylandEvent::PrimarySelectionSetSelection(seat, offer) => { - self.dd_set_selection(&self.shared.clone().primary_selection, seat, offer) - .await; - } - XWaylandEvent::PrimarySelectionAddOfferMimeType(offer, mt) => { - self.dd_add_offer_mime_type(&self.shared.clone().primary_selection, offer, mt) - .await; - } - XWaylandEvent::ClipboardCancelSource(src) => { - self.dd_cancel_source(&self.shared.clone().data, &src) - } - XWaylandEvent::ClipboardSendSource(src, mime_type, fd) => { - self.dd_send_source(&self.shared.clone().data, &src, mime_type, fd) - .await; - } - XWaylandEvent::ClipboardSetOffer(offer) => { - self.dd_set_offer(&self.shared.clone().data, offer).await; - } - XWaylandEvent::ClipboardSetSelection(seat, offer) => { - self.dd_set_selection(&self.shared.clone().data, seat, offer) - .await; - } - XWaylandEvent::ClipboardAddOfferMimeType(offer, mt) => { - self.dd_add_offer_mime_type(&self.shared.clone().data, offer, mt) - .await; - } + XWaylandEvent::IpcCancelSource { + location, + seat, + source, + } => match location { + IpcLocation::Clipboard => { + self.dd_cancel_source::(&self.shared.clone().data, seat, source) + } + IpcLocation::PrimarySelection => self.dd_cancel_source::( + &self.shared.clone().primary_selection, + seat, + source, + ), + }, + XWaylandEvent::IpcSendSource { + location, + seat, + source, + mime_type, + fd, + } => match location { + IpcLocation::Clipboard => { + self.dd_send_source::( + &self.shared.clone().data, + seat, + source, + mime_type, + fd, + ) + .await + } + IpcLocation::PrimarySelection => { + self.dd_send_source::( + &self.shared.clone().primary_selection, + seat, + source, + mime_type, + fd, + ) + .await + } + }, + XWaylandEvent::IpcSetOffer { + location, + seat, + offer, + } => match location { + IpcLocation::Clipboard => { + self.dd_set_offer::(&self.shared.clone().data, seat, offer) + .await + } + IpcLocation::PrimarySelection => { + self.dd_set_offer::( + &self.shared.clone().primary_selection, + seat, + offer, + ) + .await + } + }, + XWaylandEvent::IpcSetSelection { + seat, + location, + offer, + } => match location { + IpcLocation::Clipboard => { + self.dd_set_selection::(&self.shared.clone().data, seat, offer) + .await + } + IpcLocation::PrimarySelection => { + self.dd_set_selection::( + &self.shared.clone().primary_selection, + seat, + offer, + ) + .await + } + }, + XWaylandEvent::IpcAddOfferMimeType { + location, + seat, + offer, + mime_type, + } => match location { + IpcLocation::Clipboard => { + self.dd_add_offer_mime_type::( + &self.shared.clone().data, + seat, + offer, + mime_type, + ) + .await + } + IpcLocation::PrimarySelection => { + self.dd_add_offer_mime_type::( + &self.shared.clone().primary_selection, + seat, + offer, + mime_type, + ) + .await + } + }, } } - async fn dd_add_offer_mime_type( + async fn dd_add_offer_mime_type( &mut self, sd: &SelectionData, - offer: Rc, + seat: SeatId, + offer: DataOfferId, mt: String, ) { - let seat = offer.get_seat(); - let enhanced = match sd.offers.get(&seat.id()) { - Some(r) if !rc_eq(&r.offer, &offer) => { + let enhanced = match sd.offers.get(&seat) { + Some(r) if r.offer.offer_id != offer => { return; } None => { @@ -677,20 +724,19 @@ impl Wm { enhanced.mime_types.borrow_mut().push(mt); } - async fn dd_set_offer(&mut self, sd: &SelectionData, offer: Rc) { - let seat = offer.get_seat(); + async fn dd_set_offer( + &mut self, + sd: &SelectionData, + seat: SeatId, + offer: Rc, + ) { let mut mime_types = vec![]; - if let Some(offer) = sd.offers.remove(&seat.id()) { + if let Some(offer) = sd.offers.remove(&seat) { destroy_data_offer::(&offer.offer); mime_types = mem::take(offer.mime_types.borrow_mut().deref_mut()); } - match offer.offer_data().source() { - None => return, - Some(s) if s.source_data().is_xwm => return, - _ => {} - } sd.offers.set( - seat.id(), + seat, Rc::new(EnhancedOffer { offer, mime_types: RefCell::new(mime_types), @@ -699,11 +745,11 @@ impl Wm { ); } - async fn dd_set_selection( + async fn dd_set_selection( &mut self, sd: &SelectionData, seat: SeatId, - offer: Option>, + offer: Option>, ) { let offer = match offer { None => { @@ -794,22 +840,19 @@ impl Wm { } } - async fn dd_send_source( + async fn dd_send_source( &mut self, sd: &SelectionData, - src: &Rc, + seat: SeatId, + src: DataSourceId, mime_type: String, fd: Rc, ) { - let seat = match src.source_data().seat.get() { - Some(s) => s, - _ => return, - }; - let actual_src = match sd.sources.get(&seat.id()) { + let actual_src = match sd.sources.get(&seat) { None => return, Some(src) => src, }; - if !rc_eq(src, &actual_src) { + if actual_src.source_data().id != src { return; } let mime_type = match self.mime_type_to_atom(mime_type).await { @@ -838,13 +881,16 @@ impl Wm { .push(PendingTransfer { mime_type, fd }); } - fn dd_cancel_source(&mut self, sd: &SelectionData, src: &Rc) { - if let Some(seat) = src.source_data().seat.get() { - if let Some(cur) = sd.sources.get(&seat.id()) { - if rc_eq(src, &cur) { - sd.sources.remove(&seat.id()); - destroy_data_source::(&cur); - } + fn dd_cancel_source( + &mut self, + sd: &SelectionData, + seat: SeatId, + source: DataSourceId, + ) { + if let Some(cur) = sd.sources.get(&seat) { + if cur.source_data().id == source { + sd.sources.remove(&seat); + destroy_data_source::(&cur); } } } @@ -1508,7 +1554,7 @@ impl Wm { } } - async fn handle_xfixes_selection_notify_( + async fn handle_xfixes_selection_notify_( &mut self, sd: &SelectionData, event: &XfixesSelectionNotify, @@ -1562,7 +1608,7 @@ impl Wm { } } - async fn handle_selection_request_( + async fn handle_selection_request_( &mut self, sd: &SelectionData, event: &SelectionRequest, @@ -1663,7 +1709,7 @@ impl Wm { } } - async fn handle_selection_notify_( + async fn handle_selection_notify_( &mut self, sd: &SelectionData, event: &SelectionNotify, @@ -1676,12 +1722,17 @@ impl Wm { } if event.target == self.atoms.TARGETS { let targets = self.get_selection_mime_types(sd.win.get()).await?; - for dev in sd.devices.lock().values() { + for dev in self.shared.devices.lock().values() { let seat = T::get_device_seat(dev); if !seat.may_modify_primary_selection(&self.client, None) { continue; } - let source = Rc::new(T::create_xwm_source(&self.client)); + let source = Rc::new(XDataSource { + state: self.state.clone(), + device: dev.clone(), + data: SourceData::new(&self.client), + location: T::LOCATION, + }); if let Err(e) = T::set_seat_selection(&seat, &source, None) { log::error!("Could not set selection: {}", ErrorFmt(e)); return Ok(()); From 99be020c19c7687f66eb3ec6ca10cf57e6e3fabb Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Sun, 31 Mar 2024 01:36:45 +0100 Subject: [PATCH 4/4] wayland: implement wlr-data-control --- src/client/objects.rs | 8 +- src/globals.rs | 2 + src/ifs/ipc.rs | 52 ++- src/ifs/ipc/wl_data_source.rs | 14 +- src/ifs/ipc/x_data_source.rs | 28 +- src/ifs/ipc/zwlr_data_control_device_v1.rs | 306 ++++++++++++++++++ src/ifs/ipc/zwlr_data_control_manager_v1.rs | 145 +++++++++ src/ifs/ipc/zwlr_data_control_offer_v1.rs | 135 ++++++++ src/ifs/ipc/zwlr_data_control_source_v1.rs | 169 ++++++++++ .../ipc/zwp_primary_selection_source_v1.rs | 12 +- src/ifs/wl_seat.rs | 45 ++- src/ifs/wl_seat/event_handling.rs | 12 + src/utils/smallmap.rs | 4 + src/xwayland/xwm.rs | 6 +- wire/zwlr_data_control_device_v1.txt | 30 ++ wire/zwlr_data_control_manager_v1.txt | 14 + wire/zwlr_data_control_offer_v1.txt | 16 + wire/zwlr_data_control_source_v1.txt | 20 ++ 18 files changed, 985 insertions(+), 33 deletions(-) create mode 100644 src/ifs/ipc/zwlr_data_control_device_v1.rs create mode 100644 src/ifs/ipc/zwlr_data_control_manager_v1.rs create mode 100644 src/ifs/ipc/zwlr_data_control_offer_v1.rs create mode 100644 src/ifs/ipc/zwlr_data_control_source_v1.rs create mode 100644 wire/zwlr_data_control_device_v1.txt create mode 100644 wire/zwlr_data_control_manager_v1.txt create mode 100644 wire/zwlr_data_control_offer_v1.txt create mode 100644 wire/zwlr_data_control_source_v1.txt diff --git a/src/client/objects.rs b/src/client/objects.rs index 7e0df3ce..813a38bf 100644 --- a/src/client/objects.rs +++ b/src/client/objects.rs @@ -3,7 +3,7 @@ use { client::{Client, ClientError}, ifs::{ ipc::{ - wl_data_source::WlDataSource, + wl_data_source::WlDataSource, zwlr_data_control_source_v1::ZwlrDataControlSourceV1, zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1, }, jay_output::JayOutput, @@ -32,7 +32,7 @@ use { JayOutputId, JayScreencastId, JayWorkspaceId, WlBufferId, WlDataSourceId, WlOutputId, WlPointerId, WlRegionId, WlRegistryId, WlSeatId, WlSurfaceId, WpLinuxDrmSyncobjTimelineV1Id, XdgPositionerId, XdgSurfaceId, XdgToplevelId, - XdgWmBaseId, ZwpPrimarySelectionSourceV1Id, + XdgWmBaseId, ZwlrDataControlSourceV1Id, ZwpPrimarySelectionSourceV1Id, }, }, std::{cell::RefCell, mem, rc::Rc}, @@ -59,6 +59,7 @@ pub struct Objects { pub seats: CopyHashMap>, pub screencasts: CopyHashMap>, pub timelines: CopyHashMap>, + pub zwlr_data_sources: CopyHashMap>, ids: RefCell>, } @@ -87,6 +88,7 @@ impl Objects { seats: Default::default(), screencasts: Default::default(), timelines: Default::default(), + zwlr_data_sources: Default::default(), ids: RefCell::new(vec![]), } } @@ -118,6 +120,8 @@ impl Objects { self.seats.clear(); self.pointers.clear(); self.screencasts.clear(); + self.timelines.clear(); + self.zwlr_data_sources.clear(); } pub fn id(&self, client_data: &Client) -> Result diff --git a/src/globals.rs b/src/globals.rs index dcc01c2f..aea66c17 100644 --- a/src/globals.rs +++ b/src/globals.rs @@ -8,6 +8,7 @@ use { ext_session_lock_manager_v1::ExtSessionLockManagerV1Global, ipc::{ wl_data_device_manager::WlDataDeviceManagerGlobal, + zwlr_data_control_manager_v1::ZwlrDataControlManagerV1Global, zwp_primary_selection_device_manager_v1::ZwpPrimarySelectionDeviceManagerV1Global, }, jay_compositor::JayCompositorGlobal, @@ -171,6 +172,7 @@ impl Globals { add_singleton!(ZwpIdleInhibitManagerV1Global); add_singleton!(ExtIdleNotifierV1Global); add_singleton!(XdgToplevelDragManagerV1Global); + add_singleton!(ZwlrDataControlManagerV1Global); } pub fn add_backend_singletons(&self, backend: &Rc) { diff --git a/src/ifs/ipc.rs b/src/ifs/ipc.rs index 478d01a7..e7729437 100644 --- a/src/ifs/ipc.rs +++ b/src/ifs/ipc.rs @@ -3,7 +3,9 @@ use { client::{Client, ClientError, ClientId}, fixed::Fixed, ifs::{ - ipc::x_data_device::XIpcDevice, + ipc::{ + x_data_device::XIpcDevice, zwlr_data_control_device_v1::ZwlrDataControlDeviceV1, + }, wl_seat::{WlSeatError, WlSeatGlobal}, }, utils::{ @@ -31,6 +33,10 @@ 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; @@ -60,8 +66,9 @@ pub trait DynDataSource: 'static { 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_offers(&self); + fn cancel_unprivileged_offers(&self); fn send_target(&self, mime_type: Option<&str>) { let _ = mime_type; @@ -98,6 +105,10 @@ pub trait DynDataOffer: 'static { fn cancel(&self); fn get_seat(&self) -> Rc; + fn is_privileged(&self) -> bool { + false + } + fn send_action(&self, action: u32) { let _ = action; log::warn!( @@ -129,6 +140,12 @@ 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 { const LOCATION: IpcLocation; @@ -277,11 +294,17 @@ pub fn attach_seat( Ok(()) } -pub fn cancel_offers(src: &S) { +pub fn cancel_offers(src: &S, cancel_privileged: bool) { let data = src.source_data(); - while let Some((_, offer)) = data.offers.pop() { - offer.cancel(); - } + let mut offers = data.offers.take(); + offers.retain(|o| { + let retain = !cancel_privileged && o.1.is_privileged(); + if !retain { + o.1.cancel(); + } + retain + }); + data.offers.replace(offers); } pub fn cancel_offer(offer: &T::Offer) { @@ -293,7 +316,7 @@ pub fn cancel_offer(offer: &T::Offer) { pub fn detach_seat(src: &S, seat: &Rc) { let data = src.source_data(); data.seat.set(None); - cancel_offers(src); + cancel_offers(src, true); if !data.state.get().contains(SOURCE_STATE_FINISHED) { src.send_cancelled(seat); } @@ -342,12 +365,23 @@ where S: DynDataSource, { let data = src.source_data(); - src.cancel_offers(); + src.cancel_unprivileged_offers(); let shared = data.shared.get(); shared.role.set(data.role.get()); offer_source_to_device::(src, dd, data, shared); } +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); +} + fn offer_source_to_regular_client( src: &Rc, client: &Rc, @@ -360,7 +394,7 @@ fn offer_source_to_regular_client( return; } }; - src.cancel_offers(); + src.cancel_unprivileged_offers(); let shared = data.shared.get(); shared.role.set(data.role.get()); T::for_each_device(&seat, client.id, |dd| { diff --git a/src/ifs/ipc/wl_data_source.rs b/src/ifs/ipc/wl_data_source.rs index d8b790a3..aecd0956 100644 --- a/src/ifs/ipc/wl_data_source.rs +++ b/src/ifs/ipc/wl_data_source.rs @@ -4,10 +4,12 @@ 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_x, + detach_seat, offer_source_to_regular_client, offer_source_to_wlr_device, + 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, @@ -66,12 +68,16 @@ impl DynDataSource for WlDataSource { offer_source_to_x::(&self, dd); } + fn offer_to_wlr_device(self: Rc, dd: &Rc) { + offer_source_to_wlr_device::(&self, dd) + } + fn detach_seat(&self, seat: &Rc) { detach_seat(self, seat); } - fn cancel_offers(&self) { - cancel_offers(self); + fn cancel_unprivileged_offers(&self) { + cancel_offers(self, false); } fn send_target(&self, mime_type: Option<&str>) { @@ -112,7 +118,7 @@ impl WlDataSource { self.data.shared.set(Rc::new(SharedState::default())); self.send_target(None); self.send_action(DND_NONE); - cancel_offers(self); + cancel_offers(self, false); } pub fn update_selected_action(&self) { diff --git a/src/ifs/ipc/x_data_source.rs b/src/ifs/ipc/x_data_source.rs index cf2af861..c071b592 100644 --- a/src/ifs/ipc/x_data_source.rs +++ b/src/ifs/ipc/x_data_source.rs @@ -4,9 +4,14 @@ use { ifs::{ ipc::{ cancel_offers, detach_seat, offer_source_to_regular_client, - wl_data_device::ClipboardIpc, x_data_device::XIpcDevice, - zwp_primary_selection_device_v1::PrimarySelectionIpc, DataSource, DynDataSource, - IpcLocation, SourceData, + 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, }, wl_seat::WlSeatGlobal, }, @@ -61,7 +66,7 @@ impl DynDataSource for XDataSource { } fn offer_to_x(self: Rc, _dd: &Rc) { - self.cancel_offers(); + self.cancel_unprivileged_offers(); self.state.xwayland.queue.push(IpcSetSelection { location: self.location, seat: self.device.seat.id(), @@ -69,11 +74,22 @@ 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); } - fn cancel_offers(&self) { - cancel_offers(self) + fn cancel_unprivileged_offers(&self) { + cancel_offers(self, false) } } diff --git a/src/ifs/ipc/zwlr_data_control_device_v1.rs b/src/ifs/ipc/zwlr_data_control_device_v1.rs new file mode 100644 index 00000000..9bb6b29e --- /dev/null +++ b/src/ifs/ipc/zwlr_data_control_device_v1.rs @@ -0,0 +1,306 @@ +use { + crate::{ + client::{Client, ClientError}, + ifs::{ + ipc::{ + 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, + utils::buffd::{MsgParser, MsgParserError}, + wire::{ + zwlr_data_control_device_v1::*, ZwlrDataControlDeviceV1Id, ZwlrDataControlOfferV1Id, + ZwlrDataControlSourceV1Id, + }, + }, + std::rc::Rc, + thiserror::Error, +}; + +pub const PRIMARY_SELECTION_SINCE: u32 = 2; + +pub struct ZwlrDataControlDeviceV1 { + pub id: ZwlrDataControlDeviceV1Id, + pub client: Rc, + pub version: u32, + 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: u32, + 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)) + } + } + + fn set_selection(&self, parser: MsgParser<'_, '_>) -> Result<(), ZwlrDataControlDeviceV1Error> { + let req: SetSelection = self.client.parse(self, parser)?; + let src = self.use_source(req.source, IpcLocation::Clipboard)?; + self.seat.set_selection(src)?; + Ok(()) + } + + fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), ZwlrDataControlDeviceV1Error> { + let _req: Destroy = self.client.parse(self, parser)?; + 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, + parser: MsgParser<'_, '_>, + ) -> Result<(), ZwlrDataControlDeviceV1Error> { + let req: SetPrimarySelection = self.client.parse(self, parser)?; + 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: u32; + 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: u32 = 1; + 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: u32 = 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 { + const LOCATION: IpcLocation = T::LOCATION; + 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; + + SET_SELECTION => set_selection, + DESTROY => destroy, + SET_PRIMARY_SELECTION => set_primary_selection if self.version >= 2, +} + +impl Object for ZwlrDataControlDeviceV1 { + fn break_loops(&self) { + self.seat.remove_wlr_device(self); + } +} + +simple_add_obj!(ZwlrDataControlDeviceV1); + +#[derive(Debug, Error)] +pub enum ZwlrDataControlDeviceV1Error { + #[error("Parsing failed")] + MsgParserError(#[source] Box), + #[error(transparent)] + ClientError(Box), + #[error(transparent)] + WlSeatError(Box), + #[error("The source has already been used")] + AlreadyUsed, +} +efrom!(ZwlrDataControlDeviceV1Error, MsgParserError); +efrom!(ZwlrDataControlDeviceV1Error, ClientError); +efrom!(ZwlrDataControlDeviceV1Error, WlSeatError); diff --git a/src/ifs/ipc/zwlr_data_control_manager_v1.rs b/src/ifs/ipc/zwlr_data_control_manager_v1.rs new file mode 100644 index 00000000..df2ecdd4 --- /dev/null +++ b/src/ifs/ipc/zwlr_data_control_manager_v1.rs @@ -0,0 +1,145 @@ +use { + crate::{ + client::{Client, ClientError}, + globals::{Global, GlobalName}, + ifs::ipc::{ + zwlr_data_control_device_v1::{ZwlrDataControlDeviceV1, PRIMARY_SELECTION_SINCE}, + zwlr_data_control_source_v1::ZwlrDataControlSourceV1, + }, + leaks::Tracker, + object::Object, + utils::buffd::{MsgParser, MsgParserError}, + wire::{zwlr_data_control_manager_v1::*, ZwlrDataControlManagerV1Id}, + }, + std::rc::Rc, + thiserror::Error, +}; + +pub struct ZwlrDataControlManagerV1Global { + name: GlobalName, +} + +pub struct ZwlrDataControlManagerV1 { + pub id: ZwlrDataControlManagerV1Id, + pub client: Rc, + pub version: u32, + tracker: Tracker, +} + +impl ZwlrDataControlManagerV1Global { + pub fn new(name: GlobalName) -> Self { + Self { name } + } + + fn bind_( + self: Rc, + id: ZwlrDataControlManagerV1Id, + client: &Rc, + version: u32, + ) -> Result<(), ZwlrDataControlManagerV1Error> { + let obj = Rc::new(ZwlrDataControlManagerV1 { + id, + client: client.clone(), + version, + tracker: Default::default(), + }); + track!(client, obj); + client.add_client_obj(&obj)?; + Ok(()) + } +} + +impl ZwlrDataControlManagerV1 { + fn create_data_source( + &self, + parser: MsgParser<'_, '_>, + ) -> Result<(), ZwlrDataControlManagerV1Error> { + let req: CreateDataSource = self.client.parse(self, parser)?; + let res = Rc::new(ZwlrDataControlSourceV1::new( + req.id, + &self.client, + self.version, + )); + track!(self.client, res); + self.client.add_client_obj(&res)?; + Ok(()) + } + + fn get_data_device( + self: &Rc, + parser: MsgParser<'_, '_>, + ) -> Result<(), ZwlrDataControlManagerV1Error> { + let req: GetDataDevice = self.client.parse(&**self, parser)?; + let seat = self.client.lookup(req.seat)?; + let dev = Rc::new(ZwlrDataControlDeviceV1::new( + req.id, + &self.client, + self.version, + &seat.global, + )); + track!(self.client, dev); + 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), + _ => 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), + } + } + Ok(()) + } + + fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), ZwlrDataControlManagerV1Error> { + let _req: Destroy = self.client.parse(self, parser)?; + self.client.remove_obj(self)?; + Ok(()) + } +} + +global_base!( + ZwlrDataControlManagerV1Global, + ZwlrDataControlManagerV1, + ZwlrDataControlManagerV1Error +); + +impl Global for ZwlrDataControlManagerV1Global { + fn singleton(&self) -> bool { + true + } + + fn version(&self) -> u32 { + 2 + } + + fn secure(&self) -> bool { + true + } +} + +simple_add_global!(ZwlrDataControlManagerV1Global); + +object_base! { + self = ZwlrDataControlManagerV1; + + CREATE_DATA_SOURCE => create_data_source, + GET_DATA_DEVICE => get_data_device, + DESTROY => destroy, +} + +impl Object for ZwlrDataControlManagerV1 {} + +simple_add_obj!(ZwlrDataControlManagerV1); + +#[derive(Debug, Error)] +pub enum ZwlrDataControlManagerV1Error { + #[error(transparent)] + ClientError(Box), + #[error("Parsing failed")] + MsgParserError(#[source] Box), +} +efrom!(ZwlrDataControlManagerV1Error, ClientError); +efrom!(ZwlrDataControlManagerV1Error, MsgParserError); diff --git a/src/ifs/ipc/zwlr_data_control_offer_v1.rs b/src/ifs/ipc/zwlr_data_control_offer_v1.rs new file mode 100644 index 00000000..a9eb427c --- /dev/null +++ b/src/ifs/ipc/zwlr_data_control_offer_v1.rs @@ -0,0 +1,135 @@ +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, + utils::buffd::{MsgParser, MsgParserError}, + 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 destroy(&self) { + match self.location { + IpcLocation::Clipboard => destroy_data_offer::(self), + IpcLocation::PrimarySelection => destroy_data_offer::(self), + } + } + + 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, + }) + } + + fn receive(&self, parser: MsgParser<'_, '_>) -> Result<(), ZwlrDataControlOfferV1Error> { + let req: Receive = self.client.parse(self, parser)?; + 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, parser: MsgParser<'_, '_>) -> Result<(), ZwlrDataControlOfferV1Error> { + let _req: Destroy = self.client.parse(self, parser)?; + 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; + + RECEIVE => receive, + DESTROY => destroy, +} + +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), + #[error("Parsing failed")] + MsgParserError(#[source] Box), +} +efrom!(ZwlrDataControlOfferV1Error, ClientError); +efrom!(ZwlrDataControlOfferV1Error, MsgParserError); diff --git a/src/ifs/ipc/zwlr_data_control_source_v1.rs b/src/ifs/ipc/zwlr_data_control_source_v1.rs new file mode 100644 index 00000000..bf5fa10a --- /dev/null +++ b/src/ifs/ipc/zwlr_data_control_source_v1.rs @@ -0,0 +1,169 @@ +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, + utils::buffd::{MsgParser, MsgParserError}, + 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: u32, + 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: u32) -> 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 }) + } + + fn offer(&self, parser: MsgParser<'_, '_>) -> Result<(), ZwlrDataControlSourceV1Error> { + let req: Offer = self.data.client.parse(self, parser)?; + if self.used.get() { + return Err(ZwlrDataControlSourceV1Error::AlreadyUsed); + } + add_data_source_mime_type::(self, req.mime_type); + Ok(()) + } + + fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), ZwlrDataControlSourceV1Error> { + let _req: Destroy = self.data.client.parse(self, parser)?; + 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; + + OFFER => offer, + DESTROY => destroy, +} + +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("Parsing failed")] + MsgParserError(#[source] Box), + #[error(transparent)] + ClientError(Box), + #[error("The source has already been used")] + AlreadyUsed, +} +efrom!(ZwlrDataControlSourceV1Error, ClientError); +efrom!(ZwlrDataControlSourceV1Error, MsgParserError); diff --git a/src/ifs/ipc/zwp_primary_selection_source_v1.rs b/src/ifs/ipc/zwp_primary_selection_source_v1.rs index d58861cd..d6d50683 100644 --- a/src/ifs/ipc/zwp_primary_selection_source_v1.rs +++ b/src/ifs/ipc/zwp_primary_selection_source_v1.rs @@ -4,8 +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_x, + detach_seat, offer_source_to_regular_client, offer_source_to_wlr_device, + 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, }, @@ -50,12 +52,16 @@ impl DynDataSource for ZwpPrimarySelectionSourceV1 { offer_source_to_x::(&self, dd); } + fn offer_to_wlr_device(self: Rc, dd: &Rc) { + offer_source_to_wlr_device::(&self, dd) + } + fn detach_seat(&self, seat: &Rc) { detach_seat(self, seat); } - fn cancel_offers(&self) { - cancel_offers(self); + fn cancel_unprivileged_offers(&self) { + cancel_offers(self, false); } } diff --git a/src/ifs/wl_seat.rs b/src/ifs/wl_seat.rs index b305433d..e4bdabde 100644 --- a/src/ifs/wl_seat.rs +++ b/src/ifs/wl_seat.rs @@ -23,6 +23,9 @@ use { 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, }, @@ -64,7 +67,8 @@ use { }, wire::{ wl_seat::*, ExtIdleNotificationV1Id, WlDataDeviceId, WlKeyboardId, WlPointerId, - WlSeatId, ZwpPrimarySelectionDeviceV1Id, ZwpRelativePointerV1Id, + WlSeatId, ZwlrDataControlDeviceV1Id, ZwpPrimarySelectionDeviceV1Id, + ZwpRelativePointerV1Id, }, xkbcommon::{XkbKeymap, XkbState}, }, @@ -141,6 +145,8 @@ pub struct WlSeatGlobal { AHashMap>, >, >, + wlr_data_devices: + CopyHashMap<(ClientId, ZwlrDataControlDeviceV1Id), Rc>, repeat_rate: Cell<(i32, i32)>, kb_map: CloneCell>, kb_state: RefCell, @@ -219,6 +225,7 @@ impl WlSeatGlobal { constraint: Default::default(), idle_notifications: Default::default(), last_input_usec: Cell::new(now_usec()), + wlr_data_devices: Default::default(), }); state.add_cursor_size(*DEFAULT_CURSOR_SIZE); let seat = slf.clone(); @@ -394,6 +401,15 @@ impl WlSeatGlobal { } } + pub fn add_wlr_device(&self, device: &Rc) { + self.wlr_data_devices + .set((device.client.id, device.id), device.clone()); + } + + pub fn remove_wlr_device(&self, device: &ZwlrDataControlDeviceV1) { + self.wlr_data_devices.remove(&(device.client.id, device.id)); + } + pub fn get_output(&self) -> Rc { self.output.get() } @@ -727,7 +743,7 @@ impl WlSeatGlobal { } } - fn set_selection_( + fn set_selection_( self: &Rc, field: &CloneCell>>, src: Option>, @@ -735,6 +751,7 @@ impl WlSeatGlobal { where T: ipc::IterableIpcVtable, X: ipc::IpcVtable, + W: ipc::WlrIpcVtable, S: DynDataSource, { if let (Some(new), Some(old)) = (&src, &field.get()) { @@ -750,9 +767,13 @@ impl WlSeatGlobal { old.detach_seat(self); } if let Some(client) = self.keyboard_node.get().node_client() { - self.offer_selection_to_client::(src.map(|v| v as Rc<_>), &client); + 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), + }); Ok(()) } @@ -765,7 +786,7 @@ impl WlSeatGlobal { X: ipc::IpcVtable, { if let Some(src) = &selection { - src.cancel_offers(); + src.cancel_unprivileged_offers(); } if client.is_xwayland { self.for_each_x_data_device(|dd| match &selection { @@ -824,7 +845,14 @@ impl WlSeatGlobal { self: &Rc, selection: Option>, ) -> Result<(), WlSeatError> { - self.set_selection_::(&self.selection, selection) + self.set_selection_::( + &self.selection, + selection, + ) + } + + pub fn get_selection(&self) -> Option> { + self.selection.get() } pub fn may_modify_selection(&self, client: &Rc, serial: u32) -> bool { @@ -865,12 +893,16 @@ impl WlSeatGlobal { self: &Rc, selection: Option>, ) -> Result<(), WlSeatError> { - self.set_selection_::( + self.set_selection_::( &self.primary_selection, selection, ) } + pub fn get_primary_selection(&self) -> Option> { + self.primary_selection.get() + } + pub fn reload_known_cursor(&self) { if let Some(kc) = self.desired_known_cursor.get() { self.set_known_cursor(kc); @@ -978,6 +1010,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.cursor.set(None); 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 67263026..14b27d9e 100644 --- a/src/ifs/wl_seat/event_handling.rs +++ b/src/ifs/wl_seat/event_handling.rs @@ -7,6 +7,7 @@ use { ipc::{ wl_data_device::{ClipboardIpc, WlDataDevice}, x_data_device::{XClipboardIpc, XPrimarySelectionIpc}, + zwlr_data_control_device_v1::ZwlrDataControlDeviceV1, zwp_primary_selection_device_v1::{ PrimarySelectionIpc, ZwpPrimarySelectionDeviceV1, }, @@ -508,6 +509,17 @@ impl WlSeatGlobal { } } + pub fn for_each_wlr_data_device(&self, ver: u32, 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/utils/smallmap.rs b/src/utils/smallmap.rs index 1eb35979..624c653a 100644 --- a/src/utils/smallmap.rs +++ b/src/utils/smallmap.rs @@ -76,6 +76,10 @@ impl SmallMap { unsafe { self.m.get().deref_mut().take() } } + pub fn replace(&self, other: SmallVec<[(K, V); N]>) -> SmallVec<[(K, V); N]> { + unsafe { mem::replace(&mut self.m.get().deref_mut().m, other) } + } + pub fn pop(&self) -> Option<(K, V)> { unsafe { self.m.get().deref_mut().pop() } } diff --git a/src/xwayland/xwm.rs b/src/xwayland/xwm.rs index e28f7724..2a25e45e 100644 --- a/src/xwayland/xwm.rs +++ b/src/xwayland/xwm.rs @@ -1733,13 +1733,13 @@ impl Wm { data: SourceData::new(&self.client), location: T::LOCATION, }); + for target in &targets { + add_data_source_mime_type::(&source, target); + } if let Err(e) = T::set_seat_selection(&seat, &source, None) { log::error!("Could not set selection: {}", ErrorFmt(e)); return Ok(()); } - for target in &targets { - add_data_source_mime_type::(&source, target); - } sd.sources.set(seat.id(), source); } } else { diff --git a/wire/zwlr_data_control_device_v1.txt b/wire/zwlr_data_control_device_v1.txt new file mode 100644 index 00000000..7a458bc8 --- /dev/null +++ b/wire/zwlr_data_control_device_v1.txt @@ -0,0 +1,30 @@ +# requests + +msg set_selection = 0 { + source: id(zwlr_data_control_source_v1), +} + +msg destroy = 1 { + +} + +msg set_primary_selection = 2 { + source: id(zwlr_data_control_source_v1), +} + +# events + +msg data_offer = 0 { + id: id(zwlr_data_control_offer_v1), +} + +msg selection = 1 { + id: id(zwlr_data_control_offer_v1), +} + +msg finished = 2 { +} + +msg primary_selection = 3 { + id: id(zwlr_data_control_offer_v1), +} diff --git a/wire/zwlr_data_control_manager_v1.txt b/wire/zwlr_data_control_manager_v1.txt new file mode 100644 index 00000000..5742e10a --- /dev/null +++ b/wire/zwlr_data_control_manager_v1.txt @@ -0,0 +1,14 @@ +# requests + +msg create_data_source = 0 { + id: id(zwlr_data_control_source_v1), +} + +msg get_data_device = 1 { + id: id(zwlr_data_control_device_v1), + seat: id(wl_seat), +} + +msg destroy = 2 { + +} diff --git a/wire/zwlr_data_control_offer_v1.txt b/wire/zwlr_data_control_offer_v1.txt new file mode 100644 index 00000000..5a7bf7ae --- /dev/null +++ b/wire/zwlr_data_control_offer_v1.txt @@ -0,0 +1,16 @@ +# requests + +msg receive = 0 { + mime_type: str, + fd: fd, +} + +msg destroy = 1 { + +} + +# events + +msg offer = 0 { + mime_type: str, +} diff --git a/wire/zwlr_data_control_source_v1.txt b/wire/zwlr_data_control_source_v1.txt new file mode 100644 index 00000000..b274415f --- /dev/null +++ b/wire/zwlr_data_control_source_v1.txt @@ -0,0 +1,20 @@ +# requests + +msg offer = 0 { + mime_type: str, +} + +msg destroy = 1 { + +} + +# events + +msg send = 0 { + mime_type: str, + fd: fd, +} + +msg cancelled = 1 { + +}