diff --git a/src/compositor.rs b/src/compositor.rs index 798cfe81..98e07650 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -191,6 +191,7 @@ fn start_compositor2( enabled: Cell::new(true), handler: Default::default(), queue: Default::default(), + ipc_device_ids: Default::default(), }, acceptor: Default::default(), serial: Default::default(), diff --git a/src/ifs/ipc.rs b/src/ifs/ipc.rs index e9f320dc..478d01a7 100644 --- a/src/ifs/ipc.rs +++ b/src/ifs/ipc.rs @@ -1,8 +1,11 @@ use { crate::{ - client::{Client, ClientError, ClientId, WaylandObject}, + client::{Client, ClientError, ClientId}, fixed::Fixed, - ifs::wl_seat::{WlSeatError, WlSeatGlobal}, + ifs::{ + ipc::x_data_device::XIpcDevice, + wl_seat::{WlSeatError, WlSeatGlobal}, + }, utils::{ bitflags::BitflagsExt, cell_ext::CellExt, clonecell::CloneCell, numcell::NumCell, smallmap::SmallMap, @@ -25,6 +28,9 @@ pub mod wl_data_device; pub mod wl_data_device_manager; pub mod wl_data_offer; pub mod wl_data_source; +pub mod x_data_device; +pub mod x_data_offer; +pub mod x_data_source; pub mod zwp_primary_selection_device_manager_v1; pub mod zwp_primary_selection_device_v1; pub mod zwp_primary_selection_offer_v1; @@ -33,6 +39,12 @@ pub mod zwp_primary_selection_source_v1; linear_ids!(DataSourceIds, DataSourceId, u64); linear_ids!(DataOfferIds, DataOfferId, u64); +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum IpcLocation { + Clipboard, + PrimarySelection, +} + #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum Role { Selection, @@ -40,14 +52,15 @@ pub enum Role { } pub trait DataSource: DynDataSource { - fn send_cancelled(self: &Rc, seat: &Rc); + fn send_cancelled(&self, seat: &Rc); } pub trait DynDataSource: 'static { fn source_data(&self) -> &SourceData; - fn send_send(self: Rc, mime_type: &str, fd: Rc); - fn offer_to(self: Rc, client: &Rc); - fn detach_seat(self: Rc, seat: &Rc); + fn send_send(&self, mime_type: &str, fd: Rc); + fn offer_to_regular_client(self: Rc, client: &Rc); + fn offer_to_x(self: Rc, dd: &Rc); + fn detach_seat(&self, seat: &Rc); fn cancel_offers(&self); fn send_target(&self, mime_type: Option<&str>) { @@ -80,7 +93,7 @@ pub trait DataOffer: DynDataOffer { pub trait DynDataOffer: 'static { fn offer_id(&self) -> DataOfferId; fn client_id(&self) -> ClientId; - fn send_offer(self: Rc, mime_type: &str); + fn send_offer(&self, mime_type: &str); fn destroy(&self); fn cancel(&self); fn get_seat(&self) -> Rc; @@ -110,15 +123,18 @@ pub trait DynDataOffer: 'static { } } -pub trait XIpcVtable: IpcVtable { - fn create_xwm_source(client: &Rc) -> Self::Source; - fn remove_from_seat(device: &Self::Device); +pub trait IterableIpcVtable: IpcVtable { + fn for_each_device(seat: &WlSeatGlobal, client: ClientId, f: C) + where + C: FnMut(&Rc); } pub trait IpcVtable: Sized { + const LOCATION: IpcLocation; + type Device; type Source: DataSource; - type Offer: DataOffer + WaylandObject; + type Offer: DataOffer; fn get_device_data(dd: &Self::Device) -> &DeviceData; fn get_device_seat(dd: &Self::Device) -> Rc; @@ -127,36 +143,34 @@ pub trait IpcVtable: Sized { source: &Rc, serial: Option, ) -> Result<(), WlSeatError>; - fn for_each_device(seat: &WlSeatGlobal, client: ClientId, f: C) - where - C: FnMut(&Rc); fn create_offer( - client: &Rc, dd: &Rc, data: OfferData, ) -> Result, ClientError>; fn send_selection(dd: &Self::Device, offer: Option<&Rc>); fn send_offer(dd: &Self::Device, offer: &Rc); fn unset(seat: &Rc, role: Role); + fn device_client(dd: &Rc) -> &Rc; } pub struct DeviceData { selection: CloneCell>>, dnd: CloneCell>>, - pub is_xwm: bool, +} + +impl Default for DeviceData { + fn default() -> Self { + Self { + selection: Default::default(), + dnd: Default::default(), + } + } } pub struct OfferData { device: CloneCell>>, source: CloneCell>>, shared: Rc, - pub is_xwm: bool, -} - -impl OfferData { - pub fn source(&self) -> Option> { - self.source.get() - } } #[derive(Debug, Error)] @@ -189,7 +203,6 @@ pub struct SourceData { actions: Cell>, role: Cell, shared: CloneCell>, - pub is_xwm: bool, } struct SharedState { @@ -213,7 +226,7 @@ impl Default for SharedState { } impl SourceData { - fn new(client: &Rc, is_xwm: bool) -> Self { + pub fn new(client: &Rc) -> Self { Self { seat: Default::default(), id: client.state.data_source_ids.next(), @@ -224,7 +237,6 @@ impl SourceData { actions: Cell::new(None), role: Cell::new(Role::Selection), shared: Default::default(), - is_xwm, } } @@ -265,7 +277,7 @@ pub fn attach_seat( Ok(()) } -pub fn cancel_offers(src: &T::Source) { +pub fn cancel_offers(src: &S) { let data = src.source_data(); while let Some((_, offer)) = data.offers.pop() { offer.cancel(); @@ -278,17 +290,68 @@ pub fn cancel_offer(offer: &T::Offer) { destroy_data_offer::(&offer); } -pub fn detach_seat(src: &Rc, seat: &Rc) { +pub fn detach_seat(src: &S, seat: &Rc) { let data = src.source_data(); data.seat.set(None); - cancel_offers::(src); + cancel_offers(src); if !data.state.get().contains(SOURCE_STATE_FINISHED) { src.send_cancelled(seat); } // data.client.flush(); } -pub fn offer_source_to(src: &Rc, client: &Rc) { +fn offer_source_to_device( + src: &Rc, + dd: &Rc, + data: &SourceData, + shared: Rc, +) { + let device_data = T::get_device_data(dd); + let offer_data = OfferData { + device: CloneCell::new(Some(dd.clone())), + source: CloneCell::new(Some(src.clone())), + shared: shared.clone(), + }; + let offer = match T::create_offer(dd, offer_data) { + Ok(o) => o, + Err(e) => { + T::device_client(dd).error(e); + return; + } + }; + data.offers.insert(offer.offer_id(), offer.clone()); + let mt = data.mime_types.borrow_mut(); + T::send_offer(dd, &offer); + for mt in mt.deref() { + offer.clone().send_offer(mt); + } + match data.role.get() { + Role::Selection => { + T::send_selection(dd, Some(&offer)); + device_data.selection.set(Some(offer.clone())); + } + Role::Dnd => { + device_data.dnd.set(Some(offer.clone())); + } + } +} + +fn offer_source_to_x(src: &Rc, dd: &Rc) +where + T: IpcVtable, + S: DynDataSource, +{ + let data = src.source_data(); + src.cancel_offers(); + let shared = data.shared.get(); + shared.role.set(data.role.get()); + offer_source_to_device::(src, dd, data, shared); +} + +fn offer_source_to_regular_client( + src: &Rc, + client: &Rc, +) { let data = src.source_data(); let seat = match data.seat.get() { Some(a) => a, @@ -301,38 +364,7 @@ pub fn offer_source_to(src: &Rc, client: &Rc< let shared = data.shared.get(); shared.role.set(data.role.get()); T::for_each_device(&seat, client.id, |dd| { - let device_data = T::get_device_data(dd); - let offer_data = OfferData { - device: CloneCell::new(Some(dd.clone())), - source: CloneCell::new(Some(src.clone())), - shared: shared.clone(), - is_xwm: device_data.is_xwm, - }; - let offer = match T::create_offer(client, dd, offer_data) { - Ok(o) => o, - Err(e) => { - client.error(e); - return; - } - }; - data.offers.insert(offer.offer_id(), offer.clone()); - let mt = data.mime_types.borrow_mut(); - T::send_offer(dd, &offer); - for mt in mt.deref() { - offer.clone().send_offer(mt); - } - match data.role.get() { - Role::Selection => { - T::send_selection(dd, Some(&offer)); - device_data.selection.set(Some(offer.clone())); - } - Role::Dnd => { - device_data.dnd.set(Some(offer.clone())); - } - } - if !device_data.is_xwm { - client.add_server_obj(&offer); - } + offer_source_to_device::(src, dd, data, shared.clone()); }); } diff --git a/src/ifs/ipc/wl_data_device.rs b/src/ifs/ipc/wl_data_device.rs index a92ac640..6b7ef840 100644 --- a/src/ifs/ipc/wl_data_device.rs +++ b/src/ifs/ipc/wl_data_device.rs @@ -5,7 +5,8 @@ use { ifs::{ ipc::{ break_device_loops, destroy_data_device, wl_data_offer::WlDataOffer, - wl_data_source::WlDataSource, DeviceData, IpcVtable, OfferData, Role, XIpcVtable, + wl_data_source::WlDataSource, DeviceData, IpcLocation, IpcVtable, + IterableIpcVtable, OfferData, Role, }, wl_seat::{WlSeatError, WlSeatGlobal}, wl_surface::{SurfaceRole, WlSurfaceError}, @@ -13,8 +14,7 @@ use { leaks::Tracker, object::Object, utils::buffd::{MsgParser, MsgParserError}, - wire::{wl_data_device::*, WlDataDeviceId, WlDataOfferId, WlDataSourceId, WlSurfaceId}, - xwayland::XWaylandEvent, + wire::{wl_data_device::*, WlDataDeviceId, WlDataOfferId, WlSurfaceId}, }, std::rc::Rc, thiserror::Error, @@ -38,60 +38,34 @@ impl WlDataDevice { client: &Rc, version: u32, seat: &Rc, - is_xwm: bool, ) -> Self { Self { id, client: client.clone(), version, seat: seat.clone(), - data: DeviceData { - selection: Default::default(), - dnd: Default::default(), - is_xwm, - }, + data: Default::default(), tracker: Default::default(), } } pub fn send_data_offer(&self, offer: &Rc) { - if self.data.is_xwm { - self.client - .state - .xwayland - .queue - .push(XWaylandEvent::ClipboardSetOffer(offer.clone())); - } else { - self.client.event(DataOffer { - self_id: self.id, - id: offer.id, - }) - } + self.client.event(DataOffer { + self_id: self.id, + id: offer.id, + }) } pub fn send_selection(&self, offer: Option<&Rc>) { - if self.data.is_xwm { - self.client - .state - .xwayland - .queue - .push(XWaylandEvent::ClipboardSetSelection( - self.seat.id(), - offer.cloned(), - )); - } else { - let id = offer.map(|o| o.id).unwrap_or(WlDataOfferId::NONE); - self.client.event(Selection { - self_id: self.id, - id, - }) - } + let id = offer.map(|o| o.id).unwrap_or(WlDataOfferId::NONE); + self.client.event(Selection { + self_id: self.id, + id, + }) } pub fn send_leave(&self) { - if !self.data.is_xwm { - self.client.event(Leave { self_id: self.id }) - } + self.client.event(Leave { self_id: self.id }) } pub fn send_enter( @@ -102,33 +76,27 @@ impl WlDataDevice { offer: WlDataOfferId, serial: u32, ) { - if !self.data.is_xwm { - self.client.event(Enter { - self_id: self.id, - serial, - surface, - x, - y, - id: offer, - }) - } + self.client.event(Enter { + self_id: self.id, + serial, + surface, + x, + y, + id: offer, + }) } pub fn send_motion(&self, time_usec: u64, x: Fixed, y: Fixed) { - if !self.data.is_xwm { - self.client.event(Motion { - self_id: self.id, - time: (time_usec / 1000) as _, - x, - y, - }) - } + self.client.event(Motion { + self_id: self.id, + time: (time_usec / 1000) as _, + x, + y, + }) } pub fn send_drop(&self) { - if !self.data.is_xwm { - self.client.event(Drop { self_id: self.id }) - } + self.client.event(Drop { self_id: self.id }) } fn start_drag(&self, parser: MsgParser<'_, '_>) -> Result<(), WlDataDeviceError> { @@ -185,17 +153,18 @@ impl WlDataDevice { pub struct ClipboardIpc; -impl XIpcVtable for ClipboardIpc { - fn create_xwm_source(client: &Rc) -> Self::Source { - WlDataSource::new(WlDataSourceId::NONE, client, true, 3) - } - - fn remove_from_seat(device: &Self::Device) { - device.seat.remove_data_device(device); +impl IterableIpcVtable for ClipboardIpc { + fn for_each_device(seat: &WlSeatGlobal, client: ClientId, f: C) + where + C: FnMut(&Rc), + { + seat.for_each_data_device(0, client, f); } } impl IpcVtable for ClipboardIpc { + const LOCATION: IpcLocation = IpcLocation::Clipboard; + type Device = WlDataDevice; type Source = WlDataSource; type Offer = WlDataOffer; @@ -216,27 +185,20 @@ impl IpcVtable for ClipboardIpc { seat.set_wl_data_source_selection(Some(source.clone()), serial) } - fn for_each_device(seat: &WlSeatGlobal, client: ClientId, f: C) - where - C: FnMut(&Rc), - { - seat.for_each_data_device(0, client, f); - } - fn create_offer( - client: &Rc, device: &Rc, offer_data: OfferData, ) -> Result, ClientError> { let rc = Rc::new(WlDataOffer { - id: client.new_id()?, - offer_id: client.state.data_offer_ids.next(), - client: client.clone(), + id: device.client.new_id()?, + offer_id: device.client.state.data_offer_ids.next(), + client: device.client.clone(), device: device.clone(), data: offer_data, tracker: Default::default(), }); - track!(client, rc); + track!(device.client, rc); + device.client.add_server_obj(&rc); Ok(rc) } @@ -254,6 +216,10 @@ impl IpcVtable for ClipboardIpc { Role::Dnd => seat.cancel_dnd(), } } + + fn device_client(dd: &Rc) -> &Rc { + &dd.client + } } object_base! { diff --git a/src/ifs/ipc/wl_data_device_manager.rs b/src/ifs/ipc/wl_data_device_manager.rs index 5feaa5dd..226dd8e9 100644 --- a/src/ifs/ipc/wl_data_device_manager.rs +++ b/src/ifs/ipc/wl_data_device_manager.rs @@ -61,7 +61,7 @@ impl WlDataDeviceManager { parser: MsgParser<'_, '_>, ) -> Result<(), WlDataDeviceManagerError> { let req: CreateDataSource = self.client.parse(self, parser)?; - let res = Rc::new(WlDataSource::new(req.id, &self.client, false, self.version)); + let res = Rc::new(WlDataSource::new(req.id, &self.client, self.version)); track!(self.client, res); self.client.add_client_obj(&res)?; Ok(()) @@ -78,7 +78,6 @@ impl WlDataDeviceManager { &self.client, self.version, &seat.global, - false, )); track!(self.client, dev); seat.global.add_data_device(&dev); diff --git a/src/ifs/ipc/wl_data_offer.rs b/src/ifs/ipc/wl_data_offer.rs index 7553dbff..17078882 100644 --- a/src/ifs/ipc/wl_data_offer.rs +++ b/src/ifs/ipc/wl_data_offer.rs @@ -19,7 +19,6 @@ use { buffd::{MsgParser, MsgParserError}, }, wire::{wl_data_offer::*, WlDataOfferId, WlSurfaceId}, - xwayland::XWaylandEvent, }, std::rc::Rc, thiserror::Error, @@ -64,8 +63,8 @@ impl DynDataOffer for WlDataOffer { WlDataOffer::send_action(self, action); } - fn send_offer(self: Rc, mime_type: &str) { - WlDataOffer::send_offer(&self, mime_type); + fn send_offer(&self, mime_type: &str) { + WlDataOffer::send_offer(self, mime_type); } fn destroy(&self) { @@ -90,46 +89,29 @@ impl DynDataOffer for WlDataOffer { } impl WlDataOffer { - pub fn send_offer(self: &Rc, mime_type: &str) { - if self.data.is_xwm { - if let Some(src) = self.data.source.get() { - if !src.source_data().is_xwm { - self.client.state.xwayland.queue.push( - XWaylandEvent::ClipboardAddOfferMimeType( - self.clone(), - mime_type.to_string(), - ), - ); - } - } - } else { - self.client.event(Offer { - self_id: self.id, - mime_type, - }) - } + pub fn send_offer(&self, mime_type: &str) { + self.client.event(Offer { + self_id: self.id, + mime_type, + }) } pub fn send_source_actions(&self) { - if !self.data.is_xwm { - if let Some(src) = self.data.source.get() { - if let Some(source_actions) = src.source_data().actions.get() { - self.client.event(SourceActions { - self_id: self.id, - source_actions, - }) - } + if let Some(src) = self.data.source.get() { + if let Some(source_actions) = src.source_data().actions.get() { + self.client.event(SourceActions { + self_id: self.id, + source_actions, + }) } } } pub fn send_action(&self, dnd_action: u32) { - if !self.data.is_xwm { - self.client.event(Action { - self_id: self.id, - dnd_action, - }) - } + self.client.event(Action { + self_id: self.id, + dnd_action, + }) } fn accept(&self, parser: MsgParser<'_, '_>) -> Result<(), WlDataOfferError> { diff --git a/src/ifs/ipc/wl_data_source.rs b/src/ifs/ipc/wl_data_source.rs index 74aa0948..d8b790a3 100644 --- a/src/ifs/ipc/wl_data_source.rs +++ b/src/ifs/ipc/wl_data_source.rs @@ -4,9 +4,10 @@ use { ifs::{ ipc::{ add_data_source_mime_type, break_source_loops, cancel_offers, destroy_data_source, - detach_seat, offer_source_to, + detach_seat, offer_source_to_regular_client, offer_source_to_x, wl_data_device::ClipboardIpc, wl_data_device_manager::{DND_ALL, DND_NONE}, + x_data_device::{XClipboardIpc, XIpcDevice}, DataSource, DynDataOffer, DynDataSource, SharedState, SourceData, OFFER_STATE_ACCEPTED, OFFER_STATE_DROPPED, SOURCE_STATE_CANCELLED, SOURCE_STATE_DROPPED, @@ -23,7 +24,6 @@ use { clonecell::CloneCell, }, wire::{wl_data_source::*, WlDataSourceId}, - xwayland::XWaylandEvent, }, std::rc::Rc, thiserror::Error, @@ -44,7 +44,7 @@ pub struct WlDataSource { } impl DataSource for WlDataSource { - fn send_cancelled(self: &Rc, seat: &Rc) { + fn send_cancelled(&self, seat: &Rc) { WlDataSource::send_cancelled(self, seat); } } @@ -54,20 +54,24 @@ impl DynDataSource for WlDataSource { &self.data } - fn send_send(self: Rc, mime_type: &str, fd: Rc) { - WlDataSource::send_send(&self, mime_type, fd); + fn send_send(&self, mime_type: &str, fd: Rc) { + WlDataSource::send_send(self, mime_type, fd); } - fn offer_to(self: Rc, client: &Rc) { - offer_source_to::(&self, client); + fn offer_to_regular_client(self: Rc, client: &Rc) { + offer_source_to_regular_client::(&self, client); } - fn detach_seat(self: Rc, seat: &Rc) { - detach_seat::(&self, seat); + fn offer_to_x(self: Rc, dd: &Rc) { + offer_source_to_x::(&self, dd); + } + + fn detach_seat(&self, seat: &Rc) { + detach_seat(self, seat); } fn cancel_offers(&self) { - cancel_offers::(self); + cancel_offers(self); } fn send_target(&self, mime_type: Option<&str>) { @@ -84,11 +88,11 @@ impl DynDataSource for WlDataSource { } impl WlDataSource { - pub fn new(id: WlDataSourceId, client: &Rc, is_xwm: bool, version: u32) -> Self { + pub fn new(id: WlDataSourceId, client: &Rc, version: u32) -> Self { Self { id, tracker: Default::default(), - data: SourceData::new(client, is_xwm), + data: SourceData::new(client), version, toplevel_drag: Default::default(), } @@ -108,7 +112,7 @@ impl WlDataSource { self.data.shared.set(Rc::new(SharedState::default())); self.send_target(None); self.send_action(DND_NONE); - cancel_offers::(self); + cancel_offers(self); } pub fn update_selected_action(&self) { @@ -159,74 +163,44 @@ impl WlDataSource { shared.state.or_assign(OFFER_STATE_DROPPED); } - pub fn send_cancelled(self: &Rc, seat: &Rc) { - if self.data.is_xwm { - self.data - .client - .state - .xwayland - .queue - .push(XWaylandEvent::ClipboardCancelSource(self.clone())); - } else { - self.data.state.or_assign(SOURCE_STATE_CANCELLED); - if let Some(drag) = self.toplevel_drag.take() { - drag.finish_drag(seat); - } - self.data.client.event(Cancelled { self_id: self.id }) + pub fn send_cancelled(&self, seat: &Rc) { + self.data.state.or_assign(SOURCE_STATE_CANCELLED); + if let Some(drag) = self.toplevel_drag.take() { + drag.finish_drag(seat); } + self.data.client.event(Cancelled { self_id: self.id }) } - pub fn send_send(self: &Rc, mime_type: &str, fd: Rc) { - if self.data.is_xwm { - self.data - .client - .state - .xwayland - .queue - .push(XWaylandEvent::ClipboardSendSource( - self.clone(), - mime_type.to_string(), - fd, - )); - } else { - self.data.client.event(Send { - self_id: self.id, - mime_type, - fd, - }) - } + pub fn send_send(&self, mime_type: &str, fd: Rc) { + self.data.client.event(Send { + self_id: self.id, + mime_type, + fd, + }) } pub fn send_target(&self, mime_type: Option<&str>) { - if !self.data.is_xwm { - self.data.client.event(Target { - self_id: self.id, - mime_type, - }) - } + self.data.client.event(Target { + self_id: self.id, + mime_type, + }) } pub fn send_dnd_finished(&self) { - if !self.data.is_xwm { - self.data.client.event(DndFinished { self_id: self.id }) - } + self.data.client.event(DndFinished { self_id: self.id }) } pub fn send_action(&self, dnd_action: u32) { - if !self.data.is_xwm { - self.data.client.event(Action { - self_id: self.id, - dnd_action, - }) - } + self.data.client.event(Action { + self_id: self.id, + dnd_action, + }) } pub fn send_dnd_drop_performed(&self) { - if !self.data.is_xwm { - self.data - .client - .event(DndDropPerformed { self_id: self.id }) - } + self.data + .client + .event(DndDropPerformed { self_id: self.id }) } fn offer(&self, parser: MsgParser<'_, '_>) -> Result<(), WlDataSourceError> { diff --git a/src/ifs/ipc/x_data_device.rs b/src/ifs/ipc/x_data_device.rs new file mode 100644 index 00000000..e9f6e4a0 --- /dev/null +++ b/src/ifs/ipc/x_data_device.rs @@ -0,0 +1,134 @@ +use { + crate::{ + client::{Client, ClientError}, + ifs::{ + ipc::{ + x_data_offer::XDataOffer, x_data_source::XDataSource, DeviceData, IpcLocation, + IpcVtable, OfferData, Role, + }, + wl_seat::{WlSeatError, WlSeatGlobal}, + }, + state::State, + xwayland::XWaylandEvent, + }, + std::rc::Rc, + XWaylandEvent::IpcSetOffer, +}; + +linear_ids!(XIpcDeviceIds, XIpcDeviceId, u64); + +pub struct XIpcDevice { + pub id: XIpcDeviceId, + pub clipboard: DeviceData, + pub primary_selection: DeviceData, + pub seat: Rc, + pub state: Rc, + pub client: Rc, +} + +#[derive(Default)] +pub struct XClipboardIpc; + +#[derive(Default)] +pub struct XPrimarySelectionIpc; + +pub trait XIpc { + const LOCATION: IpcLocation; + + fn x_unset(seat: &Rc); + + fn x_device_data(dd: &XIpcDevice) -> &DeviceData; +} + +impl XIpc for XClipboardIpc { + const LOCATION: IpcLocation = IpcLocation::Clipboard; + + fn x_unset(seat: &Rc) { + seat.unset_selection(); + } + + fn x_device_data(dd: &XIpcDevice) -> &DeviceData { + &dd.clipboard + } +} + +impl XIpc for XPrimarySelectionIpc { + const LOCATION: IpcLocation = IpcLocation::PrimarySelection; + + fn x_unset(seat: &Rc) { + seat.unset_primary_selection(); + } + + fn x_device_data(dd: &XIpcDevice) -> &DeviceData { + &dd.primary_selection + } +} + +impl IpcVtable for T { + const LOCATION: IpcLocation = T::LOCATION; + type Device = XIpcDevice; + type Source = XDataSource; + type Offer = XDataOffer; + + fn get_device_data(dd: &Self::Device) -> &DeviceData { + T::x_device_data(dd) + } + + fn get_device_seat(dd: &Self::Device) -> Rc { + dd.seat.clone() + } + + fn set_seat_selection( + seat: &Rc, + source: &Rc, + _serial: Option, + ) -> Result<(), WlSeatError> { + match source.location { + IpcLocation::Clipboard => seat.set_selection(Some(source.clone())), + IpcLocation::PrimarySelection => seat.set_primary_selection(Some(source.clone())), + } + } + + fn create_offer( + dd: &Rc, + data: OfferData, + ) -> Result, ClientError> { + debug_assert!(dd.client.is_xwayland); + let rc = Rc::new(XDataOffer { + offer_id: dd.state.data_offer_ids.next(), + device: dd.clone(), + data, + tracker: Default::default(), + location: T::LOCATION, + }); + track!(dd.client, rc); + Ok(rc) + } + + fn send_selection(dd: &Self::Device, offer: Option<&Rc>) { + dd.state + .xwayland + .queue + .push(XWaylandEvent::IpcSetSelection { + seat: dd.seat.id(), + location: T::LOCATION, + offer: offer.cloned(), + }); + } + + fn send_offer(dd: &Self::Device, offer: &Rc) { + dd.state.xwayland.queue.push(IpcSetOffer { + location: T::LOCATION, + seat: dd.seat.id(), + offer: offer.clone(), + }); + } + + fn unset(seat: &Rc, _role: Role) { + T::x_unset(seat) + } + + fn device_client(dd: &Rc) -> &Rc { + &dd.client + } +} diff --git a/src/ifs/ipc/x_data_offer.rs b/src/ifs/ipc/x_data_offer.rs new file mode 100644 index 00000000..7b9887dc --- /dev/null +++ b/src/ifs/ipc/x_data_offer.rs @@ -0,0 +1,70 @@ +use { + crate::{ + client::ClientId, + ifs::{ + ipc::{ + cancel_offer, destroy_data_offer, + x_data_device::{XClipboardIpc, XIpcDevice, XPrimarySelectionIpc}, + DataOffer, DataOfferId, DynDataOffer, IpcLocation, OfferData, + }, + wl_seat::WlSeatGlobal, + }, + leaks::Tracker, + xwayland::XWaylandEvent, + }, + std::rc::Rc, + XWaylandEvent::IpcAddOfferMimeType, +}; + +pub struct XDataOffer { + pub offer_id: DataOfferId, + pub device: Rc, + pub data: OfferData, + pub tracker: Tracker, + pub location: IpcLocation, +} + +impl DataOffer for XDataOffer { + type Device = XIpcDevice; + + fn offer_data(&self) -> &OfferData { + &self.data + } +} + +impl DynDataOffer for XDataOffer { + fn offer_id(&self) -> DataOfferId { + self.offer_id + } + + fn client_id(&self) -> ClientId { + self.device.client.id + } + + fn send_offer(&self, mime_type: &str) { + self.device.state.xwayland.queue.push(IpcAddOfferMimeType { + location: self.location, + seat: self.device.seat.id(), + offer: self.offer_id, + mime_type: mime_type.to_string(), + }) + } + + fn destroy(&self) { + match self.location { + IpcLocation::Clipboard => destroy_data_offer::(self), + IpcLocation::PrimarySelection => destroy_data_offer::(self), + } + } + + fn cancel(&self) { + match self.location { + IpcLocation::Clipboard => cancel_offer::(self), + IpcLocation::PrimarySelection => cancel_offer::(self), + } + } + + fn get_seat(&self) -> Rc { + self.device.seat.clone() + } +} diff --git a/src/ifs/ipc/x_data_source.rs b/src/ifs/ipc/x_data_source.rs new file mode 100644 index 00000000..cf2af861 --- /dev/null +++ b/src/ifs/ipc/x_data_source.rs @@ -0,0 +1,79 @@ +use { + crate::{ + client::Client, + ifs::{ + ipc::{ + cancel_offers, detach_seat, offer_source_to_regular_client, + wl_data_device::ClipboardIpc, x_data_device::XIpcDevice, + zwp_primary_selection_device_v1::PrimarySelectionIpc, DataSource, DynDataSource, + IpcLocation, SourceData, + }, + wl_seat::WlSeatGlobal, + }, + state::State, + xwayland::XWaylandEvent::{IpcCancelSource, IpcSendSource, IpcSetSelection}, + }, + std::rc::Rc, + uapi::OwnedFd, +}; + +pub struct XDataSource { + pub state: Rc, + pub device: Rc, + pub data: SourceData, + pub location: IpcLocation, +} + +impl DataSource for XDataSource { + fn send_cancelled(&self, seat: &Rc) { + self.state.xwayland.queue.push(IpcCancelSource { + location: self.location, + seat: seat.id(), + source: self.data.id, + }); + } +} + +impl DynDataSource for XDataSource { + fn source_data(&self) -> &SourceData { + &self.data + } + + fn send_send(&self, mime_type: &str, fd: Rc) { + self.state.xwayland.queue.push(IpcSendSource { + location: self.location, + seat: self.device.seat.id(), + source: self.data.id, + mime_type: mime_type.to_string(), + fd, + }); + } + + fn offer_to_regular_client(self: Rc, client: &Rc) { + match self.location { + IpcLocation::Clipboard => { + offer_source_to_regular_client::(&self, client) + } + IpcLocation::PrimarySelection => { + offer_source_to_regular_client::(&self, client) + } + } + } + + fn offer_to_x(self: Rc, _dd: &Rc) { + self.cancel_offers(); + self.state.xwayland.queue.push(IpcSetSelection { + location: self.location, + seat: self.device.seat.id(), + offer: None, + }); + } + + fn detach_seat(&self, seat: &Rc) { + detach_seat(self, seat); + } + + fn cancel_offers(&self) { + cancel_offers(self) + } +} diff --git a/src/ifs/ipc/zwp_primary_selection_device_manager_v1.rs b/src/ifs/ipc/zwp_primary_selection_device_manager_v1.rs index 1b33871b..fd1768ae 100644 --- a/src/ifs/ipc/zwp_primary_selection_device_manager_v1.rs +++ b/src/ifs/ipc/zwp_primary_selection_device_manager_v1.rs @@ -55,11 +55,7 @@ impl ZwpPrimarySelectionDeviceManagerV1 { parser: MsgParser<'_, '_>, ) -> Result<(), ZwpPrimarySelectionDeviceManagerV1Error> { let req: CreateSource = self.client.parse(self, parser)?; - let res = Rc::new(ZwpPrimarySelectionSourceV1::new( - req.id, - &self.client, - false, - )); + let res = Rc::new(ZwpPrimarySelectionSourceV1::new(req.id, &self.client)); track!(self.client, res); self.client.add_client_obj(&res)?; Ok(()) @@ -76,7 +72,6 @@ impl ZwpPrimarySelectionDeviceManagerV1 { &self.client, self.version, &seat.global, - false, )); track!(self.client, dev); seat.global.add_primary_selection_device(&dev); diff --git a/src/ifs/ipc/zwp_primary_selection_device_v1.rs b/src/ifs/ipc/zwp_primary_selection_device_v1.rs index b9a09176..c73814a5 100644 --- a/src/ifs/ipc/zwp_primary_selection_device_v1.rs +++ b/src/ifs/ipc/zwp_primary_selection_device_v1.rs @@ -6,7 +6,7 @@ use { break_device_loops, destroy_data_device, zwp_primary_selection_offer_v1::ZwpPrimarySelectionOfferV1, zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1, DeviceData, - IpcVtable, OfferData, Role, XIpcVtable, + IpcLocation, IpcVtable, IterableIpcVtable, OfferData, Role, }, wl_seat::{WlSeatError, WlSeatGlobal}, }, @@ -15,9 +15,8 @@ use { utils::buffd::{MsgParser, MsgParserError}, wire::{ zwp_primary_selection_device_v1::*, ZwpPrimarySelectionDeviceV1Id, - ZwpPrimarySelectionOfferV1Id, ZwpPrimarySelectionSourceV1Id, + ZwpPrimarySelectionOfferV1Id, }, - xwayland::XWaylandEvent, }, std::rc::Rc, thiserror::Error, @@ -38,56 +37,32 @@ impl ZwpPrimarySelectionDeviceV1 { client: &Rc, version: u32, seat: &Rc, - is_xwm: bool, ) -> Self { Self { id, client: client.clone(), version, seat: seat.clone(), - data: DeviceData { - selection: Default::default(), - dnd: Default::default(), - is_xwm, - }, + data: Default::default(), tracker: Default::default(), } } pub fn send_data_offer(&self, offer: &Rc) { - if self.data.is_xwm { - self.client - .state - .xwayland - .queue - .push(XWaylandEvent::PrimarySelectionSetOffer(offer.clone())); - } else { - self.client.event(DataOffer { - self_id: self.id, - offer: offer.id, - }) - } + self.client.event(DataOffer { + self_id: self.id, + offer: offer.id, + }) } pub fn send_selection(&self, offer: Option<&Rc>) { - if self.data.is_xwm { - self.client - .state - .xwayland - .queue - .push(XWaylandEvent::PrimarySelectionSetSelection( - self.seat.id(), - offer.cloned(), - )); - } else { - let id = offer - .map(|o| o.id) - .unwrap_or(ZwpPrimarySelectionOfferV1Id::NONE); - self.client.event(Selection { - self_id: self.id, - id, - }) - } + let id = offer + .map(|o| o.id) + .unwrap_or(ZwpPrimarySelectionOfferV1Id::NONE); + self.client.event(Selection { + self_id: self.id, + id, + }) } fn set_selection( @@ -126,17 +101,18 @@ impl ZwpPrimarySelectionDeviceV1 { pub struct PrimarySelectionIpc; -impl XIpcVtable for PrimarySelectionIpc { - fn create_xwm_source(client: &Rc) -> Self::Source { - ZwpPrimarySelectionSourceV1::new(ZwpPrimarySelectionSourceV1Id::NONE, client, true) - } - - fn remove_from_seat(device: &Self::Device) { - device.seat.remove_primary_selection_device(device); +impl IterableIpcVtable for PrimarySelectionIpc { + fn for_each_device(seat: &WlSeatGlobal, client: ClientId, f: C) + where + C: FnMut(&Rc), + { + seat.for_each_primary_selection_device(0, client, f) } } impl IpcVtable for PrimarySelectionIpc { + const LOCATION: IpcLocation = IpcLocation::PrimarySelection; + type Device = ZwpPrimarySelectionDeviceV1; type Source = ZwpPrimarySelectionSourceV1; type Offer = ZwpPrimarySelectionOfferV1; @@ -157,32 +133,20 @@ impl IpcVtable for PrimarySelectionIpc { seat.set_zwp_primary_selection(Some(source.clone()), serial) } - fn for_each_device(seat: &WlSeatGlobal, client: ClientId, f: C) - where - C: FnMut(&Rc), - { - seat.for_each_primary_selection_device(0, client, f) - } - fn create_offer( - client: &Rc, device: &Rc, offer_data: OfferData, ) -> Result, ClientError> { - let id = if device.data.is_xwm { - ZwpPrimarySelectionOfferV1Id::NONE - } else { - client.new_id()? - }; let rc = Rc::new(ZwpPrimarySelectionOfferV1 { - id, - offer_id: client.state.data_offer_ids.next(), + id: device.client.new_id()?, + offer_id: device.client.state.data_offer_ids.next(), seat: device.seat.clone(), - client: client.clone(), + client: device.client.clone(), data: offer_data, tracker: Default::default(), }); - track!(client, rc); + track!(device.client, rc); + device.client.add_server_obj(&rc); Ok(rc) } @@ -197,6 +161,10 @@ impl IpcVtable for PrimarySelectionIpc { fn unset(seat: &Rc, _role: Role) { seat.unset_primary_selection(); } + + fn device_client(dd: &Rc) -> &Rc { + &dd.client + } } object_base! { diff --git a/src/ifs/ipc/zwp_primary_selection_offer_v1.rs b/src/ifs/ipc/zwp_primary_selection_offer_v1.rs index 54fba396..d6a7caa3 100644 --- a/src/ifs/ipc/zwp_primary_selection_offer_v1.rs +++ b/src/ifs/ipc/zwp_primary_selection_offer_v1.rs @@ -15,7 +15,6 @@ use { object::Object, utils::buffd::{MsgParser, MsgParserError}, wire::{zwp_primary_selection_offer_v1::*, ZwpPrimarySelectionOfferV1Id}, - xwayland::XWaylandEvent, }, std::rc::Rc, thiserror::Error, @@ -47,8 +46,8 @@ impl DynDataOffer for ZwpPrimarySelectionOfferV1 { self.client.id } - fn send_offer(self: Rc, mime_type: &str) { - ZwpPrimarySelectionOfferV1::send_offer(&self, mime_type); + fn send_offer(&self, mime_type: &str) { + ZwpPrimarySelectionOfferV1::send_offer(self, mime_type); } fn destroy(&self) { @@ -65,24 +64,11 @@ impl DynDataOffer for ZwpPrimarySelectionOfferV1 { } impl ZwpPrimarySelectionOfferV1 { - pub fn send_offer(self: &Rc, mime_type: &str) { - if self.data.is_xwm { - if let Some(src) = self.data.source.get() { - if !src.source_data().is_xwm { - self.client.state.xwayland.queue.push( - XWaylandEvent::PrimarySelectionAddOfferMimeType( - self.clone(), - mime_type.to_string(), - ), - ); - } - } - } else { - self.client.event(Offer { - self_id: self.id, - mime_type, - }) - } + pub fn send_offer(&self, mime_type: &str) { + self.client.event(Offer { + self_id: self.id, + mime_type, + }) } fn receive(&self, parser: MsgParser<'_, '_>) -> Result<(), ZwpPrimarySelectionOfferV1Error> { diff --git a/src/ifs/ipc/zwp_primary_selection_source_v1.rs b/src/ifs/ipc/zwp_primary_selection_source_v1.rs index a657882b..d58861cd 100644 --- a/src/ifs/ipc/zwp_primary_selection_source_v1.rs +++ b/src/ifs/ipc/zwp_primary_selection_source_v1.rs @@ -4,7 +4,9 @@ use { ifs::{ ipc::{ add_data_source_mime_type, break_source_loops, cancel_offers, destroy_data_source, - detach_seat, offer_source_to, zwp_primary_selection_device_v1::PrimarySelectionIpc, + detach_seat, offer_source_to_regular_client, offer_source_to_x, + x_data_device::{XIpcDevice, XPrimarySelectionIpc}, + zwp_primary_selection_device_v1::PrimarySelectionIpc, DataSource, DynDataSource, SourceData, }, wl_seat::WlSeatGlobal, @@ -13,7 +15,6 @@ use { object::Object, utils::buffd::{MsgParser, MsgParserError}, wire::{zwp_primary_selection_source_v1::*, ZwpPrimarySelectionSourceV1Id}, - xwayland::XWaylandEvent, }, std::rc::Rc, thiserror::Error, @@ -27,7 +28,7 @@ pub struct ZwpPrimarySelectionSourceV1 { } impl DataSource for ZwpPrimarySelectionSourceV1 { - fn send_cancelled(self: &Rc, _seat: &Rc) { + fn send_cancelled(&self, _seat: &Rc) { ZwpPrimarySelectionSourceV1::send_cancelled(self); } } @@ -37,64 +38,46 @@ impl DynDataSource for ZwpPrimarySelectionSourceV1 { &self.data } - fn send_send(self: Rc, mime_type: &str, fd: Rc) { - ZwpPrimarySelectionSourceV1::send_send(&self, mime_type, fd) + fn send_send(&self, mime_type: &str, fd: Rc) { + ZwpPrimarySelectionSourceV1::send_send(self, mime_type, fd) } - fn offer_to(self: Rc, client: &Rc) { - offer_source_to::(&self, client); + fn offer_to_regular_client(self: Rc, client: &Rc) { + offer_source_to_regular_client::(&self, client); } - fn detach_seat(self: Rc, seat: &Rc) { - detach_seat::(&self, seat); + fn offer_to_x(self: Rc, dd: &Rc) { + offer_source_to_x::(&self, dd); + } + + fn detach_seat(&self, seat: &Rc) { + detach_seat(self, seat); } fn cancel_offers(&self) { - cancel_offers::(self); + cancel_offers(self); } } impl ZwpPrimarySelectionSourceV1 { - pub fn new(id: ZwpPrimarySelectionSourceV1Id, client: &Rc, is_xwm: bool) -> Self { + pub fn new(id: ZwpPrimarySelectionSourceV1Id, client: &Rc) -> Self { Self { id, - data: SourceData::new(client, is_xwm), + data: SourceData::new(client), tracker: Default::default(), } } - pub fn send_cancelled(self: &Rc) { - if self.data.is_xwm { - self.data - .client - .state - .xwayland - .queue - .push(XWaylandEvent::PrimarySelectionCancelSource(self.clone())); - } else { - self.data.client.event(Cancelled { self_id: self.id }); - } + pub fn send_cancelled(&self) { + self.data.client.event(Cancelled { self_id: self.id }); } - pub fn send_send(self: &Rc, mime_type: &str, fd: Rc) { - if self.data.is_xwm { - self.data - .client - .state - .xwayland - .queue - .push(XWaylandEvent::PrimarySelectionSendSource( - self.clone(), - mime_type.to_string(), - fd, - )); - } else { - self.data.client.event(Send { - self_id: self.id, - mime_type, - fd, - }) - } + pub fn send_send(&self, mime_type: &str, fd: Rc) { + self.data.client.event(Send { + self_id: self.id, + mime_type, + fd, + }) } fn offer(&self, parser: MsgParser<'_, '_>) -> Result<(), ZwpPrimarySelectionSourceV1Error> { diff --git a/src/ifs/wl_seat.rs b/src/ifs/wl_seat.rs index 1a3d6dc7..b305433d 100644 --- a/src/ifs/wl_seat.rs +++ b/src/ifs/wl_seat.rs @@ -22,6 +22,7 @@ use { self, wl_data_device::{ClipboardIpc, WlDataDevice}, wl_data_source::WlDataSource, + x_data_device::{XClipboardIpc, XIpcDevice, XIpcDeviceId, XPrimarySelectionIpc}, zwp_primary_selection_device_v1::{ PrimarySelectionIpc, ZwpPrimarySelectionDeviceV1, }, @@ -58,6 +59,7 @@ use { linkedlist::LinkedNode, numcell::NumCell, rc_eq::rc_eq, + smallmap::SmallMap, transform_ext::TransformExt, }, wire::{ @@ -108,7 +110,7 @@ pub struct DroppedDnd { impl Drop for DroppedDnd { fn drop(&mut self) { if let Some(src) = self.dnd.src.take() { - ipc::detach_seat::(&src, &self.dnd.seat); + ipc::detach_seat(&*src, &self.dnd.seat); } } } @@ -131,6 +133,7 @@ pub struct WlSeatGlobal { keyboard_node: CloneCell>, pressed_keys: RefCell>, bindings: RefCell>>>, + x_data_devices: SmallMap, 1>, data_devices: RefCell>>>, primary_selection_devices: RefCell< AHashMap< @@ -190,6 +193,7 @@ impl WlSeatGlobal { keyboard_node: CloneCell::new(state.root.clone()), pressed_keys: RefCell::new(Default::default()), bindings: Default::default(), + x_data_devices: Default::default(), data_devices: RefCell::new(Default::default()), primary_selection_devices: RefCell::new(Default::default()), repeat_rate: Cell::new((25, 250)), @@ -349,6 +353,20 @@ impl WlSeatGlobal { .insert(device.id, device.clone()); } + pub fn set_x_data_device(&self, device: &Rc) { + self.x_data_devices.insert(device.id, device.clone()); + } + + pub fn unset_x_data_device(&self, id: XIpcDeviceId) { + self.x_data_devices.remove(&id); + } + + pub fn for_each_x_data_device(&self, mut f: impl FnMut(&Rc)) { + for (_, dev) in &self.x_data_devices { + f(&dev); + } + } + pub fn remove_data_device(&self, device: &WlDataDevice) { let mut dd = self.data_devices.borrow_mut(); if let Entry::Occupied(mut e) = dd.entry(device.client.id) { @@ -709,11 +727,16 @@ impl WlSeatGlobal { } } - fn set_selection_( + fn set_selection_( self: &Rc, field: &CloneCell>>, src: Option>, - ) -> Result<(), WlSeatError> { + ) -> Result<(), WlSeatError> + where + T: ipc::IterableIpcVtable, + X: ipc::IpcVtable, + S: DynDataSource, + { if let (Some(new), Some(old)) = (&src, &field.get()) { if new.source_data().id == old.source_data().id { return Ok(()); @@ -727,15 +750,36 @@ impl WlSeatGlobal { old.detach_seat(self); } if let Some(client) = self.keyboard_node.get().node_client() { - match src { - Some(src) => ipc::offer_source_to::(&src, &client), + self.offer_selection_to_client::(src.map(|v| v as Rc<_>), &client); + // client.flush(); + } + Ok(()) + } + + fn offer_selection_to_client( + &self, + selection: Option>, + client: &Rc, + ) where + T: ipc::IterableIpcVtable, + X: ipc::IpcVtable, + { + if let Some(src) = &selection { + src.cancel_offers(); + } + if client.is_xwayland { + self.for_each_x_data_device(|dd| match &selection { + Some(src) => src.clone().offer_to_x(&dd), + _ => X::send_selection(&dd, None), + }); + } else { + match selection { + Some(src) => src.offer_to_regular_client(client), _ => T::for_each_device(self, client.id, |device| { T::send_selection(device, None); }), } - // client.flush(); } - Ok(()) } pub fn start_drag( @@ -780,7 +824,7 @@ impl WlSeatGlobal { self: &Rc, selection: Option>, ) -> Result<(), WlSeatError> { - self.set_selection_::(&self.selection, selection) + self.set_selection_::(&self.selection, selection) } pub fn may_modify_selection(&self, client: &Rc, serial: u32) -> bool { @@ -821,7 +865,10 @@ impl WlSeatGlobal { self: &Rc, selection: Option>, ) -> Result<(), WlSeatError> { - self.set_selection_::(&self.primary_selection, selection) + self.set_selection_::( + &self.primary_selection, + selection, + ) } pub fn reload_known_cursor(&self) { diff --git a/src/ifs/wl_seat/event_handling.rs b/src/ifs/wl_seat/event_handling.rs index 57748764..67263026 100644 --- a/src/ifs/wl_seat/event_handling.rs +++ b/src/ifs/wl_seat/event_handling.rs @@ -4,10 +4,13 @@ use { client::ClientId, fixed::Fixed, ifs::{ - ipc, ipc::{ wl_data_device::{ClipboardIpc, WlDataDevice}, - zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1, + x_data_device::{XClipboardIpc, XPrimarySelectionIpc}, + zwp_primary_selection_device_v1::{ + PrimarySelectionIpc, ZwpPrimarySelectionDeviceV1, + }, + DynDataSource, }, wl_seat::{ wl_keyboard::{self, WlKeyboard}, @@ -742,22 +745,14 @@ impl WlSeatGlobal { }); if self.keyboard_node.get().node_client_id() != Some(surface.client.id) { - match self.selection.get() { - Some(sel) => sel.offer_to(&surface.client), - None => { - self.for_each_data_device(0, surface.client.id, |dd| { - dd.send_selection(None); - }); - } - } - match self.primary_selection.get() { - Some(sel) => sel.offer_to(&surface.client), - None => { - self.for_each_primary_selection_device(0, surface.client.id, |dd| { - dd.send_selection(None); - }); - } - } + self.offer_selection_to_client::( + self.selection.get(), + &surface.client, + ); + self.offer_selection_to_client::( + self.primary_selection.get(), + &surface.client, + ); } } } @@ -819,7 +814,9 @@ impl WlSeatGlobal { serial: u32, ) { if let Some(src) = &dnd.src { - ipc::offer_source_to::(src, &surface.client); + if !surface.client.is_xwayland { + src.clone().offer_to_regular_client(&surface.client); + } src.for_each_data_offer(|offer| { offer.send_enter(surface.id, x, y, serial); offer.send_source_actions(); diff --git a/src/ifs/wl_seat/pointer_owner.rs b/src/ifs/wl_seat/pointer_owner.rs index 91c82e5d..7d4490c2 100644 --- a/src/ifs/wl_seat/pointer_owner.rs +++ b/src/ifs/wl_seat/pointer_owner.rs @@ -4,7 +4,7 @@ use { fixed::Fixed, ifs::{ ipc, - ipc::{wl_data_device::ClipboardIpc, wl_data_source::WlDataSource}, + ipc::wl_data_source::WlDataSource, wl_seat::{ wl_pointer::PendingScroll, Dnd, DroppedDnd, WlSeatError, WlSeatGlobal, CHANGE_CURSOR_MOVED, @@ -456,7 +456,7 @@ impl PointerOwner for DndPointerOwner { target.node_seat_state().remove_dnd_target(seat); if !should_drop { if let Some(src) = &self.dnd.src { - ipc::detach_seat::(src, seat); + ipc::detach_seat(&**src, seat); } } if let Some(icon) = self.icon.get() { @@ -527,7 +527,7 @@ impl PointerOwner for DndPointerOwner { target.node_on_dnd_leave(&self.dnd); target.node_seat_state().remove_dnd_target(seat); if let Some(src) = &self.dnd.src { - ipc::detach_seat::(src, seat); + ipc::detach_seat(&**src, seat); } if let Some(icon) = self.icon.get() { icon.set_dnd_icon_seat(seat.id(), None); diff --git a/src/state.rs b/src/state.rs index 89601ebe..b18b6399 100644 --- a/src/state.rs +++ b/src/state.rs @@ -26,7 +26,7 @@ use { ifs::{ ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1, ext_session_lock_v1::ExtSessionLockV1, - ipc::{DataOfferIds, DataSourceIds}, + ipc::{x_data_device::XIpcDeviceIds, DataOfferIds, DataSourceIds}, jay_render_ctx::JayRenderCtx, jay_seat_events::JaySeatEvents, jay_workspace_watcher::JayWorkspaceWatcher, @@ -198,6 +198,7 @@ pub struct XWaylandState { pub enabled: Cell, pub handler: RefCell>>, pub queue: Rc>, + pub ipc_device_ids: XIpcDeviceIds, } pub struct IdleState { diff --git a/src/xwayland.rs b/src/xwayland.rs index e98199f8..79e5a398 100644 --- a/src/xwayland.rs +++ b/src/xwayland.rs @@ -7,11 +7,7 @@ use { compositor::DISPLAY, forker::{ForkerError, ForkerProxy}, ifs::{ - ipc::{ - wl_data_offer::WlDataOffer, wl_data_source::WlDataSource, - zwp_primary_selection_offer_v1::ZwpPrimarySelectionOfferV1, - zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1, - }, + ipc::{x_data_offer::XDataOffer, DataOfferId, DataSourceId, IpcLocation}, wl_seat::SeatId, wl_surface::x_surface::xwindow::{Xwindow, XwindowData}, }, @@ -237,15 +233,32 @@ pub enum XWaylandEvent { #[allow(dead_code)] SeatChanged, - PrimarySelectionCancelSource(Rc), - PrimarySelectionSendSource(Rc, String, Rc), - PrimarySelectionSetOffer(Rc), - PrimarySelectionSetSelection(SeatId, Option>), - PrimarySelectionAddOfferMimeType(Rc, String), - - ClipboardCancelSource(Rc), - ClipboardSendSource(Rc, String, Rc), - ClipboardSetOffer(Rc), - ClipboardSetSelection(SeatId, Option>), - ClipboardAddOfferMimeType(Rc, String), + IpcCancelSource { + location: IpcLocation, + seat: SeatId, + source: DataSourceId, + }, + IpcSendSource { + location: IpcLocation, + seat: SeatId, + source: DataSourceId, + mime_type: String, + fd: Rc, + }, + IpcSetOffer { + location: IpcLocation, + seat: SeatId, + offer: Rc, + }, + IpcSetSelection { + location: IpcLocation, + seat: SeatId, + offer: Option>, + }, + IpcAddOfferMimeType { + location: IpcLocation, + seat: SeatId, + offer: DataOfferId, + mime_type: String, + }, } diff --git a/src/xwayland/xwm.rs b/src/xwayland/xwm.rs index 9b9a8d25..e28f7724 100644 --- a/src/xwayland/xwm.rs +++ b/src/xwayland/xwm.rs @@ -8,11 +8,11 @@ use { ipc::{ add_data_source_mime_type, destroy_data_device, destroy_data_offer, destroy_data_source, receive_data_offer, - wl_data_device::{ClipboardIpc, WlDataDevice}, - zwp_primary_selection_device_v1::{ - PrimarySelectionIpc, ZwpPrimarySelectionDeviceV1, - }, - DataOffer, DynDataOffer, DynDataSource, IpcVtable, XIpcVtable, + x_data_device::{XClipboardIpc, XIpc, XIpcDevice, XPrimarySelectionIpc}, + x_data_offer::XDataOffer, + x_data_source::XDataSource, + DataOfferId, DataSourceId, DynDataOffer, DynDataSource, IpcLocation, IpcVtable, + SourceData, }, wl_seat::{SeatId, WlSeatGlobal}, wl_surface::{ @@ -30,7 +30,7 @@ use { copyhashmap::CopyHashMap, errorfmt::ErrorFmt, linkedlist::LinkedList, numcell::NumCell, oserror::OsError, rc_eq::rc_eq, }, - wire::{WlDataDeviceId, WlSurfaceId, ZwpPrimarySelectionDeviceV1Id}, + wire::WlSurfaceId, wire_xcon::{ ChangeProperty, ChangeWindowAttributes, ClientMessage, CompositeRedirectSubwindows, ConfigureNotify, ConfigureRequest, ConfigureWindow, ConfigureWindowValues, @@ -67,6 +67,7 @@ use { std::{ borrow::Cow, cell::{Cell, RefCell}, + marker::PhantomData, mem::{self}, ops::{Deref, DerefMut}, rc::Rc, @@ -151,47 +152,30 @@ atoms! { XdndTypeList, } -struct EnhancedOffer { - offer: Rc, +struct EnhancedOffer { + offer: Rc, mime_types: RefCell>, active: Cell, } -struct SelectionData { - devices: CopyHashMap>, - sources: CopyHashMap>, - offers: CopyHashMap>>, - active_offer: CloneCell>>>, +#[derive(Default)] +struct SelectionData { + sources: CopyHashMap>, + offers: CopyHashMap>, + active_offer: CloneCell>>, win: Cell, selection: Cell, pending_transfers: RefCell>, + _phantom: PhantomData, } -impl Default for SelectionData { - fn default() -> Self { - Self { - devices: Default::default(), - sources: Default::default(), - offers: Default::default(), - active_offer: Default::default(), - win: Cell::new(0), - selection: Cell::new(0), - pending_transfers: RefCell::new(vec![]), - } - } -} - -impl SelectionData { +impl SelectionData { fn destroy(&self) { for (_, offer) in self.offers.lock().drain() { destroy_data_offer::(&offer.offer); } self.active_offer.take(); self.destroy_sources(); - for (_, device) in self.devices.lock().drain() { - destroy_data_device::(&device); - T::remove_from_seat(&device); - } } fn destroy_sources(&self) { @@ -208,14 +192,14 @@ impl SelectionData { } self.offers.remove(&id); self.sources.remove(&id); - self.devices.remove(&id); } } #[derive(Default)] pub struct XwmShared { - data: SelectionData, - primary_selection: SelectionData, + devices: CopyHashMap>, + data: SelectionData, + primary_selection: SelectionData, transfers: CopyHashMap>, } @@ -223,6 +207,11 @@ impl Drop for XwmShared { fn drop(&mut self) { self.data.destroy(); self.primary_selection.destroy(); + for (_, device) in self.devices.lock().drain() { + destroy_data_device::(&device); + destroy_data_device::(&device); + device.seat.unset_x_data_device(device.id); + } self.transfers.clear(); } } @@ -557,27 +546,19 @@ impl Wm { for seat in removed_seats { self.shared.data.seat_removed(seat); self.shared.primary_selection.seat_removed(seat); + self.shared.devices.remove(&seat); } for seat in new_seats { - let dd = Rc::new(WlDataDevice::new( - WlDataDeviceId::NONE, - &self.client, - 1, - &seat, - true, - )); - seat.add_data_device(&dd); - self.shared.data.devices.set(seat.id(), dd); - - let dd = Rc::new(ZwpPrimarySelectionDeviceV1::new( - ZwpPrimarySelectionDeviceV1Id::NONE, - &self.client, - 1, - &seat, - true, - )); - seat.add_primary_selection_device(&dd); - self.shared.primary_selection.devices.set(seat.id(), dd); + let dd = Rc::new(XIpcDevice { + id: self.state.xwayland.ipc_device_ids.next(), + clipboard: Default::default(), + primary_selection: Default::default(), + seat: seat.clone(), + state: self.state.clone(), + client: self.client.clone(), + }); + seat.set_x_data_device(&dd); + self.shared.devices.set(seat.id(), dd.clone()); } self.known_seats = current_seats; } @@ -611,55 +592,121 @@ impl Wm { XWaylandEvent::ActivateRoot => self.activate_window(None, Initiator::Wayland).await, XWaylandEvent::Close(window) => self.close_window(&window).await, XWaylandEvent::SeatChanged => self.seats_changed(), - XWaylandEvent::PrimarySelectionCancelSource(src) => { - self.dd_cancel_source(&self.shared.clone().primary_selection, &src) - } - XWaylandEvent::PrimarySelectionSendSource(src, mime_type, fd) => { - self.dd_send_source(&self.shared.clone().primary_selection, &src, mime_type, fd) - .await; - } - XWaylandEvent::PrimarySelectionSetOffer(offer) => { - self.dd_set_offer(&self.shared.clone().primary_selection, offer) - .await; - } - XWaylandEvent::PrimarySelectionSetSelection(seat, offer) => { - self.dd_set_selection(&self.shared.clone().primary_selection, seat, offer) - .await; - } - XWaylandEvent::PrimarySelectionAddOfferMimeType(offer, mt) => { - self.dd_add_offer_mime_type(&self.shared.clone().primary_selection, offer, mt) - .await; - } - XWaylandEvent::ClipboardCancelSource(src) => { - self.dd_cancel_source(&self.shared.clone().data, &src) - } - XWaylandEvent::ClipboardSendSource(src, mime_type, fd) => { - self.dd_send_source(&self.shared.clone().data, &src, mime_type, fd) - .await; - } - XWaylandEvent::ClipboardSetOffer(offer) => { - self.dd_set_offer(&self.shared.clone().data, offer).await; - } - XWaylandEvent::ClipboardSetSelection(seat, offer) => { - self.dd_set_selection(&self.shared.clone().data, seat, offer) - .await; - } - XWaylandEvent::ClipboardAddOfferMimeType(offer, mt) => { - self.dd_add_offer_mime_type(&self.shared.clone().data, offer, mt) - .await; - } + XWaylandEvent::IpcCancelSource { + location, + seat, + source, + } => match location { + IpcLocation::Clipboard => { + self.dd_cancel_source::(&self.shared.clone().data, seat, source) + } + IpcLocation::PrimarySelection => self.dd_cancel_source::( + &self.shared.clone().primary_selection, + seat, + source, + ), + }, + XWaylandEvent::IpcSendSource { + location, + seat, + source, + mime_type, + fd, + } => match location { + IpcLocation::Clipboard => { + self.dd_send_source::( + &self.shared.clone().data, + seat, + source, + mime_type, + fd, + ) + .await + } + IpcLocation::PrimarySelection => { + self.dd_send_source::( + &self.shared.clone().primary_selection, + seat, + source, + mime_type, + fd, + ) + .await + } + }, + XWaylandEvent::IpcSetOffer { + location, + seat, + offer, + } => match location { + IpcLocation::Clipboard => { + self.dd_set_offer::(&self.shared.clone().data, seat, offer) + .await + } + IpcLocation::PrimarySelection => { + self.dd_set_offer::( + &self.shared.clone().primary_selection, + seat, + offer, + ) + .await + } + }, + XWaylandEvent::IpcSetSelection { + seat, + location, + offer, + } => match location { + IpcLocation::Clipboard => { + self.dd_set_selection::(&self.shared.clone().data, seat, offer) + .await + } + IpcLocation::PrimarySelection => { + self.dd_set_selection::( + &self.shared.clone().primary_selection, + seat, + offer, + ) + .await + } + }, + XWaylandEvent::IpcAddOfferMimeType { + location, + seat, + offer, + mime_type, + } => match location { + IpcLocation::Clipboard => { + self.dd_add_offer_mime_type::( + &self.shared.clone().data, + seat, + offer, + mime_type, + ) + .await + } + IpcLocation::PrimarySelection => { + self.dd_add_offer_mime_type::( + &self.shared.clone().primary_selection, + seat, + offer, + mime_type, + ) + .await + } + }, } } - async fn dd_add_offer_mime_type( + async fn dd_add_offer_mime_type( &mut self, sd: &SelectionData, - offer: Rc, + seat: SeatId, + offer: DataOfferId, mt: String, ) { - let seat = offer.get_seat(); - let enhanced = match sd.offers.get(&seat.id()) { - Some(r) if !rc_eq(&r.offer, &offer) => { + let enhanced = match sd.offers.get(&seat) { + Some(r) if r.offer.offer_id != offer => { return; } None => { @@ -677,20 +724,19 @@ impl Wm { enhanced.mime_types.borrow_mut().push(mt); } - async fn dd_set_offer(&mut self, sd: &SelectionData, offer: Rc) { - let seat = offer.get_seat(); + async fn dd_set_offer( + &mut self, + sd: &SelectionData, + seat: SeatId, + offer: Rc, + ) { let mut mime_types = vec![]; - if let Some(offer) = sd.offers.remove(&seat.id()) { + if let Some(offer) = sd.offers.remove(&seat) { destroy_data_offer::(&offer.offer); mime_types = mem::take(offer.mime_types.borrow_mut().deref_mut()); } - match offer.offer_data().source() { - None => return, - Some(s) if s.source_data().is_xwm => return, - _ => {} - } sd.offers.set( - seat.id(), + seat, Rc::new(EnhancedOffer { offer, mime_types: RefCell::new(mime_types), @@ -699,11 +745,11 @@ impl Wm { ); } - async fn dd_set_selection( + async fn dd_set_selection( &mut self, sd: &SelectionData, seat: SeatId, - offer: Option>, + offer: Option>, ) { let offer = match offer { None => { @@ -794,22 +840,19 @@ impl Wm { } } - async fn dd_send_source( + async fn dd_send_source( &mut self, sd: &SelectionData, - src: &Rc, + seat: SeatId, + src: DataSourceId, mime_type: String, fd: Rc, ) { - let seat = match src.source_data().seat.get() { - Some(s) => s, - _ => return, - }; - let actual_src = match sd.sources.get(&seat.id()) { + let actual_src = match sd.sources.get(&seat) { None => return, Some(src) => src, }; - if !rc_eq(src, &actual_src) { + if actual_src.source_data().id != src { return; } let mime_type = match self.mime_type_to_atom(mime_type).await { @@ -838,13 +881,16 @@ impl Wm { .push(PendingTransfer { mime_type, fd }); } - fn dd_cancel_source(&mut self, sd: &SelectionData, src: &Rc) { - if let Some(seat) = src.source_data().seat.get() { - if let Some(cur) = sd.sources.get(&seat.id()) { - if rc_eq(src, &cur) { - sd.sources.remove(&seat.id()); - destroy_data_source::(&cur); - } + fn dd_cancel_source( + &mut self, + sd: &SelectionData, + seat: SeatId, + source: DataSourceId, + ) { + if let Some(cur) = sd.sources.get(&seat) { + if cur.source_data().id == source { + sd.sources.remove(&seat); + destroy_data_source::(&cur); } } } @@ -1508,7 +1554,7 @@ impl Wm { } } - async fn handle_xfixes_selection_notify_( + async fn handle_xfixes_selection_notify_( &mut self, sd: &SelectionData, event: &XfixesSelectionNotify, @@ -1562,7 +1608,7 @@ impl Wm { } } - async fn handle_selection_request_( + async fn handle_selection_request_( &mut self, sd: &SelectionData, event: &SelectionRequest, @@ -1663,7 +1709,7 @@ impl Wm { } } - async fn handle_selection_notify_( + async fn handle_selection_notify_( &mut self, sd: &SelectionData, event: &SelectionNotify, @@ -1676,12 +1722,17 @@ impl Wm { } if event.target == self.atoms.TARGETS { let targets = self.get_selection_mime_types(sd.win.get()).await?; - for dev in sd.devices.lock().values() { + for dev in self.shared.devices.lock().values() { let seat = T::get_device_seat(dev); if !seat.may_modify_primary_selection(&self.client, None) { continue; } - let source = Rc::new(T::create_xwm_source(&self.client)); + let source = Rc::new(XDataSource { + state: self.state.clone(), + device: dev.clone(), + data: SourceData::new(&self.client), + location: T::LOCATION, + }); if let Err(e) = T::set_seat_selection(&seat, &source, None) { log::error!("Could not set selection: {}", ErrorFmt(e)); return Ok(());