1
0
Fork 0
forked from wry/wry

Merge pull request #288 from mahkoh/jorth/data-control

ipc: implement ext-data-control
This commit is contained in:
mahkoh 2024-10-25 15:35:59 +02:00 committed by GitHub
commit 163bb2c893
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
33 changed files with 1440 additions and 766 deletions

View file

@ -140,6 +140,7 @@ Jay supports the following wayland protocols:
| Global | Version | Privileged |
|------------------------------------------------------|:----------------|---------------|
| ext_data_control_manager_v1 | 1 | Yes |
| ext_foreign_toplevel_image_capture_source_manager_v1 | 1 | |
| ext_foreign_toplevel_list_v1 | 1 | Yes |
| ext_idle_notifier_v1 | 1 | Yes |

View file

@ -1,5 +1,7 @@
# Unreleased
- Add support fo ext-data-control-v1.
# 1.7.0 (2024-10-25)
- Various bugfixes.

View file

@ -6,7 +6,11 @@ use {
ext_image_capture_source_v1::ExtImageCaptureSourceV1,
ext_image_copy::ext_image_copy_capture_session_v1::ExtImageCopyCaptureSessionV1,
ipc::{
wl_data_source::WlDataSource, zwlr_data_control_source_v1::ZwlrDataControlSourceV1,
data_control::{
ext_data_control_source_v1::ExtDataControlSourceV1,
zwlr_data_control_source_v1::ZwlrDataControlSourceV1,
},
wl_data_source::WlDataSource,
zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1,
},
jay_output::JayOutput,
@ -34,7 +38,7 @@ use {
copyhashmap::{CopyHashMap, Locked},
},
wire::{
ExtForeignToplevelHandleV1Id, ExtImageCaptureSourceV1Id,
ExtDataControlSourceV1Id, ExtForeignToplevelHandleV1Id, ExtImageCaptureSourceV1Id,
ExtImageCopyCaptureSessionV1Id, JayOutputId, JayScreencastId, JayToplevelId,
JayWorkspaceId, WlBufferId, WlDataSourceId, WlOutputId, WlPointerId, WlRegionId,
WlRegistryId, WlSeatId, WlSurfaceId, WpDrmLeaseConnectorV1Id,
@ -77,6 +81,7 @@ pub struct Objects {
CopyHashMap<ExtForeignToplevelHandleV1Id, Rc<ExtForeignToplevelHandleV1>>,
pub ext_copy_sessions:
CopyHashMap<ExtImageCopyCaptureSessionV1Id, Rc<ExtImageCopyCaptureSessionV1>>,
pub ext_data_sources: CopyHashMap<ExtDataControlSourceV1Id, Rc<ExtDataControlSourceV1>>,
ids: RefCell<Vec<usize>>,
}
@ -113,6 +118,7 @@ impl Objects {
image_capture_sources: Default::default(),
foreign_toplevel_handles: Default::default(),
ext_copy_sessions: Default::default(),
ext_data_sources: Default::default(),
ids: RefCell::new(vec![]),
}
}
@ -153,6 +159,7 @@ impl Objects {
self.image_capture_sources.clear();
self.foreign_toplevel_handles.clear();
self.ext_copy_sessions.clear();
self.ext_data_sources.clear();
}
pub fn id<T>(&self, client_data: &Client) -> Result<T, ClientError>

View file

@ -274,6 +274,7 @@ fn start_compositor2(
toplevels: Default::default(),
const_40hz_latch: Default::default(),
tray_item_ids: Default::default(),
data_control_device_ids: Default::default(),
});
state.tracker.register(ClientId::from_raw(0));
create_dummy_output(&state);

View file

