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/compositor.rs b/src/compositor.rs index 4f30dc91..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(), @@ -200,6 +201,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/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 36993c9c..e7729437 100644 --- a/src/ifs/ipc.rs +++ b/src/ifs/ipc.rs @@ -1,14 +1,23 @@ use { crate::{ - client::{Client, ClientError, ClientId, WaylandObject}, - ifs::wl_seat::{WlSeatError, WlSeatGlobal}, + client::{Client, ClientError, ClientId}, + fixed::Fixed, + ifs::{ + ipc::{ + x_data_device::XIpcDevice, zwlr_data_control_device_v1::ZwlrDataControlDeviceV1, + }, + 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, @@ -21,71 +30,166 @@ 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 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; 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, Dnd, } -pub trait IpcVtable: Sized { - type Device; - type Source; - type Offer: WaylandObject; +pub trait DataSource: DynDataSource { + fn send_cancelled(&self, seat: &Rc); +} - fn get_device_data(dd: &Self::Device) -> &DeviceData; +pub trait DynDataSource: 'static { + fn source_data(&self) -> &SourceData; + fn send_send(&self, mime_type: &str, fd: Rc); + fn offer_to_regular_client(self: Rc, client: &Rc); + fn offer_to_x(self: Rc, dd: &Rc); + fn offer_to_wlr_device(self: Rc, dd: &Rc); + fn detach_seat(&self, seat: &Rc); + fn cancel_unprivileged_offers(&self); + + 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, mime_type: &str); + fn destroy(&self); + fn cancel(&self); + fn get_seat(&self) -> Rc; + + fn is_privileged(&self) -> bool { + false + } + + 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 IterableIpcVtable: IpcVtable { + fn for_each_device(seat: &WlSeatGlobal, client: ClientId, f: C) + where + 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; + + type Device; + type Source: DataSource; + type Offer: DataOffer; + + 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) -> u64; 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; + fn device_client(dd: &Rc) -> &Rc; } -pub struct DeviceData { - selection: CloneCell>>, - dnd: CloneCell>>, - pub is_xwm: bool, +pub struct DeviceData { + selection: CloneCell>>, + dnd: CloneCell>>, } -pub struct OfferData { - device: CloneCell>>, - source: CloneCell>>, - shared: Rc, - pub is_xwm: bool, -} - -impl OfferData { - pub fn source(&self) -> Option> { - self.source.get() +impl Default for DeviceData { + fn default() -> Self { + Self { + selection: Default::default(), + dnd: Default::default(), + } } } +pub struct OfferData { + device: CloneCell>>, + source: CloneCell>>, + shared: Rc, +} + #[derive(Debug, Error)] pub enum IpcError { #[error("The data source is already attached")] @@ -106,17 +210,16 @@ 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>>, - offers: SmallMap, 1>, - offer_client: Cell, + pub id: DataSourceId, + offers: SmallMap, 1>, mime_types: RefCell>, pub client: Rc, state: NumCell, actions: Cell>, role: Cell, shared: CloneCell>, - pub is_xwm: bool, } struct SharedState { @@ -139,19 +242,18 @@ impl Default for SharedState { } } -impl SourceData { - fn new(client: &Rc, is_xwm: bool) -> Self { +impl SourceData { + pub fn new(client: &Rc) -> 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), actions: Cell::new(None), role: Cell::new(Role::Selection), shared: Default::default(), - is_xwm, } } @@ -166,12 +268,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); @@ -192,27 +294,99 @@ pub fn attach_seat( Ok(()) } -pub fn cancel_offers(src: &T::Source) { - let data = T::get_source_data(src); - while let Some((_, offer)) = data.offers.pop() { - let data = T::get_offer_data(&offer); - data.source.take(); - destroy_data_offer::(&offer); - } +pub fn cancel_offers(src: &S, cancel_privileged: bool) { + let data = src.source_data(); + 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 detach_seat(src: &Rc, seat: &Rc) { - let data = T::get_source_data(src); +pub fn cancel_offer(offer: &T::Offer) { + let data = offer.offer_data(); + data.source.take(); + destroy_data_offer::(&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) { - 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); +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_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, +) { + let data = src.source_data(); let seat = match data.seat.get() { Some(a) => a, _ => { @@ -220,51 +394,19 @@ pub fn offer_source_to(src: &Rc, client: &Rc) { return; } }; - cancel_offers::(src); - data.offer_client.set(client.id); + src.cancel_unprivileged_offers(); 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(T::get_offer_id(&offer), 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); - } - 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()); }); } 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(); } @@ -272,14 +414,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() { @@ -293,8 +435,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) @@ -310,21 +452,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); } @@ -337,9 +485,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 820209b2..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, SourceData, + wl_data_source::WlDataSource, DeviceData, IpcLocation, IpcVtable, + IterableIpcVtable, OfferData, Role, }, wl_seat::{WlSeatError, WlSeatGlobal}, wl_surface::{SurfaceRole, WlSurfaceError}, @@ -13,12 +14,10 @@ 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, - uapi::OwnedFd, }; #[allow(dead_code)] @@ -29,7 +28,7 @@ pub struct WlDataDevice { pub client: Rc, pub version: u32, pub seat: Rc, - pub data: DeviceData, + pub data: DeviceData, pub tracker: Tracker, } @@ -39,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( @@ -103,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> { @@ -170,7 +137,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(()) } @@ -185,12 +153,23 @@ impl WlDataDevice { pub struct ClipboardIpc; +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; - fn get_device_data(dd: &Self::Device) -> &DeviceData { + fn get_device_data(dd: &Self::Device) -> &DeviceData { &dd.data } @@ -198,47 +177,28 @@ 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 - } - - fn for_each_device(seat: &WlSeatGlobal, client: ClientId, f: C) - where - C: FnMut(&Rc), - { - seat.for_each_data_device(0, client, f); + seat.set_wl_data_source_selection(Some(source.clone()), serial) } fn create_offer( - client: &Rc, device: &Rc, - offer_data: OfferData, + offer_data: OfferData, ) -> Result, ClientError> { let rc = Rc::new(WlDataOffer { - id: client.new_id()?, - u64_id: client.state.data_offer_ids.fetch_add(1), - 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) } @@ -246,22 +206,10 @@ 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) -> u64 { - offer.u64_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(), @@ -269,20 +217,8 @@ impl IpcVtable for ClipboardIpc { } } - 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() - } - - fn source_eq(left: &Self::Source, right: &Self::Source) -> bool { - left as *const _ == right as *const _ + fn device_client(dd: &Rc) -> &Rc { + &dd.client } } 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 83d16834..17078882 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, - 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,8 +18,7 @@ use { bitflags::BitflagsExt, buffd::{MsgParser, MsgParserError}, }, - wire::{wl_data_offer::*, WlDataOfferId}, - xwayland::XWaylandEvent, + wire::{wl_data_offer::*, WlDataOfferId, WlSurfaceId}, }, std::rc::Rc, thiserror::Error, @@ -32,54 +35,83 @@ 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, + 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, 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 { - 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.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> { @@ -134,7 +166,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..aecd0956 100644 --- a/src/ifs/ipc/wl_data_source.rs +++ b/src/ifs/ipc/wl_data_source.rs @@ -4,11 +4,15 @@ use { ifs::{ ipc::{ add_data_source_mime_type, break_source_loops, cancel_offers, destroy_data_source, + detach_seat, offer_source_to_regular_client, offer_source_to_wlr_device, + offer_source_to_x, wl_data_device::ClipboardIpc, 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, + 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, }, wl_seat::WlSeatGlobal, xdg_toplevel_drag_v1::XdgToplevelDragV1, @@ -22,7 +26,6 @@ use { clonecell::CloneCell, }, wire::{wl_data_source::*, WlDataSourceId}, - xwayland::XWaylandEvent, }, std::rc::Rc, thiserror::Error, @@ -36,18 +39,66 @@ 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, seat: &Rc) { + WlDataSource::send_cancelled(self, seat); + } +} + +impl DynDataSource for WlDataSource { + fn source_data(&self) -> &SourceData { + &self.data + } + + fn send_send(&self, mime_type: &str, fd: Rc) { + WlDataSource::send_send(self, mime_type, fd); + } + + fn offer_to_regular_client(self: Rc, client: &Rc) { + offer_source_to_regular_client::(&self, client); + } + + fn offer_to_x(self: Rc, dd: &Rc) { + offer_source_to_x::(&self, dd); + } + + fn offer_to_wlr_device(self: Rc, dd: &Rc) { + offer_source_to_wlr_device::(&self, dd) + } + + fn detach_seat(&self, seat: &Rc) { + detach_seat(self, seat); + } + + fn cancel_unprivileged_offers(&self) { + cancel_offers(self, false); + } + + 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 { + 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(), } @@ -67,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) { @@ -97,9 +148,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); } } @@ -118,74 +169,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..c071b592 --- /dev/null +++ b/src/ifs/ipc/x_data_source.rs @@ -0,0 +1,95 @@ +use { + crate::{ + client::Client, + ifs::{ + ipc::{ + cancel_offers, detach_seat, offer_source_to_regular_client, + offer_source_to_wlr_device, + wl_data_device::ClipboardIpc, + x_data_device::XIpcDevice, + zwlr_data_control_device_v1::{ + WlrClipboardIpc, WlrPrimarySelectionIpc, ZwlrDataControlDeviceV1, + }, + zwp_primary_selection_device_v1::PrimarySelectionIpc, + DataSource, DynDataSource, IpcLocation, SourceData, + }, + 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_unprivileged_offers(); + self.state.xwayland.queue.push(IpcSetSelection { + location: self.location, + seat: self.device.seat.id(), + offer: None, + }); + } + + 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_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_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 9c470925..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, SourceData, + IpcLocation, IpcVtable, IterableIpcVtable, OfferData, Role, }, wl_seat::{WlSeatError, WlSeatGlobal}, }, @@ -15,13 +15,11 @@ use { utils::buffd::{MsgParser, MsgParserError}, wire::{ zwp_primary_selection_device_v1::*, ZwpPrimarySelectionDeviceV1Id, - ZwpPrimarySelectionOfferV1Id, ZwpPrimarySelectionSourceV1Id, + ZwpPrimarySelectionOfferV1Id, }, - xwayland::XWaylandEvent, }, std::rc::Rc, thiserror::Error, - uapi::OwnedFd, }; pub struct ZwpPrimarySelectionDeviceV1 { @@ -29,7 +27,7 @@ pub struct ZwpPrimarySelectionDeviceV1 { pub client: Rc, pub version: u32, pub seat: Rc, - data: DeviceData, + data: DeviceData, pub tracker: Tracker, } @@ -39,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( @@ -112,7 +86,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 +101,23 @@ impl ZwpPrimarySelectionDeviceV1 { pub struct PrimarySelectionIpc; +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; - fn get_device_data(dd: &Self::Device) -> &DeviceData { + fn get_device_data(dd: &Self::Device) -> &DeviceData { &dd.data } @@ -140,52 +125,28 @@ 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 - } - - fn for_each_device(seat: &WlSeatGlobal, client: ClientId, f: C) - where - C: FnMut(&Rc), - { - seat.for_each_primary_selection_device(0, client, f) + seat.set_zwp_primary_selection(Some(source.clone()), serial) } fn create_offer( - client: &Rc, device: &Rc, - offer_data: OfferData, + offer_data: OfferData, ) -> Result, ClientError> { - let id = if device.data.is_xwm { - ZwpPrimarySelectionOfferV1Id::NONE - } else { - client.new_id()? - }; let rc = Rc::new(ZwpPrimarySelectionOfferV1 { - id, - u64_id: client.state.data_offer_ids.fetch_add(1), + 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) } @@ -193,40 +154,16 @@ 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) -> u64 { - offer.u64_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() - } - - fn source_eq(left: &Self::Source, right: &Self::Source) -> bool { - left as *const _ == right as *const _ + fn device_client(dd: &Rc) -> &Rc { + &dd.client } } diff --git a/src/ifs/ipc/zwp_primary_selection_offer_v1.rs b/src/ifs/ipc/zwp_primary_selection_offer_v1.rs index c70d6d82..d6a7caa3 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, 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, }, @@ -12,7 +15,6 @@ use { object::Object, utils::buffd::{MsgParser, MsgParserError}, wire::{zwp_primary_selection_offer_v1::*, ZwpPrimarySelectionOfferV1Id}, - xwayland::XWaylandEvent, }, std::rc::Rc, thiserror::Error, @@ -20,32 +22,53 @@ use { pub struct ZwpPrimarySelectionOfferV1 { pub id: ZwpPrimarySelectionOfferV1Id, - pub u64_id: u64, + 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, 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 { - 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 a563f9fe..d6d50683 100644 --- a/src/ifs/ipc/zwp_primary_selection_source_v1.rs +++ b/src/ifs/ipc/zwp_primary_selection_source_v1.rs @@ -1,15 +1,22 @@ 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_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, + }, + wl_seat::WlSeatGlobal, }, leaks::Tracker, object::Object, utils::buffd::{MsgParser, MsgParserError}, wire::{zwp_primary_selection_source_v1::*, ZwpPrimarySelectionSourceV1Id}, - xwayland::XWaylandEvent, }, std::rc::Rc, thiserror::Error, @@ -18,51 +25,65 @@ use { pub struct ZwpPrimarySelectionSourceV1 { pub id: ZwpPrimarySelectionSourceV1Id, - pub data: SourceData, + pub data: SourceData, pub tracker: Tracker, } +impl DataSource for ZwpPrimarySelectionSourceV1 { + fn send_cancelled(&self, _seat: &Rc) { + ZwpPrimarySelectionSourceV1::send_cancelled(self); + } +} + +impl DynDataSource for ZwpPrimarySelectionSourceV1 { + fn source_data(&self) -> &SourceData { + &self.data + } + + fn send_send(&self, mime_type: &str, fd: Rc) { + ZwpPrimarySelectionSourceV1::send_send(self, mime_type, fd) + } + + fn offer_to_regular_client(self: Rc, client: &Rc) { + offer_source_to_regular_client::(&self, client); + } + + fn offer_to_x(self: Rc, dd: &Rc) { + offer_source_to_x::(&self, dd); + } + + fn offer_to_wlr_device(self: Rc, dd: &Rc) { + offer_source_to_wlr_device::(&self, dd) + } + + fn detach_seat(&self, seat: &Rc) { + detach_seat(self, seat); + } + + fn cancel_unprivileged_offers(&self) { + cancel_offers(self, false); + } +} + 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 e9c225ca..e4bdabde 100644 --- a/src/ifs/wl_seat.rs +++ b/src/ifs/wl_seat.rs @@ -22,11 +22,15 @@ use { self, wl_data_device::{ClipboardIpc, WlDataDevice}, wl_data_source::WlDataSource, + x_data_device::{XClipboardIpc, XIpcDevice, XIpcDeviceId, XPrimarySelectionIpc}, + zwlr_data_control_device_v1::{ + WlrClipboardIpc, WlrPrimarySelectionIpc, ZwlrDataControlDeviceV1, + }, zwp_primary_selection_device_v1::{ PrimarySelectionIpc, ZwpPrimarySelectionDeviceV1, }, zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1, - IpcError, + DynDataSource, IpcError, }, wl_seat::{ kb_owner::KbOwnerHolder, @@ -58,11 +62,13 @@ use { linkedlist::LinkedNode, numcell::NumCell, rc_eq::rc_eq, + smallmap::SmallMap, transform_ext::TransformExt, }, wire::{ wl_seat::*, ExtIdleNotificationV1Id, WlDataDeviceId, WlKeyboardId, WlPointerId, - WlSeatId, ZwpPrimarySelectionDeviceV1Id, ZwpRelativePointerV1Id, + WlSeatId, ZwlrDataControlDeviceV1Id, ZwpPrimarySelectionDeviceV1Id, + ZwpRelativePointerV1Id, }, xkbcommon::{XkbKeymap, XkbState}, }, @@ -108,7 +114,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 +137,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< @@ -138,14 +145,16 @@ pub struct WlSeatGlobal { AHashMap>, >, >, + wlr_data_devices: + CopyHashMap<(ClientId, ZwlrDataControlDeviceV1Id), Rc>, repeat_rate: Cell<(i32, i32)>, kb_map: CloneCell>, 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, @@ -190,6 +199,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)), @@ -215,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(); @@ -349,6 +360,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) { @@ -376,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() } @@ -709,32 +743,64 @@ impl WlSeatGlobal { } } - fn set_selection_( + fn set_selection_( self: &Rc, - field: &CloneCell>>, - src: Option>, - ) -> Result<(), WlSeatError> { + field: &CloneCell>>, + src: Option>, + ) -> Result<(), WlSeatError> + where + T: ipc::IterableIpcVtable, + X: ipc::IpcVtable, + W: ipc::WlrIpcVtable, + S: DynDataSource, + { if let (Some(new), Some(old)) = (&src, &field.get()) { - if T::source_eq(old, new) { + 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), + 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(()) + } + + fn offer_selection_to_client( + &self, + selection: Option>, + client: &Rc, + ) where + T: ipc::IterableIpcVtable, + X: ipc::IpcVtable, + { + if let Some(src) = &selection { + src.cancel_unprivileged_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( @@ -756,10 +822,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 +838,21 @@ 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 get_selection(&self) -> Option> { + self.selection.get() } pub fn may_modify_selection(&self, client: &Rc, serial: u32) -> bool { @@ -795,10 +875,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 +886,21 @@ 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 get_primary_selection(&self) -> Option> { + self.primary_selection.get() } pub fn reload_known_cursor(&self) { @@ -916,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 8ba365c4..14b27d9e 100644 --- a/src/ifs/wl_seat/event_handling.rs +++ b/src/ifs/wl_seat/event_handling.rs @@ -1,15 +1,17 @@ use { crate::{ backend::{ConnectorId, InputEvent, KeyState, AXIS_120}, - client::{Client, ClientId}, + client::ClientId, fixed::Fixed, ifs::{ - ipc, ipc::{ wl_data_device::{ClipboardIpc, WlDataDevice}, + x_data_device::{XClipboardIpc, XPrimarySelectionIpc}, + zwlr_data_control_device_v1::ZwlrDataControlDeviceV1, zwp_primary_selection_device_v1::{ PrimarySelectionIpc, ZwpPrimarySelectionDeviceV1, }, + DynDataSource, }, wl_seat::{ wl_keyboard::{self, WlKeyboard}, @@ -27,7 +29,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 +431,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), @@ -520,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()); } @@ -757,8 +757,14 @@ 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); + self.offer_selection_to_client::( + self.selection.get(), + &surface.client, + ); + self.offer_selection_to_client::( + self.primary_selection.get(), + &surface.client, + ); } } } @@ -820,9 +826,11 @@ 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.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..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, @@ -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(); } @@ -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 a6047fe4..b18b6399 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::{x_data_device::XIpcDeviceIds, 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, @@ -196,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/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.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 ba661c0e..2a25e45e 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, - }, - IpcVtable, + 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) { @@ -202,20 +186,20 @@ 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(); } } 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 = T::get_offer_seat(&offer); - 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 = T::get_offer_seat(&offer); + 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 T::get_offer_data(&offer).source() { - None => return, - Some(s) if T::get_source_data(&s).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 T::get_source_data(src).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) = T::get_source_data(src).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,19 +1722,24 @@ 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, + }); + 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 { + +}