@ -10,8 +10,11 @@ use {
ext_output_image_capture_source_manager_v1::ExtOutputImageCaptureSourceManagerV1Global,
ext_session_lock_manager_v1::ExtSessionLockManagerV1Global,
ipc::{
data_control::{
ext_data_control_manager_v1::ExtDataControlManagerV1Global,
zwlr_data_control_manager_v1::ZwlrDataControlManagerV1Global,
},
wl_data_device_manager::WlDataDeviceManagerGlobal,
zwlr_data_control_manager_v1::ZwlrDataControlManagerV1Global,
zwp_primary_selection_device_manager_v1::ZwpPrimarySelectionDeviceManagerV1Global,
},
jay_compositor::JayCompositorGlobal,
@ -207,6 +210,7 @@ impl Globals {
add_singleton!(ExtImageCopyCaptureManagerV1Global);
add_singleton!(WpFifoManagerV1Global);
add_singleton!(WpCommitTimingManagerV1Global);
add_singleton!(ExtDataControlManagerV1Global);
}
pub fn add_backend_singletons(&self, backend: &Rc<dyn Backend>) {

View file

@ -2,12 +2,7 @@ use {
crate::{
client::{Client, ClientError, ClientId},
fixed::Fixed,
ifs::{
ipc::{
x_data_device::XIpcDevice, zwlr_data_control_device_v1::ZwlrDataControlDeviceV1,
},
wl_seat::{WlSeatError, WlSeatGlobal},
},
ifs::{ipc::x_data_device::XIpcDevice, wl_seat::WlSeatGlobal},
utils::{
bitflags::BitflagsExt, cell_ext::CellExt, clonecell::CloneCell, numcell::NumCell,
smallmap::SmallMap,
@ -26,6 +21,7 @@ use {
uapi::OwnedFd,
};
pub mod data_control;
pub mod wl_data_device;
pub mod wl_data_device_manager;
pub mod wl_data_offer;
@ -33,10 +29,6 @@ pub mod wl_data_source;
pub mod x_data_device;
pub mod x_data_offer;
pub mod x_data_source;
pub mod zwlr_data_control_device_v1;
pub mod zwlr_data_control_manager_v1;
pub mod zwlr_data_control_offer_v1;
pub mod zwlr_data_control_source_v1;
pub mod zwp_primary_selection_device_manager_v1;
pub mod zwp_primary_selection_device_v1;
pub mod zwp_primary_selection_offer_v1;
@ -64,9 +56,7 @@ pub trait DataSource: DynDataSource {
pub trait DynDataSource: 'static {
fn source_data(&self) -> &SourceData;
fn send_send(&self, mime_type: &str, fd: Rc<OwnedFd>);
fn offer_to_regular_client(self: Rc<Self>, client: &Rc<Client>);
fn offer_to_x(self: Rc<Self>, dd: &Rc<XIpcDevice>);
fn offer_to_wlr_device(self: Rc<Self>, dd: &Rc<ZwlrDataControlDeviceV1>);
fn detach_seat(&self, seat: &Rc<WlSeatGlobal>);
fn cancel_unprivileged_offers(&self);
@ -139,12 +129,6 @@ pub trait IterableIpcVtable: IpcVtable {
C: FnMut(&Rc<Self::Device>);
}
pub trait WlrIpcVtable: IpcVtable<Device = ZwlrDataControlDeviceV1> {
fn for_each_device<C>(seat: &WlSeatGlobal, f: C)
where
C: FnMut(&Rc<Self::Device>);
}
pub trait IpcVtable: Sized {
type Device;
type Source: DataSource;
@ -152,11 +136,6 @@ pub trait IpcVtable: Sized {
fn get_device_data(dd: &Self::Device) -> &DeviceData<Self::Offer>;
fn get_device_seat(dd: &Self::Device) -> Rc<WlSeatGlobal>;
fn set_seat_selection(
seat: &Rc<WlSeatGlobal>,
source: &Rc<Self::Source>,
serial: Option<u64>,
) -> Result<(), WlSeatError>;
fn create_offer(
dd: &Rc<Self::Device>,
data: OfferData<Self::Device>,
@ -320,8 +299,8 @@ pub fn detach_seat<S: DataSource>(src: &S, seat: &Rc<WlSeatGlobal>) {
// data.client.flush();
}
fn offer_source_to_device<T: IpcVtable, S: DynDataSource>(
src: &Rc<S>,
fn offer_source_to_device<T: IpcVtable>(
src: &Rc<dyn DynDataSource>,
dd: &Rc<T::Device>,
data: &SourceData,
shared: Rc<SharedState>,
@ -356,31 +335,29 @@ fn offer_source_to_device<T: IpcVtable, S: DynDataSource>(
}
}
fn offer_source_to_x<T, S>(src: &Rc<S>, dd: &Rc<XIpcDevice>)
fn offer_source_to_x<T>(src: Rc<dyn DynDataSource>, dd: &Rc<XIpcDevice>)
where
T: IpcVtable<Device = XIpcDevice>,
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::<T, S>(src, dd, data, shared);
offer_source_to_device::<T>(&src, dd, data, shared);
}
pub fn offer_source_to_wlr_device<T, S>(src: &Rc<S>, dd: &Rc<T::Device>)
pub fn offer_source_to_data_control_device<T>(src: Rc<dyn DynDataSource>, dd: &Rc<T::Device>)
where
T: IpcVtable<Device = ZwlrDataControlDeviceV1>,
S: DynDataSource,
T: IpcVtable,
{
let data = src.source_data();
let shared = data.shared.get();
shared.role.set(data.role.get());
offer_source_to_device::<T, _>(src, dd, data, shared);
offer_source_to_device::<T>(&src, dd, data, shared);
}
fn offer_source_to_regular_client<T: IterableIpcVtable, S: DynDataSource>(
src: &Rc<S>,
pub fn offer_source_to_regular_client<T: IterableIpcVtable>(
src: Rc<dyn DynDataSource>,
client: &Rc<Client>,
) {
let data = src.source_data();
@ -395,7 +372,7 @@ fn offer_source_to_regular_client<T: IterableIpcVtable, S: DynDataSource>(
let shared = data.shared.get();
shared.role.set(data.role.get());
T::for_each_device(&seat, client.id, |dd| {
offer_source_to_device::<T, S>(src, dd, data, shared.clone());
offer_source_to_device::<T>(&src, dd, data, shared.clone());
});
}

View file

@ -0,0 +1,26 @@
use {
crate::ifs::ipc::{DynDataSource, IpcLocation},
std::rc::Rc,
};
pub mod ext_data_control_device_v1;
pub mod ext_data_control_manager_v1;
pub mod ext_data_control_offer_v1;
pub mod ext_data_control_source_v1;
mod private;
pub mod zwlr_data_control_device_v1;
pub mod zwlr_data_control_manager_v1;
pub mod zwlr_data_control_offer_v1;
pub mod zwlr_data_control_source_v1;
linear_ids!(DataControlDeviceIds, DataControlDeviceId, u64);
pub trait DynDataControlDevice {
fn id(&self) -> DataControlDeviceId;
fn handle_new_source(
self: Rc<Self>,
location: IpcLocation,
source: Option<Rc<dyn DynDataSource>>,
);
}

View file

@ -0,0 +1,158 @@
use {
crate::{
client::Client,
ifs::{
ipc::data_control::{
ext_data_control_offer_v1::ExtDataControlOfferV1,
ext_data_control_source_v1::ExtDataControlSourceV1,
private::{
logic::{self, DataControlError},
DataControlDevice, DataControlDeviceData, DataControlIpc, DataControlOfferData,
},
},
wl_seat::WlSeatGlobal,
},
leaks::Tracker,
object::{Object, Version},
wire::{
ext_data_control_device_v1::*, ExtDataControlDeviceV1Id, ExtDataControlOfferV1Id,
ExtDataControlSourceV1Id,
},
},
std::rc::Rc,
thiserror::Error,
};
pub struct ExtDataControlDeviceV1 {
pub id: ExtDataControlDeviceV1Id,
pub data: DataControlDeviceData<ExtDataControlIpc>,
pub tracker: Tracker<Self>,
}
impl ExtDataControlDeviceV1 {
pub fn new(
id: ExtDataControlDeviceV1Id,
client: &Rc<Client>,
version: Version,
seat: &Rc<WlSeatGlobal>,
) -> Self {
Self {
id,
data: DataControlDeviceData {
data_control_device_id: client.state.data_control_device_ids.next(),
client: client.clone(),
version,
seat: seat.clone(),
clipboard_data: Default::default(),
primary_selection_data: Default::default(),
},
tracker: Default::default(),
}
}
pub fn send_data_offer(&self, offer: &Rc<ExtDataControlOfferV1>) {
self.data.client.event(DataOffer {
self_id: self.id,
id: offer.id,
})
}
pub fn send_selection(&self, offer: Option<&Rc<ExtDataControlOfferV1>>) {
let id = offer.map(|o| o.id).unwrap_or(ExtDataControlOfferV1Id::NONE);
self.data.client.event(Selection {
self_id: self.id,
id,
})
}
pub fn send_primary_selection(&self, offer: Option<&Rc<ExtDataControlOfferV1>>) {
let id = offer.map(|o| o.id).unwrap_or(ExtDataControlOfferV1Id::NONE);
self.data.client.event(PrimarySelection {
self_id: self.id,
id,
})
}
}
impl ExtDataControlDeviceV1RequestHandler for ExtDataControlDeviceV1 {
type Error = ExtDataControlDeviceV1Error;
fn set_selection(&self, req: SetSelection, _slf: &Rc<Self>) -> Result<(), Self::Error> {
logic::device_set_selection(self, req.source.is_some().then_some(req.source))?;
Ok(())
}
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
logic::device_destroy(self)?;
Ok(())
}
fn set_primary_selection(
&self,
req: SetPrimarySelection,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
logic::device_set_primary_selection(self, req.source.is_some().then_some(req.source))?;
Ok(())
}
}
pub struct ExtDataControlIpc;
impl DataControlIpc for ExtDataControlIpc {
const PRIMARY_SELECTION_SINCE: Version = Version(1);
type Device = ExtDataControlDeviceV1;
type OfferId = ExtDataControlOfferV1Id;
type Offer = ExtDataControlOfferV1;
type SourceId = ExtDataControlSourceV1Id;
type Source = ExtDataControlSourceV1;
fn create_offer(id: Self::OfferId, data: DataControlOfferData<Self>) -> Rc<Self::Offer> {
let rc = Rc::new(ExtDataControlOfferV1 {
id,
data,
tracker: Default::default(),
});
track!(rc.data.client, rc);
rc
}
}
impl DataControlDevice for ExtDataControlDeviceV1 {
type Ipc = ExtDataControlIpc;
fn data(&self) -> &DataControlDeviceData<Self::Ipc> {
&self.data
}
fn send_data_offer(&self, offer: &Rc<<Self::Ipc as DataControlIpc>::Offer>) {
self.send_data_offer(offer)
}
fn send_selection(&self, offer: Option<&Rc<<Self::Ipc as DataControlIpc>::Offer>>) {
self.send_selection(offer)
}
fn send_primary_selection(&self, offer: Option<&Rc<<Self::Ipc as DataControlIpc>::Offer>>) {
self.send_primary_selection(offer)
}
}
object_base! {
self = ExtDataControlDeviceV1;
version = self.data.version;
}
impl Object for ExtDataControlDeviceV1 {
fn break_loops(&self) {
logic::data_device_break_loops(self);
}
}
simple_add_obj!(ExtDataControlDeviceV1);
#[derive(Debug, Error)]
pub enum ExtDataControlDeviceV1Error {
#[error(transparent)]
DataControlError(#[from] DataControlError),
}

View file

@ -0,0 +1,134 @@
use {
crate::{
client::{Client, ClientCaps, ClientError, CAP_DATA_CONTROL_MANAGER},
globals::{Global, GlobalName},
ifs::ipc::{
data_control::{
ext_data_control_device_v1::ExtDataControlDeviceV1,
ext_data_control_source_v1::ExtDataControlSourceV1, DynDataControlDevice,
},
IpcLocation,
},
leaks::Tracker,
object::{Object, Version},
wire::{ext_data_control_manager_v1::*, ExtDataControlManagerV1Id},
},
std::rc::Rc,
thiserror::Error,
};
pub struct ExtDataControlManagerV1Global {
name: GlobalName,
}
pub struct ExtDataControlManagerV1 {
pub id: ExtDataControlManagerV1Id,
pub client: Rc<Client>,
pub version: Version,
tracker: Tracker<Self>,
}
impl ExtDataControlManagerV1Global {
pub fn new(name: GlobalName) -> Self {
Self { name }
}
fn bind_(
self: Rc<Self>,
id: ExtDataControlManagerV1Id,
client: &Rc<Client>,
version: Version,
) -> Result<(), ExtDataControlManagerV1Error> {
let obj = Rc::new(ExtDataControlManagerV1 {
id,
client: client.clone(),
version,
tracker: Default::default(),
});
track!(client, obj);
client.add_client_obj(&obj)?;
Ok(())
}
}
impl ExtDataControlManagerV1RequestHandler for ExtDataControlManagerV1 {
type Error = ExtDataControlManagerV1Error;
fn create_data_source(
&self,
req: CreateDataSource,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
let res = Rc::new(ExtDataControlSourceV1::new(
req.id,
&self.client,
self.version,
));
track!(self.client, res);
self.client.add_client_obj(&res)?;
Ok(())
}
fn get_data_device(&self, req: GetDataDevice, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let seat = self.client.lookup(req.seat)?;
let dev = Rc::new(ExtDataControlDeviceV1::new(
req.id,
&self.client,
self.version,
&seat.global,
));
track!(self.client, dev);
seat.global.add_data_control_device(dev.clone());
self.client.add_client_obj(&dev)?;
dev.clone()
.handle_new_source(IpcLocation::Clipboard, seat.global.get_selection());
dev.clone().handle_new_source(
IpcLocation::PrimarySelection,
seat.global.get_primary_selection(),
);
Ok(())
}
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.client.remove_obj(self)?;
Ok(())
}
}
global_base!(
ExtDataControlManagerV1Global,
ExtDataControlManagerV1,
ExtDataControlManagerV1Error
);
impl Global for ExtDataControlManagerV1Global {
fn singleton(&self) -> bool {
true
}
fn version(&self) -> u32 {
1
}
fn required_caps(&self) -> ClientCaps {
CAP_DATA_CONTROL_MANAGER
}
}
simple_add_global!(ExtDataControlManagerV1Global);
object_base! {
self = ExtDataControlManagerV1;
version = self.version;
}
impl Object for ExtDataControlManagerV1 {}
simple_add_obj!(ExtDataControlManagerV1);
#[derive(Debug, Error)]
pub enum ExtDataControlManagerV1Error {
#[error(transparent)]
ClientError(Box<ClientError>),
}
efrom!(ExtDataControlManagerV1Error, ClientError);

View file

@ -0,0 +1,76 @@
use {
crate::{
ifs::ipc::data_control::{
ext_data_control_device_v1::ExtDataControlIpc,
private::{
logic::{self, DataControlError},
DataControlOffer, DataControlOfferData,
},
},
leaks::Tracker,
object::Object,
wire::{ext_data_control_offer_v1::*, ExtDataControlOfferV1Id},
},
std::rc::Rc,
thiserror::Error,
};
pub struct ExtDataControlOfferV1 {
pub id: ExtDataControlOfferV1Id,
pub data: DataControlOfferData<ExtDataControlIpc>,
pub tracker: Tracker<Self>,
}
impl DataControlOffer for ExtDataControlOfferV1 {
type Ipc = ExtDataControlIpc;
fn data(&self) -> &DataControlOfferData<Self::Ipc> {
&self.data
}
fn send_offer(&self, mime_type: &str) {
self.send_offer(mime_type);
}
}
impl ExtDataControlOfferV1 {
pub fn send_offer(&self, mime_type: &str) {
self.data.client.event(Offer {
self_id: self.id,
mime_type,
})
}
}
impl ExtDataControlOfferV1RequestHandler for ExtDataControlOfferV1 {
type Error = ExtDataControlOfferV1Error;
fn receive(&self, req: Receive, _slf: &Rc<Self>) -> Result<(), Self::Error> {
logic::data_offer_receive(self, req.mime_type, req.fd);
Ok(())
}
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
logic::data_offer_destroy(self)?;
Ok(())
}
}
object_base! {
self = ExtDataControlOfferV1;
version = self.data.device.data.version;
}
impl Object for ExtDataControlOfferV1 {
fn break_loops(&self) {
logic::data_offer_break_loops(self);
}
}
simple_add_obj!(ExtDataControlOfferV1);
#[derive(Debug, Error)]
pub enum ExtDataControlOfferV1Error {
#[error(transparent)]
DataControlError(#[from] DataControlError),
}

View file

@ -0,0 +1,107 @@
use {
crate::{
client::Client,
ifs::ipc::{
data_control::{
ext_data_control_device_v1::ExtDataControlIpc,
private::{
logic::{self, DataControlError},
DataControlSource, DataControlSourceData,
},
},
IpcLocation, SourceData,
},
leaks::Tracker,
object::{Object, Version},
wire::{ext_data_control_source_v1::*, ExtDataControlSourceV1Id},
},
std::{cell::Cell, rc::Rc},
thiserror::Error,
uapi::OwnedFd,
};
pub struct ExtDataControlSourceV1 {
pub id: ExtDataControlSourceV1Id,
pub data: DataControlSourceData,
pub tracker: Tracker<Self>,
}
impl DataControlSource for ExtDataControlSourceV1 {
type Ipc = ExtDataControlIpc;
fn data(&self) -> &DataControlSourceData {
&self.data
}
fn send_cancelled(&self) {
self.send_cancelled();
}
fn send_send(&self, mime_type: &str, fd: Rc<OwnedFd>) {
self.send_send(mime_type, fd);
}
}
impl ExtDataControlSourceV1 {
pub fn new(id: ExtDataControlSourceV1Id, client: &Rc<Client>, version: Version) -> Self {
Self {
id,
data: DataControlSourceData {
data: SourceData::new(client),
version,
location: Cell::new(IpcLocation::Clipboard),
used: Cell::new(false),
},
tracker: Default::default(),
}
}
pub fn send_send(&self, mime_type: &str, fd: Rc<OwnedFd>) {
self.data.data.client.event(Send {
self_id: self.id,
mime_type,
fd,
})
}
pub fn send_cancelled(&self) {
self.data.data.client.event(Cancelled { self_id: self.id })
}
}
impl ExtDataControlSourceV1RequestHandler for ExtDataControlSourceV1 {
type Error = ExtDataControlSourceV1Error;
fn offer(&self, req: Offer, _slf: &Rc<Self>) -> Result<(), Self::Error> {
logic::data_source_offer(self, req.mime_type)?;
Ok(())
}
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
logic::data_source_destroy(self)?;
Ok(())
}
}
object_base! {
self = ExtDataControlSourceV1;
version = self.data.version;
}
impl Object for ExtDataControlSourceV1 {
fn break_loops(&self) {
logic::data_source_break_loops(self);
}
}
dedicated_add_obj!(
ExtDataControlSourceV1,
ExtDataControlSourceV1Id,
ext_data_sources
);
#[derive(Debug, Error)]
pub enum ExtDataControlSourceV1Error {
#[error(transparent)]
DataControlError(#[from] DataControlError),
}

View file

@ -0,0 +1,426 @@
use {
crate::{
client::{Client, ClientError, ClientId, WaylandObject, WaylandObjectLookup},
ifs::{
ipc::{
cancel_offer, cancel_offers,
data_control::{DataControlDeviceId, DynDataControlDevice},
detach_seat, offer_source_to_data_control_device, offer_source_to_x,
x_data_device::{XClipboardIpc, XIpcDevice, XPrimarySelectionIpc},
DataOffer, DataOfferId, DataSource, DeviceData, DynDataOffer, DynDataSource,
IpcLocation, IpcVtable, OfferData, Role, SourceData,
},
wl_seat::WlSeatGlobal,
},
object::{ObjectId, Version},
},
std::{cell::Cell, marker::PhantomData, rc::Rc},
uapi::OwnedFd,
};
struct ClipboardCore<T>(PhantomData<T>);
struct PrimarySelectionCore<T>(PhantomData<T>);
struct DataControlIpcImpl<T>(PhantomData<T>);
type Device<T> = <T as DataControlIpc>::Device;
type Offer<T> = <T as DataControlIpc>::Offer;
type Source<T> = <T as DataControlIpc>::Source;
type SourceId<T> = <T as DataControlIpc>::SourceId;
pub trait DataControlIpc: Sized + 'static {
const PRIMARY_SELECTION_SINCE: Version;
type Device: DataControlDevice<Ipc = Self>;
type OfferId: From<ObjectId>;
type Offer: DataControlOffer<Ipc = Self>;
type SourceId: WaylandObjectLookup<Object = Self::Source>;
type Source: DataControlSource<Ipc = Self>;
fn create_offer(id: Self::OfferId, data: DataControlOfferData<Self>) -> Rc<Self::Offer>;
}
pub struct DataControlDeviceData<T: DataControlIpc> {
pub data_control_device_id: DataControlDeviceId,
pub client: Rc<Client>,
pub version: Version,
pub seat: Rc<WlSeatGlobal>,
pub clipboard_data: DeviceData<T::Offer>,
pub primary_selection_data: DeviceData<T::Offer>,
}
pub trait DataControlDevice: WaylandObject {
type Ipc: DataControlIpc<Device = Self>;
fn data(&self) -> &DataControlDeviceData<Self::Ipc>;
fn send_data_offer(&self, offer: &Rc<Offer<Self::Ipc>>);
fn send_selection(&self, offer: Option<&Rc<Offer<Self::Ipc>>>);
fn send_primary_selection(&self, offer: Option<&Rc<Offer<Self::Ipc>>>);
}
pub struct DataControlOfferData<T: DataControlIpc> {
pub offer_id: DataOfferId,
pub client: Rc<Client>,
pub device: Rc<T::Device>,
pub data: OfferData<T::Device>,
pub location: IpcLocation,
}
pub trait DataControlOffer: WaylandObject {
type Ipc: DataControlIpc<Offer = Self>;
fn data(&self) -> &DataControlOfferData<Self::Ipc>;
fn send_offer(&self, mime_type: &str);
}
pub struct DataControlSourceData {
pub data: SourceData,
pub version: Version,
pub location: Cell<IpcLocation>,
pub used: Cell<bool>,
}
pub trait DataControlSource: WaylandObject {
type Ipc: DataControlIpc<Source = Self>;
fn data(&self) -> &DataControlSourceData;
fn send_cancelled(&self);
fn send_send(&self, mime_type: &str, fd: Rc<OwnedFd>);
}
impl<T: DataControlDevice> DynDataControlDevice for T {
fn id(&self) -> DataControlDeviceId {
self.data().data_control_device_id
}
fn handle_new_source(
self: Rc<Self>,
location: IpcLocation,
source: Option<Rc<dyn DynDataSource>>,
) {
if location == IpcLocation::PrimarySelection
&& self.data().version < T::Ipc::PRIMARY_SELECTION_SINCE
{
return;
}
match location {
IpcLocation::Clipboard => match source {
Some(src) => {
offer_source_to_data_control_device::<Clipboard<T::Ipc>>(src, &self);
}
_ => self.send_selection(None),
},
IpcLocation::PrimarySelection => match source {
Some(src) => {
offer_source_to_data_control_device::<PrimarySelection<T::Ipc>>(src, &self);
}
_ => self.send_primary_selection(None),
},
}
}
}
type Clipboard<T> = DataControlIpcImpl<ClipboardCore<T>>;
type PrimarySelection<T> = DataControlIpcImpl<PrimarySelectionCore<T>>;
pub trait DataControlLocationIpc {
type Ipc: DataControlIpc;
const LOCATION: IpcLocation;
fn loc_get_device_data(dd: &Device<Self::Ipc>) -> &DeviceData<Offer<Self::Ipc>>;
fn loc_send_selection(dd: &Device<Self::Ipc>, offer: Option<&Rc<Offer<Self::Ipc>>>);
fn loc_unset(seat: &Rc<WlSeatGlobal>);
}
impl<T: DataControlIpc> DataControlLocationIpc for ClipboardCore<T> {
type Ipc = T;
const LOCATION: IpcLocation = IpcLocation::Clipboard;
fn loc_get_device_data(dd: &Device<Self::Ipc>) -> &DeviceData<Offer<Self::Ipc>> {
&dd.data().clipboard_data
}
fn loc_send_selection(dd: &Device<Self::Ipc>, offer: Option<&Rc<Offer<Self::Ipc>>>) {
dd.send_selection(offer)
}
fn loc_unset(seat: &Rc<WlSeatGlobal>) {
seat.unset_selection()
}
}
impl<T: DataControlIpc> DataControlLocationIpc for PrimarySelectionCore<T> {
type Ipc = T;
const LOCATION: IpcLocation = IpcLocation::PrimarySelection;
fn loc_get_device_data(dd: &Device<Self::Ipc>) -> &DeviceData<Offer<Self::Ipc>> {
&dd.data().primary_selection_data
}
fn loc_send_selection(dd: &Device<Self::Ipc>, offer: Option<&Rc<Offer<Self::Ipc>>>) {
dd.send_primary_selection(offer)
}
fn loc_unset(seat: &Rc<WlSeatGlobal>) {
seat.unset_primary_selection()
}
}
impl<T: DataControlLocationIpc> IpcVtable for DataControlIpcImpl<T> {
type Device = Device<T::Ipc>;
type Source = Source<T::Ipc>;
type Offer = Offer<T::Ipc>;
fn get_device_data(dd: &Self::Device) -> &DeviceData<Self::Offer> {
T::loc_get_device_data(dd)
}
fn get_device_seat(dd: &Self::Device) -> Rc<WlSeatGlobal> {
dd.data().seat.clone()
}
fn create_offer(
device: &Rc<Self::Device>,
offer_data: OfferData<Self::Device>,
) -> Result<Rc<Self::Offer>, ClientError> {
let data = device.data();
let offer = DataControlOfferData {
offer_id: data.client.state.data_offer_ids.next(),
client: data.client.clone(),
device: device.clone(),
data: offer_data,
location: T::LOCATION,
};
let rc = T::Ipc::create_offer(data.client.new_id()?, offer);
data.client.add_server_obj(&rc);
Ok(rc)
}
fn send_selection(dd: &Self::Device, offer: Option<&Rc<Self::Offer>>) {
T::loc_send_selection(dd, offer)
}
fn send_offer(dd: &Self::Device, offer: &Rc<Self::Offer>) {
dd.send_data_offer(offer);
}
fn unset(seat: &Rc<WlSeatGlobal>, _role: Role) {
T::loc_unset(seat)
}
fn device_client(dd: &Rc<Self::Device>) -> &Rc<Client> {
&dd.data().client
}
}
impl<T: DataControlSource> DataSource for T {
fn send_cancelled(&self, _seat: &Rc<WlSeatGlobal>) {
self.send_cancelled();
}
}
impl<T: DataControlSource> DynDataSource for T {
fn source_data(&self) -> &SourceData {
&self.data().data
}
fn send_send(&self, mime_type: &str, fd: Rc<OwnedFd>) {
self.send_send(mime_type, fd);
}
fn offer_to_x(self: Rc<Self>, dd: &Rc<XIpcDevice>) {
match self.data().location.get() {
IpcLocation::Clipboard => offer_source_to_x::<XClipboardIpc>(self, dd),
IpcLocation::PrimarySelection => offer_source_to_x::<XPrimarySelectionIpc>(self, dd),
}
}
fn detach_seat(&self, seat: &Rc<WlSeatGlobal>) {
detach_seat(self, seat)
}
fn cancel_unprivileged_offers(&self) {
cancel_offers(self, false)
}
}
impl<T: DataControlOffer> DataOffer for T {
type Device = Device<T::Ipc>;
fn offer_data(&self) -> &OfferData<Self::Device> {
&self.data().data
}
}
impl<T: DataControlOffer> DynDataOffer for T {
fn offer_id(&self) -> DataOfferId {
self.data().offer_id
}
fn client_id(&self) -> ClientId {
self.data().client.id
}
fn send_offer(&self, mime_type: &str) {
self.send_offer(mime_type);
}
fn cancel(&self) {
match self.data().location {
IpcLocation::Clipboard => cancel_offer::<Clipboard<T::Ipc>>(self),
IpcLocation::PrimarySelection => cancel_offer::<PrimarySelection<T::Ipc>>(self),
}
}
fn get_seat(&self) -> Rc<WlSeatGlobal> {
self.data().device.data().seat.clone()
}
fn is_privileged(&self) -> bool {
true
}
}
pub mod logic {
use {
crate::{
client::ClientError,
ifs::{
ipc::{
add_data_source_mime_type, break_device_loops, break_offer_loops,
break_source_loops,
data_control::private::{
Clipboard, DataControlDevice, DataControlOffer, DataControlSource,
PrimarySelection, Source, SourceId,
},
destroy_data_device, destroy_data_offer, destroy_data_source,
receive_data_offer, IpcLocation,
},
wl_seat::WlSeatError,
},
},
std::rc::Rc,
thiserror::Error,
uapi::OwnedFd,
};
pub fn data_device_break_loops<D: DataControlDevice>(d: &D) {
break_device_loops::<Clipboard<D::Ipc>>(d);
break_device_loops::<PrimarySelection<D::Ipc>>(d);
d.data().seat.remove_data_control_device(d);
}
fn use_source<D: DataControlDevice>(
device: &D,
source: Option<SourceId<D::Ipc>>,
location: IpcLocation,
) -> Result<Option<Rc<Source<D::Ipc>>>, DataControlError> {
if let Some(source) = source {
let src = device.data().client.lookup(source)?;
if src.data().used.replace(true) {
return Err(DataControlError::AlreadyUsed);
}
src.data().location.set(location);
Ok(Some(src))
} else {
Ok(None)
}
}
pub fn device_set_selection<D: DataControlDevice>(
d: &D,
source: Option<SourceId<D::Ipc>>,
) -> Result<(), DataControlError> {
let src = use_source(d, source, IpcLocation::Clipboard)?;
d.data().seat.set_selection(src)?;
Ok(())
}
pub fn device_destroy<D: DataControlDevice>(d: &D) -> Result<(), DataControlError> {
destroy_data_device::<Clipboard<D::Ipc>>(d);
destroy_data_device::<PrimarySelection<D::Ipc>>(d);
d.data().seat.remove_data_control_device(d);
d.data().client.remove_obj(d)?;
Ok(())
}
pub fn device_set_primary_selection<D: DataControlDevice>(
d: &D,
source: Option<SourceId<D::Ipc>>,
) -> Result<(), DataControlError> {
let src = use_source(d, source, IpcLocation::PrimarySelection)?;
d.data().seat.set_primary_selection(src)?;
Ok(())
}
pub fn data_source_offer<S: DataControlSource>(
s: &S,
mime_type: &str,
) -> Result<(), DataControlError> {
if s.data().used.get() {
return Err(DataControlError::AlreadyUsed);
}
add_data_source_mime_type::<Clipboard<S::Ipc>>(s, mime_type);
Ok(())
}
pub fn data_source_destroy<S: DataControlSource>(s: &S) -> Result<(), DataControlError> {
match s.data().location.get() {
IpcLocation::Clipboard => destroy_data_source::<Clipboard<S::Ipc>>(s),
IpcLocation::PrimarySelection => destroy_data_source::<PrimarySelection<S::Ipc>>(s),
}
s.data().data.client.remove_obj(s)?;
Ok(())
}
pub fn data_source_break_loops<S: DataControlSource>(s: &S) {
match s.data().location.get() {
IpcLocation::Clipboard => break_source_loops::<Clipboard<S::Ipc>>(s),
IpcLocation::PrimarySelection => break_source_loops::<PrimarySelection<S::Ipc>>(s),
}
}
pub fn data_offer_receive<O: DataControlOffer>(o: &O, mime_type: &str, fd: Rc<OwnedFd>) {
match o.data().location {
IpcLocation::Clipboard => receive_data_offer::<Clipboard<O::Ipc>>(o, mime_type, fd),
IpcLocation::PrimarySelection => {
receive_data_offer::<PrimarySelection<O::Ipc>>(o, mime_type, fd)
}
}
}
pub fn data_offer_destroy<O: DataControlOffer>(o: &O) -> Result<(), DataControlError> {
match o.data().location {
IpcLocation::Clipboard => destroy_data_offer::<Clipboard<O::Ipc>>(o),
IpcLocation::PrimarySelection => destroy_data_offer::<PrimarySelection<O::Ipc>>(o),
}
o.data().client.remove_obj(o)?;
Ok(())
}
pub fn data_offer_break_loops<O: DataControlOffer>(o: &O) {
match o.data().location {
IpcLocation::Clipboard => break_offer_loops::<Clipboard<O::Ipc>>(o),
IpcLocation::PrimarySelection => break_offer_loops::<PrimarySelection<O::Ipc>>(o),
}
}
#[derive(Debug, Error)]
pub enum DataControlError {
#[error(transparent)]
ClientError(Box<ClientError>),
#[error(transparent)]
WlSeatError(Box<WlSeatError>),
#[error("The source has already been used")]
AlreadyUsed,
}
efrom!(DataControlError, ClientError);
efrom!(DataControlError, WlSeatError);
}

View file

@ -0,0 +1,164 @@
use {
crate::{
client::Client,
ifs::{
ipc::data_control::{
private::{
logic::{self, DataControlError},
DataControlDevice, DataControlDeviceData, DataControlIpc, DataControlOfferData,
},
zwlr_data_control_offer_v1::ZwlrDataControlOfferV1,
zwlr_data_control_source_v1::ZwlrDataControlSourceV1,
},
wl_seat::WlSeatGlobal,
},
leaks::Tracker,
object::{Object, Version},
wire::{
zwlr_data_control_device_v1::*, ZwlrDataControlDeviceV1Id, ZwlrDataControlOfferV1Id,
ZwlrDataControlSourceV1Id,
},
},
std::rc::Rc,
thiserror::Error,
};
pub const PRIMARY_SELECTION_SINCE: Version = Version(2);
pub struct ZwlrDataControlDeviceV1 {
pub id: ZwlrDataControlDeviceV1Id,
pub data: DataControlDeviceData<WlrDataControlIpc>,
pub tracker: Tracker<Self>,
}
impl ZwlrDataControlDeviceV1 {
pub fn new(
id: ZwlrDataControlDeviceV1Id,
client: &Rc<Client>,
version: Version,
seat: &Rc<WlSeatGlobal>,
) -> Self {
Self {
id,
data: DataControlDeviceData {
data_control_device_id: client.state.data_control_device_ids.next(),
client: client.clone(),
version,
seat: seat.clone(),
clipboard_data: Default::default(),
primary_selection_data: Default::default(),
},
tracker: Default::default(),
}
}
pub fn send_data_offer(&self, offer: &Rc<ZwlrDataControlOfferV1>) {
self.data.client.event(DataOffer {
self_id: self.id,
id: offer.id,
})
}
pub fn send_selection(&self, offer: Option<&Rc<ZwlrDataControlOfferV1>>) {
let id = offer
.map(|o| o.id)
.unwrap_or(ZwlrDataControlOfferV1Id::NONE);
self.data.client.event(Selection {
self_id: self.id,
id,
})
}
pub fn send_primary_selection(&self, offer: Option<&Rc<ZwlrDataControlOfferV1>>) {
let id = offer
.map(|o| o.id)
.unwrap_or(ZwlrDataControlOfferV1Id::NONE);
self.data.client.event(PrimarySelection {
self_id: self.id,
id,
})
}
}
impl ZwlrDataControlDeviceV1RequestHandler for ZwlrDataControlDeviceV1 {
type Error = ZwlrDataControlDeviceV1Error;
fn set_selection(&self, req: SetSelection, _slf: &Rc<Self>) -> Result<(), Self::Error> {
logic::device_set_selection(self, req.source.is_some().then_some(req.source))?;
Ok(())
}
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
logic::device_destroy(self)?;
Ok(())
}
fn set_primary_selection(
&self,
req: SetPrimarySelection,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
logic::device_set_primary_selection(self, req.source.is_some().then_some(req.source))?;
Ok(())
}
}
pub struct WlrDataControlIpc;
impl DataControlIpc for WlrDataControlIpc {
const PRIMARY_SELECTION_SINCE: Version = PRIMARY_SELECTION_SINCE;
type Device = ZwlrDataControlDeviceV1;
type OfferId = ZwlrDataControlOfferV1Id;
type Offer = ZwlrDataControlOfferV1;
type SourceId = ZwlrDataControlSourceV1Id;
type Source = ZwlrDataControlSourceV1;
fn create_offer(id: Self::OfferId, data: DataControlOfferData<Self>) -> Rc<Self::Offer> {
let rc = Rc::new(ZwlrDataControlOfferV1 {
id,
data,
tracker: Default::default(),
});
track!(rc.data.client, rc);
rc
}
}
impl DataControlDevice for ZwlrDataControlDeviceV1 {
type Ipc = WlrDataControlIpc;
fn data(&self) -> &DataControlDeviceData<Self::Ipc> {
&self.data
}
fn send_data_offer(&self, offer: &Rc<<Self::Ipc as DataControlIpc>::Offer>) {
self.send_data_offer(offer)
}
fn send_selection(&self, offer: Option<&Rc<<Self::Ipc as DataControlIpc>::Offer>>) {
self.send_selection(offer)
}
fn send_primary_selection(&self, offer: Option<&Rc<<Self::Ipc as DataControlIpc>::Offer>>) {
self.send_primary_selection(offer)
}
}
object_base! {
self = ZwlrDataControlDeviceV1;
version = self.data.version;
}
impl Object for ZwlrDataControlDeviceV1 {
fn break_loops(&self) {
logic::data_device_break_loops(self);
}
}
simple_add_obj!(ZwlrDataControlDeviceV1);
#[derive(Debug, Error)]
pub enum ZwlrDataControlDeviceV1Error {
#[error(transparent)]
DataControlError(#[from] DataControlError),
}

View file

@ -3,8 +3,11 @@ use {
client::{Client, ClientCaps, ClientError, CAP_DATA_CONTROL_MANAGER},
globals::{Global, GlobalName},
ifs::ipc::{
zwlr_data_control_device_v1::{ZwlrDataControlDeviceV1, PRIMARY_SELECTION_SINCE},
zwlr_data_control_source_v1::ZwlrDataControlSourceV1,
data_control::{
zwlr_data_control_device_v1::ZwlrDataControlDeviceV1,
zwlr_data_control_source_v1::ZwlrDataControlSourceV1, DynDataControlDevice,
},
IpcLocation,
},
leaks::Tracker,
object::{Object, Version},
@ -75,18 +78,14 @@ impl ZwlrDataControlManagerV1RequestHandler for ZwlrDataControlManagerV1 {
&seat.global,
));
track!(self.client, dev);
seat.global.add_wlr_device(&dev);
seat.global.add_data_control_device(dev.clone());
self.client.add_client_obj(&dev)?;
match seat.global.get_selection() {
Some(s) => s.offer_to_wlr_device(&dev),
_ => dev.send_selection(None),
}
if self.version >= PRIMARY_SELECTION_SINCE {
match seat.global.get_primary_selection() {
Some(s) => s.offer_to_wlr_device(&dev),
_ => dev.send_primary_selection(None),
}
}
dev.clone()
.handle_new_source(IpcLocation::Clipboard, seat.global.get_selection());
dev.clone().handle_new_source(
IpcLocation::PrimarySelection,
seat.global.get_primary_selection(),
);
Ok(())
}

View file

@ -0,0 +1,76 @@
use {
crate::{
ifs::ipc::data_control::{
private::{
logic::{self, DataControlError},
DataControlOffer, DataControlOfferData,
},
zwlr_data_control_device_v1::WlrDataControlIpc,
},
leaks::Tracker,
object::Object,
wire::{zwlr_data_control_offer_v1::*, ZwlrDataControlOfferV1Id},
},
std::rc::Rc,
thiserror::Error,
};
pub struct ZwlrDataControlOfferV1 {
pub id: ZwlrDataControlOfferV1Id,
pub data: DataControlOfferData<WlrDataControlIpc>,
pub tracker: Tracker<Self>,
}
impl DataControlOffer for ZwlrDataControlOfferV1 {
type Ipc = WlrDataControlIpc;
fn data(&self) -> &DataControlOfferData<Self::Ipc> {
&self.data
}
fn send_offer(&self, mime_type: &str) {
self.send_offer(mime_type);
}
}
impl ZwlrDataControlOfferV1 {
pub fn send_offer(&self, mime_type: &str) {
self.data.client.event(Offer {
self_id: self.id,
mime_type,
})
}
}
impl ZwlrDataControlOfferV1RequestHandler for ZwlrDataControlOfferV1 {
type Error = ZwlrDataControlOfferV1Error;
fn receive(&self, req: Receive, _slf: &Rc<Self>) -> Result<(), Self::Error> {
logic::data_offer_receive(self, req.mime_type, req.fd);
Ok(())
}
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
logic::data_offer_destroy(self)?;
Ok(())
}
}
object_base! {
self = ZwlrDataControlOfferV1;
version = self.data.device.data.version;
}
impl Object for ZwlrDataControlOfferV1 {
fn break_loops(&self) {
logic::data_offer_break_loops(self);
}
}
simple_add_obj!(ZwlrDataControlOfferV1);
#[derive(Debug, Error)]
pub enum ZwlrDataControlOfferV1Error {
#[error(transparent)]
DataControlError(#[from] DataControlError),
}

View file

@ -0,0 +1,107 @@
use {
crate::{
client::Client,
ifs::ipc::{
data_control::{
private::{
logic::{self, DataControlError},
DataControlSource, DataControlSourceData,
},
zwlr_data_control_device_v1::WlrDataControlIpc,
},
IpcLocation, SourceData,
},
leaks::Tracker,
object::{Object, Version},
wire::{zwlr_data_control_source_v1::*, ZwlrDataControlSourceV1Id},
},
std::{cell::Cell, rc::Rc},
thiserror::Error,
uapi::OwnedFd,
};
pub struct ZwlrDataControlSourceV1 {
pub id: ZwlrDataControlSourceV1Id,
pub data: DataControlSourceData,
pub tracker: Tracker<Self>,
}
impl DataControlSource for ZwlrDataControlSourceV1 {
type Ipc = WlrDataControlIpc;
fn data(&self) -> &DataControlSourceData {
&self.data
}
fn send_cancelled(&self) {
self.send_cancelled();
}
fn send_send(&self, mime_type: &str, fd: Rc<OwnedFd>) {
self.send_send(mime_type, fd);
}
}
impl ZwlrDataControlSourceV1 {
pub fn new(id: ZwlrDataControlSourceV1Id, client: &Rc<Client>, version: Version) -> Self {
Self {
id,
data: DataControlSourceData {
data: SourceData::new(client),
version,
location: Cell::new(IpcLocation::Clipboard),
used: Cell::new(false),
},
tracker: Default::default(),
}
}
pub fn send_send(&self, mime_type: &str, fd: Rc<OwnedFd>) {
self.data.data.client.event(Send {
self_id: self.id,
mime_type,
fd,
})
}
pub fn send_cancelled(&self) {
self.data.data.client.event(Cancelled { self_id: self.id })
}
}
impl ZwlrDataControlSourceV1RequestHandler for ZwlrDataControlSourceV1 {
type Error = ZwlrDataControlSourceV1Error;
fn offer(&self, req: Offer, _slf: &Rc<Self>) -> Result<(), Self::Error> {
logic::data_source_offer(self, req.mime_type)?;
Ok(())
}
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
logic::data_source_destroy(self)?;
Ok(())
}
}
object_base! {
self = ZwlrDataControlSourceV1;
version = self.data.version;
}
impl Object for ZwlrDataControlSourceV1 {
fn break_loops(&self) {
logic::data_source_break_loops(self);
}
}
dedicated_add_obj!(
ZwlrDataControlSourceV1,
ZwlrDataControlSourceV1Id,
zwlr_data_sources
);
#[derive(Debug, Error)]
pub enum ZwlrDataControlSourceV1Error {
#[error(transparent)]
DataControlError(#[from] DataControlError),
}

View file

@ -173,14 +173,6 @@ impl IpcVtable for ClipboardIpc {
dd.seat.clone()
}
fn set_seat_selection(
seat: &Rc<WlSeatGlobal>,
source: &Rc<Self::Source>,
serial: Option<u64>,
) -> Result<(), WlSeatError> {
seat.set_wl_data_source_selection(Some(source.clone()), serial)
}
fn create_offer(
device: &Rc<WlDataDevice>,
offer_data: OfferData<Self::Device>,

View file

@ -4,12 +4,10 @@ use {
ifs::{
ipc::{
add_data_source_mime_type, break_source_loops, cancel_offers, destroy_data_source,
detach_seat, offer_source_to_regular_client, offer_source_to_wlr_device,
offer_source_to_x,
detach_seat, offer_source_to_x,
wl_data_device::ClipboardIpc,
wl_data_device_manager::{DND_ALL, DND_NONE},
x_data_device::{XClipboardIpc, XIpcDevice},
zwlr_data_control_device_v1::{WlrClipboardIpc, ZwlrDataControlDeviceV1},
DataSource, DynDataOffer, DynDataSource, SharedState, SourceData,
OFFER_STATE_ACCEPTED, OFFER_STATE_DROPPED, SOURCE_STATE_CANCELLED,
SOURCE_STATE_DROPPED,
@ -55,16 +53,8 @@ impl DynDataSource for WlDataSource {
WlDataSource::send_send(self, mime_type, fd);
}
fn offer_to_regular_client(self: Rc<Self>, client: &Rc<Client>) {
offer_source_to_regular_client::<ClipboardIpc, Self>(&self, client);
}
fn offer_to_x(self: Rc<Self>, dd: &Rc<XIpcDevice>) {
offer_source_to_x::<XClipboardIpc, Self>(&self, dd);
}
fn offer_to_wlr_device(self: Rc<Self>, dd: &Rc<ZwlrDataControlDeviceV1>) {
offer_source_to_wlr_device::<WlrClipboardIpc, Self>(&self, dd)
offer_source_to_x::<XClipboardIpc>(self, dd);
}
fn detach_seat(&self, seat: &Rc<WlSeatGlobal>) {

View file

@ -6,7 +6,7 @@ use {
x_data_offer::XDataOffer, x_data_source::XDataSource, DeviceData, IpcLocation,
IpcVtable, OfferData, Role,
},
wl_seat::{WlSeatError, WlSeatGlobal},
wl_seat::WlSeatGlobal,
},
state::State,
xwayland::XWaylandEvent,
@ -77,17 +77,6 @@ impl<T: XIpc> IpcVtable for T {
dd.seat.clone()
}
fn set_seat_selection(
seat: &Rc<WlSeatGlobal>,
source: &Rc<Self::Source>,
_serial: Option<u64>,
) -> 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<Self::Device>,
data: OfferData<Self::Device>,

View file

@ -1,17 +1,9 @@
use {
crate::{
client::Client,
ifs::{
ipc::{
cancel_offers, detach_seat, offer_source_to_regular_client,
offer_source_to_wlr_device,
wl_data_device::ClipboardIpc,
x_data_device::XIpcDevice,
zwlr_data_control_device_v1::{
WlrClipboardIpc, WlrPrimarySelectionIpc, ZwlrDataControlDeviceV1,
},
zwp_primary_selection_device_v1::PrimarySelectionIpc,
DataSource, DynDataSource, IpcLocation, SourceData,
cancel_offers, detach_seat, x_data_device::XIpcDevice, DataSource, DynDataSource,
IpcLocation, SourceData,
},
wl_seat::WlSeatGlobal,
},
@ -54,17 +46,6 @@ impl DynDataSource for XDataSource {
});
}
fn offer_to_regular_client(self: Rc<Self>, client: &Rc<Client>) {
match self.location {
IpcLocation::Clipboard => {
offer_source_to_regular_client::<ClipboardIpc, Self>(&self, client)
}
IpcLocation::PrimarySelection => {
offer_source_to_regular_client::<PrimarySelectionIpc, Self>(&self, client)
}
}
}
fn offer_to_x(self: Rc<Self>, _dd: &Rc<XIpcDevice>) {
self.cancel_unprivileged_offers();
self.state.xwayland.queue.push(IpcSetSelection {
@ -74,17 +55,6 @@ impl DynDataSource for XDataSource {
});
}
fn offer_to_wlr_device(self: Rc<Self>, dd: &Rc<ZwlrDataControlDeviceV1>) {
match self.location {
IpcLocation::Clipboard => {
offer_source_to_wlr_device::<WlrClipboardIpc, Self>(&self, dd)
}
IpcLocation::PrimarySelection => {
offer_source_to_wlr_device::<WlrPrimarySelectionIpc, Self>(&self, dd)
}
}
}
fn detach_seat(&self, seat: &Rc<WlSeatGlobal>) {
detach_seat(self, seat);
}

View file

@ -1,302 +0,0 @@
use {
crate::{
client::{Client, ClientError},
ifs::{
ipc::{
break_device_loops, destroy_data_device,
zwlr_data_control_device_v1::private::{
WlrClipboardIpcCore, WlrIpcImpl, WlrPrimarySelectionIpcCore,
},
zwlr_data_control_offer_v1::ZwlrDataControlOfferV1,
zwlr_data_control_source_v1::ZwlrDataControlSourceV1,
DeviceData, IpcLocation, IpcVtable, OfferData, Role, WlrIpcVtable,
},
wl_seat::{WlSeatError, WlSeatGlobal},
},
leaks::Tracker,
object::{Object, Version},
wire::{
zwlr_data_control_device_v1::*, ZwlrDataControlDeviceV1Id, ZwlrDataControlOfferV1Id,
ZwlrDataControlSourceV1Id,
},
},
std::rc::Rc,
thiserror::Error,
};
pub const PRIMARY_SELECTION_SINCE: Version = Version(2);
pub struct ZwlrDataControlDeviceV1 {
pub id: ZwlrDataControlDeviceV1Id,
pub client: Rc<Client>,
pub version: Version,
pub seat: Rc<WlSeatGlobal>,
pub clipboard_data: DeviceData<ZwlrDataControlOfferV1>,
pub primary_selection_data: DeviceData<ZwlrDataControlOfferV1>,
pub tracker: Tracker<Self>,
}
impl ZwlrDataControlDeviceV1 {
pub fn new(
id: ZwlrDataControlDeviceV1Id,
client: &Rc<Client>,
version: Version,
seat: &Rc<WlSeatGlobal>,
) -> 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<ZwlrDataControlOfferV1>) {
self.client.event(DataOffer {
self_id: self.id,
id: offer.id,
})
}
pub fn send_selection(&self, offer: Option<&Rc<ZwlrDataControlOfferV1>>) {
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<ZwlrDataControlOfferV1>>) {
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<Option<Rc<ZwlrDataControlSourceV1>>, ZwlrDataControlDeviceV1Error> {
if source.is_none() {
Ok(None)
} else {
let src = self.client.lookup(source)?;
if src.used.replace(true) {
return Err(ZwlrDataControlDeviceV1Error::AlreadyUsed);
}
src.location.set(location);
Ok(Some(src))
}
}
}
impl ZwlrDataControlDeviceV1RequestHandler for ZwlrDataControlDeviceV1 {
type Error = ZwlrDataControlDeviceV1Error;
fn set_selection(&self, req: SetSelection, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let src = self.use_source(req.source, IpcLocation::Clipboard)?;
self.seat.set_selection(src)?;
Ok(())
}
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
destroy_data_device::<WlrClipboardIpc>(self);
destroy_data_device::<WlrPrimarySelectionIpc>(self);
self.seat.remove_wlr_device(self);
self.client.remove_obj(self)?;
Ok(())
}
fn set_primary_selection(
&self,
req: SetPrimarySelection,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
let src = self.use_source(req.source, IpcLocation::PrimarySelection)?;
self.seat.set_primary_selection(src)?;
Ok(())
}
}
mod private {
use std::marker::PhantomData;
pub struct WlrClipboardIpcCore;
pub struct WlrPrimarySelectionIpcCore;
pub struct WlrIpcImpl<T>(PhantomData<T>);
}
pub type WlrClipboardIpc = WlrIpcImpl<WlrClipboardIpcCore>;
pub type WlrPrimarySelectionIpc = WlrIpcImpl<WlrPrimarySelectionIpcCore>;
trait WlrIpc {
const MIN_VERSION: Version;
const LOCATION: IpcLocation;
fn wlr_get_device_data(dd: &ZwlrDataControlDeviceV1) -> &DeviceData<ZwlrDataControlOfferV1>;
fn wlr_set_seat_selection(
seat: &Rc<WlSeatGlobal>,
source: &Rc<ZwlrDataControlSourceV1>,
) -> Result<(), WlSeatError>;
fn wlr_send_selection(dd: &ZwlrDataControlDeviceV1, offer: Option<&Rc<ZwlrDataControlOfferV1>>);
fn wlr_unset(seat: &Rc<WlSeatGlobal>);
}
impl WlrIpc for WlrClipboardIpcCore {
const MIN_VERSION: Version = Version::ALL;
const LOCATION: IpcLocation = IpcLocation::Clipboard;
fn wlr_get_device_data(dd: &ZwlrDataControlDeviceV1) -> &DeviceData<ZwlrDataControlOfferV1> {
&dd.clipboard_data
}
fn wlr_set_seat_selection(
seat: &Rc<WlSeatGlobal>,
source: &Rc<ZwlrDataControlSourceV1>,
) -> Result<(), WlSeatError> {
seat.set_selection(Some(source.clone()))
}
fn wlr_send_selection(
dd: &ZwlrDataControlDeviceV1,
offer: Option<&Rc<ZwlrDataControlOfferV1>>,
) {
dd.send_selection(offer)
}
fn wlr_unset(seat: &Rc<WlSeatGlobal>) {
seat.unset_selection()
}
}
impl WlrIpc for WlrPrimarySelectionIpcCore {
const MIN_VERSION: Version = PRIMARY_SELECTION_SINCE;
const LOCATION: IpcLocation = IpcLocation::PrimarySelection;
fn wlr_get_device_data(dd: &ZwlrDataControlDeviceV1) -> &DeviceData<ZwlrDataControlOfferV1> {
&dd.primary_selection_data
}
fn wlr_set_seat_selection(
seat: &Rc<WlSeatGlobal>,
source: &Rc<ZwlrDataControlSourceV1>,
) -> Result<(), WlSeatError> {
seat.set_primary_selection(Some(source.clone()))
}
fn wlr_send_selection(
dd: &ZwlrDataControlDeviceV1,
offer: Option<&Rc<ZwlrDataControlOfferV1>>,
) {
dd.send_primary_selection(offer)
}
fn wlr_unset(seat: &Rc<WlSeatGlobal>) {
seat.unset_primary_selection()
}
}
impl<T: WlrIpc> WlrIpcVtable for WlrIpcImpl<T> {
fn for_each_device<C>(seat: &WlSeatGlobal, f: C)
where
C: FnMut(&Rc<Self::Device>),
{
seat.for_each_wlr_data_device(T::MIN_VERSION, f)
}
}
impl<T: WlrIpc> IpcVtable for WlrIpcImpl<T> {
type Device = ZwlrDataControlDeviceV1;
type Source = ZwlrDataControlSourceV1;
type Offer = ZwlrDataControlOfferV1;
fn get_device_data(dd: &Self::Device) -> &DeviceData<Self::Offer> {
T::wlr_get_device_data(dd)
}
fn get_device_seat(dd: &Self::Device) -> Rc<WlSeatGlobal> {
dd.seat.clone()
}
fn set_seat_selection(
seat: &Rc<WlSeatGlobal>,
source: &Rc<Self::Source>,
serial: Option<u64>,
) -> Result<(), WlSeatError> {
debug_assert!(serial.is_none());
let _ = serial;
T::wlr_set_seat_selection(seat, source)
}
fn create_offer(
device: &Rc<ZwlrDataControlDeviceV1>,
offer_data: OfferData<Self::Device>,
) -> Result<Rc<Self::Offer>, 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<Self::Offer>>) {
T::wlr_send_selection(dd, offer)
}
fn send_offer(dd: &Self::Device, offer: &Rc<Self::Offer>) {
dd.send_data_offer(offer);
}
fn unset(seat: &Rc<WlSeatGlobal>, _role: Role) {
T::wlr_unset(seat)
}
fn device_client(dd: &Rc<Self::Device>) -> &Rc<Client> {
&dd.client
}
}
object_base! {
self = ZwlrDataControlDeviceV1;
version = self.version;
}
impl Object for ZwlrDataControlDeviceV1 {
fn break_loops(&self) {
break_device_loops::<WlrClipboardIpc>(self);
break_device_loops::<WlrPrimarySelectionIpc>(self);
self.seat.remove_wlr_device(self);
}
}
simple_add_obj!(ZwlrDataControlDeviceV1);
#[derive(Debug, Error)]
pub enum ZwlrDataControlDeviceV1Error {
#[error(transparent)]
ClientError(Box<ClientError>),
#[error(transparent)]
WlSeatError(Box<WlSeatError>),
#[error("The source has already been used")]
AlreadyUsed,
}
efrom!(ZwlrDataControlDeviceV1Error, ClientError);
efrom!(ZwlrDataControlDeviceV1Error, WlSeatError);

View file

@ -1,124 +0,0 @@
use {
crate::{
client::{Client, ClientError, ClientId},
ifs::{
ipc::{
break_offer_loops, cancel_offer, destroy_data_offer, receive_data_offer,
zwlr_data_control_device_v1::{
WlrClipboardIpc, WlrPrimarySelectionIpc, ZwlrDataControlDeviceV1,
},
DataOffer, DataOfferId, DynDataOffer, IpcLocation, OfferData,
},
wl_seat::WlSeatGlobal,
},
leaks::Tracker,
object::Object,
wire::{zwlr_data_control_offer_v1::*, ZwlrDataControlOfferV1Id},
},
std::rc::Rc,
thiserror::Error,
};
pub struct ZwlrDataControlOfferV1 {
pub id: ZwlrDataControlOfferV1Id,
pub offer_id: DataOfferId,
pub client: Rc<Client>,
pub device: Rc<ZwlrDataControlDeviceV1>,
pub data: OfferData<ZwlrDataControlDeviceV1>,
pub location: IpcLocation,
pub tracker: Tracker<Self>,
}
impl DataOffer for ZwlrDataControlOfferV1 {
type Device = ZwlrDataControlDeviceV1;
fn offer_data(&self) -> &OfferData<ZwlrDataControlDeviceV1> {
&self.data
}
}
impl DynDataOffer for ZwlrDataControlOfferV1 {
fn offer_id(&self) -> DataOfferId {
self.offer_id
}
fn client_id(&self) -> ClientId {
self.client.id
}
fn send_offer(&self, mime_type: &str) {
ZwlrDataControlOfferV1::send_offer(self, mime_type)
}
fn cancel(&self) {
match self.location {
IpcLocation::Clipboard => cancel_offer::<WlrClipboardIpc>(self),
IpcLocation::PrimarySelection => cancel_offer::<WlrPrimarySelectionIpc>(self),
}
}
fn get_seat(&self) -> Rc<WlSeatGlobal> {
self.device.seat.clone()
}
fn is_privileged(&self) -> bool {
true
}
}
impl ZwlrDataControlOfferV1 {
pub fn send_offer(&self, mime_type: &str) {
self.client.event(Offer {
self_id: self.id,
mime_type,
})
}
}
impl ZwlrDataControlOfferV1RequestHandler for ZwlrDataControlOfferV1 {
type Error = ZwlrDataControlOfferV1Error;
fn receive(&self, req: Receive, _slf: &Rc<Self>) -> Result<(), Self::Error> {
match self.location {
IpcLocation::Clipboard => {
receive_data_offer::<WlrClipboardIpc>(self, req.mime_type, req.fd)
}
IpcLocation::PrimarySelection => {
receive_data_offer::<WlrPrimarySelectionIpc>(self, req.mime_type, req.fd)
}
}
Ok(())
}
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
match self.location {
IpcLocation::Clipboard => destroy_data_offer::<WlrClipboardIpc>(self),
IpcLocation::PrimarySelection => destroy_data_offer::<WlrPrimarySelectionIpc>(self),
}
self.client.remove_obj(self)?;
Ok(())
}
}
object_base! {
self = ZwlrDataControlOfferV1;
version = self.device.version;
}
impl Object for ZwlrDataControlOfferV1 {
fn break_loops(&self) {
match self.location {
IpcLocation::Clipboard => break_offer_loops::<WlrClipboardIpc>(self),
IpcLocation::PrimarySelection => break_offer_loops::<WlrPrimarySelectionIpc>(self),
}
}
}
simple_add_obj!(ZwlrDataControlOfferV1);
#[derive(Debug, Error)]
pub enum ZwlrDataControlOfferV1Error {
#[error(transparent)]
ClientError(Box<ClientError>),
}
efrom!(ZwlrDataControlOfferV1Error, ClientError);

View file

@ -1,165 +0,0 @@
use {
crate::{
client::{Client, ClientError},
ifs::{
ipc::{
add_data_source_mime_type, break_source_loops, cancel_offers, destroy_data_source,
detach_seat, offer_source_to_regular_client, offer_source_to_wlr_device,
offer_source_to_x,
wl_data_device::ClipboardIpc,
x_data_device::{XClipboardIpc, XIpcDevice, XPrimarySelectionIpc},
zwlr_data_control_device_v1::{
WlrClipboardIpc, WlrPrimarySelectionIpc, ZwlrDataControlDeviceV1,
},
zwp_primary_selection_device_v1::PrimarySelectionIpc,
DataSource, DynDataSource, IpcLocation, SourceData,
},
wl_seat::WlSeatGlobal,
},
leaks::Tracker,
object::{Object, Version},
wire::{zwlr_data_control_source_v1::*, ZwlrDataControlSourceV1Id},
},
std::{cell::Cell, rc::Rc},
thiserror::Error,
uapi::OwnedFd,
};
pub struct ZwlrDataControlSourceV1 {
pub id: ZwlrDataControlSourceV1Id,
pub data: SourceData,
pub version: Version,
pub location: Cell<IpcLocation>,
pub used: Cell<bool>,
pub tracker: Tracker<Self>,
}
impl DataSource for ZwlrDataControlSourceV1 {
fn send_cancelled(&self, _seat: &Rc<WlSeatGlobal>) {
ZwlrDataControlSourceV1::send_cancelled(self);
}
}
impl DynDataSource for ZwlrDataControlSourceV1 {
fn source_data(&self) -> &SourceData {
&self.data
}
fn send_send(&self, mime_type: &str, fd: Rc<OwnedFd>) {
ZwlrDataControlSourceV1::send_send(&self, mime_type, fd);
}
fn offer_to_regular_client(self: Rc<Self>, client: &Rc<Client>) {
match self.location.get() {
IpcLocation::Clipboard => {
offer_source_to_regular_client::<ClipboardIpc, Self>(&self, client)
}
IpcLocation::PrimarySelection => {
offer_source_to_regular_client::<PrimarySelectionIpc, Self>(&self, client)
}
}
}
fn offer_to_x(self: Rc<Self>, dd: &Rc<XIpcDevice>) {
match self.location.get() {
IpcLocation::Clipboard => offer_source_to_x::<XClipboardIpc, Self>(&self, dd),
IpcLocation::PrimarySelection => {
offer_source_to_x::<XPrimarySelectionIpc, Self>(&self, dd)
}
}
}
fn offer_to_wlr_device(self: Rc<Self>, dd: &Rc<ZwlrDataControlDeviceV1>) {
match self.location.get() {
IpcLocation::Clipboard => {
offer_source_to_wlr_device::<WlrClipboardIpc, Self>(&self, dd)
}
IpcLocation::PrimarySelection => {
offer_source_to_wlr_device::<WlrPrimarySelectionIpc, Self>(&self, dd)
}
}
}
fn detach_seat(&self, seat: &Rc<WlSeatGlobal>) {
detach_seat(self, seat)
}
fn cancel_unprivileged_offers(&self) {
cancel_offers(self, false)
}
}
impl ZwlrDataControlSourceV1 {
pub fn new(id: ZwlrDataControlSourceV1Id, client: &Rc<Client>, version: Version) -> Self {
Self {
id,
tracker: Default::default(),
data: SourceData::new(client),
version,
location: Cell::new(IpcLocation::Clipboard),
used: Cell::new(false),
}
}
pub fn send_send(&self, mime_type: &str, fd: Rc<OwnedFd>) {
self.data.client.event(Send {
self_id: self.id,
mime_type,
fd,
})
}
pub fn send_cancelled(&self) {
self.data.client.event(Cancelled { self_id: self.id })
}
}
impl ZwlrDataControlSourceV1RequestHandler for ZwlrDataControlSourceV1 {
type Error = ZwlrDataControlSourceV1Error;
fn offer(&self, req: Offer, _slf: &Rc<Self>) -> Result<(), Self::Error> {
if self.used.get() {
return Err(ZwlrDataControlSourceV1Error::AlreadyUsed);
}
add_data_source_mime_type::<WlrClipboardIpc>(self, req.mime_type);
Ok(())
}
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
match self.location.get() {
IpcLocation::Clipboard => destroy_data_source::<WlrClipboardIpc>(self),
IpcLocation::PrimarySelection => destroy_data_source::<WlrPrimarySelectionIpc>(self),
}
self.data.client.remove_obj(self)?;
Ok(())
}
}
object_base! {
self = ZwlrDataControlSourceV1;
version = self.version;
}
impl Object for ZwlrDataControlSourceV1 {
fn break_loops(&self) {
match self.location.get() {
IpcLocation::Clipboard => break_source_loops::<WlrClipboardIpc>(self),
IpcLocation::PrimarySelection => break_source_loops::<WlrPrimarySelectionIpc>(self),
}
}
}
dedicated_add_obj!(
ZwlrDataControlSourceV1,
ZwlrDataControlSourceV1Id,
zwlr_data_sources
);
#[derive(Debug, Error)]
pub enum ZwlrDataControlSourceV1Error {
#[error(transparent)]
ClientError(Box<ClientError>),
#[error("The source has already been used")]
AlreadyUsed,
}
efrom!(ZwlrDataControlSourceV1Error, ClientError);

View file

@ -121,14 +121,6 @@ impl IpcVtable for PrimarySelectionIpc {
dd.seat.clone()
}
fn set_seat_selection(
seat: &Rc<WlSeatGlobal>,
source: &Rc<Self::Source>,
serial: Option<u64>,
) -> Result<(), WlSeatError> {
seat.set_zwp_primary_selection(Some(source.clone()), serial)
}
fn create_offer(
device: &Rc<ZwpPrimarySelectionDeviceV1>,
offer_data: OfferData<Self::Device>,

View file

@ -4,10 +4,8 @@ use {
ifs::{
ipc::{
add_data_source_mime_type, break_source_loops, cancel_offers, destroy_data_source,
detach_seat, offer_source_to_regular_client, offer_source_to_wlr_device,
offer_source_to_x,
detach_seat, offer_source_to_x,
x_data_device::{XIpcDevice, XPrimarySelectionIpc},
zwlr_data_control_device_v1::{WlrPrimarySelectionIpc, ZwlrDataControlDeviceV1},
zwp_primary_selection_device_v1::PrimarySelectionIpc,
DataSource, DynDataSource, SourceData,
},
@ -44,16 +42,8 @@ impl DynDataSource for ZwpPrimarySelectionSourceV1 {
ZwpPrimarySelectionSourceV1::send_send(self, mime_type, fd)
}
fn offer_to_regular_client(self: Rc<Self>, client: &Rc<Client>) {
offer_source_to_regular_client::<PrimarySelectionIpc, Self>(&self, client);
}
fn offer_to_x(self: Rc<Self>, dd: &Rc<XIpcDevice>) {
offer_source_to_x::<XPrimarySelectionIpc, Self>(&self, dd);
}
fn offer_to_wlr_device(self: Rc<Self>, dd: &Rc<ZwlrDataControlDeviceV1>) {
offer_source_to_wlr_device::<WlrPrimarySelectionIpc, Self>(&self, dd)
offer_source_to_x::<XPrimarySelectionIpc>(self, dd);
}
fn detach_seat(&self, seat: &Rc<WlSeatGlobal>) {

View file

@ -33,17 +33,16 @@ use {
ext_idle_notification_v1::ExtIdleNotificationV1,
ipc::{
self,
data_control::{DataControlDeviceId, DynDataControlDevice},
offer_source_to_regular_client,
wl_data_device::{ClipboardIpc, WlDataDevice},
wl_data_source::WlDataSource,
x_data_device::{XClipboardIpc, XIpcDevice, XIpcDeviceId, XPrimarySelectionIpc},
zwlr_data_control_device_v1::{
WlrClipboardIpc, WlrPrimarySelectionIpc, ZwlrDataControlDeviceV1,
},
zwp_primary_selection_device_v1::{
PrimarySelectionIpc, ZwpPrimarySelectionDeviceV1,
},
zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1,
DynDataSource, IpcError,
DynDataSource, IpcError, IpcLocation,
},
wl_output::WlOutputGlobal,
wl_seat::{
@ -88,8 +87,8 @@ use {
},
wire::{
wl_seat::*, ExtIdleNotificationV1Id, WlDataDeviceId, WlKeyboardId, WlPointerId,
WlSeatId, WlTouchId, XdgPopupId, ZwlrDataControlDeviceV1Id,
ZwpPrimarySelectionDeviceV1Id, ZwpRelativePointerV1Id, ZwpTextInputV3Id,
WlSeatId, WlTouchId, XdgPopupId, ZwpPrimarySelectionDeviceV1Id, ZwpRelativePointerV1Id,
ZwpTextInputV3Id,
},
wire_ei::EiSeatId,
xkbcommon::{DynKeyboardState, KeyboardState, KeymapId, XkbKeymap, XkbState},
@ -167,8 +166,7 @@ pub struct WlSeatGlobal {
AHashMap<ZwpPrimarySelectionDeviceV1Id, Rc<ZwpPrimarySelectionDeviceV1>>,
>,
>,
wlr_data_devices:
CopyHashMap<(ClientId, ZwlrDataControlDeviceV1Id), Rc<ZwlrDataControlDeviceV1>>,
data_control_devices: CopyHashMap<DataControlDeviceId, Rc<dyn DynDataControlDevice>>,
repeat_rate: Cell<(i32, i32)>,
seat_kb_map: CloneCell<Rc<XkbKeymap>>,
seat_xkb_state: CloneCell<Rc<RefCell<XkbState>>>,
@ -267,7 +265,7 @@ impl WlSeatGlobal {
constraint: Default::default(),
idle_notifications: Default::default(),
last_input_usec: Cell::new(state.now_usec()),
wlr_data_devices: Default::default(),
data_control_devices: Default::default(),
text_inputs: Default::default(),
text_input: Default::default(),
input_method: Default::default(),
@ -383,13 +381,12 @@ impl WlSeatGlobal {
}
}
pub fn add_wlr_device(&self, device: &Rc<ZwlrDataControlDeviceV1>) {
self.wlr_data_devices
.set((device.client.id, device.id), device.clone());
pub fn add_data_control_device(&self, device: Rc<dyn DynDataControlDevice>) {
self.data_control_devices.set(device.id(), device.clone());
}
pub fn remove_wlr_device(&self, device: &ZwlrDataControlDeviceV1) {
self.wlr_data_devices.remove(&(device.client.id, device.id));
pub fn remove_data_control_device(&self, device: &dyn DynDataControlDevice) {
self.data_control_devices.remove(&device.id());
}
pub fn get_output(&self) -> Rc<OutputNode> {
@ -711,15 +708,15 @@ impl WlSeatGlobal {
}
}
fn set_selection_<T, X, W, S>(
fn set_selection_<T, X, S>(
self: &Rc<Self>,
field: &CloneCell<Option<Rc<dyn DynDataSource>>>,
src: Option<Rc<S>>,
location: IpcLocation,
) -> Result<(), WlSeatError>
where
T: ipc::IterableIpcVtable,
X: ipc::IpcVtable<Device = XIpcDevice>,
W: ipc::WlrIpcVtable,
S: DynDataSource,
{
if let (Some(new), Some(old)) = (&src, &field.get()) {
@ -738,10 +735,10 @@ impl WlSeatGlobal {
self.offer_selection_to_client::<T, X>(src.clone().map(|v| v as Rc<_>), &client);
// client.flush();
}
W::for_each_device(self, |device| match &src {
Some(src) => src.clone().offer_to_wlr_device(device),
_ => W::send_selection(device, None),
});
let dyn_source = src.map(|s| s as Rc<dyn DynDataSource>);
for dd in self.data_control_devices.lock().values() {
dd.clone().handle_new_source(location, dyn_source.clone());
}
Ok(())
}
@ -763,7 +760,7 @@ impl WlSeatGlobal {
});
} else {
match selection {
Some(src) => src.offer_to_regular_client(client),
Some(src) => offer_source_to_regular_client::<T>(src, client),
_ => T::for_each_device(self, client.id, |device| {
T::send_selection(device, None);
}),
@ -825,9 +822,10 @@ impl WlSeatGlobal {
self: &Rc<Self>,
selection: Option<Rc<S>>,
) -> Result<(), WlSeatError> {
self.set_selection_::<ClipboardIpc, XClipboardIpc, WlrClipboardIpc, _>(
self.set_selection_::<ClipboardIpc, XClipboardIpc, _>(
&self.selection,
selection,
IpcLocation::Clipboard,
)
}
@ -871,9 +869,10 @@ impl WlSeatGlobal {
self: &Rc<Self>,
selection: Option<Rc<S>>,
) -> Result<(), WlSeatError> {
self.set_selection_::<PrimarySelectionIpc, XPrimarySelectionIpc, WlrPrimarySelectionIpc, _>(
self.set_selection_::<PrimarySelectionIpc, XPrimarySelectionIpc, _>(
&self.primary_selection,
selection,
IpcLocation::PrimarySelection,
)
}
@ -910,7 +909,7 @@ impl WlSeatGlobal {
self.bindings.borrow_mut().clear();
self.data_devices.borrow_mut().clear();
self.primary_selection_devices.borrow_mut().clear();
self.wlr_data_devices.clear();
self.data_control_devices.clear();
self.cursor_user_group.detach();
self.selection.set(None);
self.primary_selection.set(None);

View file

@ -9,13 +9,12 @@ use {
fixed::Fixed,
ifs::{
ipc::{
offer_source_to_regular_client,
wl_data_device::{ClipboardIpc, WlDataDevice},
x_data_device::{XClipboardIpc, XPrimarySelectionIpc},
zwlr_data_control_device_v1::ZwlrDataControlDeviceV1,
zwp_primary_selection_device_v1::{
PrimarySelectionIpc, ZwpPrimarySelectionDeviceV1,
},
DynDataSource,
},
wl_seat::{
tablet::{TabletPad, TabletPadId, TabletTool, TabletToolId},
@ -1037,17 +1036,6 @@ impl WlSeatGlobal {
}
}
pub fn for_each_wlr_data_device<C>(&self, ver: Version, mut f: C)
where
C: FnMut(&Rc<ZwlrDataControlDeviceV1>),
{
for dd in self.wlr_data_devices.lock().values() {
if dd.version >= ver {
f(dd);
}
}
}
fn surface_pointer_frame(&self, surface: &WlSurface) {
self.surface_pointer_event(POINTER_FRAME_SINCE_VERSION, surface, |p| p.send_frame());
}
@ -1445,7 +1433,7 @@ impl WlSeatGlobal {
) {
if let Some(src) = &dnd.src {
if !surface.client.is_xwayland {
src.clone().offer_to_regular_client(&surface.client);
offer_source_to_regular_client::<ClipboardIpc>(src.clone(), &surface.client);
}
src.for_each_data_offer(|offer| {
offer.send_enter(surface.id, x, y, serial);

View file

@ -35,7 +35,10 @@ use {
ifs::{
ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1,
ext_session_lock_v1::ExtSessionLockV1,
ipc::{x_data_device::XIpcDeviceIds, DataOfferIds, DataSourceIds},
ipc::{
data_control::DataControlDeviceIds, x_data_device::XIpcDeviceIds, DataOfferIds,
DataSourceIds,
},
jay_render_ctx::JayRenderCtx,
jay_screencast::JayScreencast,
jay_seat_events::JaySeatEvents,
@ -224,6 +227,7 @@ pub struct State {
pub toplevels: CopyHashMap<ToplevelIdentifier, Weak<dyn ToplevelNode>>,
pub const_40hz_latch: EventSource<dyn LatchListener>,
pub tray_item_ids: TrayItemIds,
pub data_control_device_ids: DataControlDeviceIds,
}
// impl Drop for State {

View file

@ -1749,7 +1749,13 @@ impl Wm {
for target in &targets {
add_data_source_mime_type::<T>(&source, target);
}
if let Err(e) = T::set_seat_selection(&seat, &source, None) {
let res = match source.location {
IpcLocation::Clipboard => seat.set_selection(Some(source.clone())),
IpcLocation::PrimarySelection => {
seat.set_primary_selection(Some(source.clone()))
}
};
if let Err(e) = res {
log::error!("Could not set selection: {}", ErrorFmt(e));
return Ok(());
}

View file

@ -0,0 +1,30 @@
# requests
request set_selection {
source: id(ext_data_control_source_v1),
}
request destroy {
}
request set_primary_selection {
source: id(ext_data_control_source_v1),
}
# events
event data_offer {
id: id(ext_data_control_offer_v1),
}
event selection {
id: id(ext_data_control_offer_v1),
}
event finished {
}
event primary_selection {
id: id(ext_data_control_offer_v1),
}

View file

@ -0,0 +1,14 @@
# requests
request create_data_source {
id: id(ext_data_control_source_v1),
}
request get_data_device {
id: id(ext_data_control_device_v1),
seat: id(wl_seat),
}
request destroy {
}

View file

@ -0,0 +1,16 @@
# requests
request receive {
mime_type: str,
fd: fd,
}
request destroy {
}
# events
event offer {
mime_type: str,
}

View file

@ -0,0 +1,20 @@
# requests
request offer {
mime_type: str,
}
request destroy {
}
# events
event send {
mime_type: str,
fd: fd,
}
event cancelled {
}