ipc: move data control logic out of wlr code
This commit is contained in:
parent
dfc0a11935
commit
be1462d0ef
5 changed files with 525 additions and 310 deletions
|
|
@ -3,6 +3,7 @@ use {
|
|||
std::rc::Rc,
|
||||
};
|
||||
|
||||
mod private;
|
||||
pub mod zwlr_data_control_device_v1;
|
||||
pub mod zwlr_data_control_manager_v1;
|
||||
pub mod zwlr_data_control_offer_v1;
|
||||
|
|
|
|||
426
src/ifs/ipc/data_control/private.rs
Normal file
426
src/ifs/ipc/data_control/private.rs
Normal 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);
|
||||
}
|
||||
|
|
@ -1,21 +1,16 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
client::Client,
|
||||
ifs::{
|
||||
ipc::{
|
||||
break_device_loops,
|
||||
data_control::{
|
||||
zwlr_data_control_device_v1::private::{
|
||||
WlrClipboardIpcCore, WlrIpcImpl, WlrPrimarySelectionIpcCore,
|
||||
},
|
||||
zwlr_data_control_offer_v1::ZwlrDataControlOfferV1,
|
||||
zwlr_data_control_source_v1::ZwlrDataControlSourceV1,
|
||||
DataControlDeviceId, DynDataControlDevice,
|
||||
ipc::data_control::{
|
||||
private::{
|
||||
logic::{self, DataControlError},
|
||||
DataControlDevice, DataControlDeviceData, DataControlIpc, DataControlOfferData,
|
||||
},
|
||||
destroy_data_device, offer_source_to_data_control_device, DeviceData,
|
||||
DynDataSource, IpcLocation, IpcVtable, OfferData, Role,
|
||||
zwlr_data_control_offer_v1::ZwlrDataControlOfferV1,
|
||||
zwlr_data_control_source_v1::ZwlrDataControlSourceV1,
|
||||
},
|
||||
wl_seat::{WlSeatError, WlSeatGlobal},
|
||||
wl_seat::WlSeatGlobal,
|
||||
},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
|
|
@ -32,12 +27,7 @@ pub const PRIMARY_SELECTION_SINCE: Version = Version(2);
|
|||
|
||||
pub struct ZwlrDataControlDeviceV1 {
|
||||
pub id: ZwlrDataControlDeviceV1Id,
|
||||
pub data_control_device_id: DataControlDeviceId,
|
||||
pub client: Rc<Client>,
|
||||
pub version: Version,
|
||||
pub seat: Rc<WlSeatGlobal>,
|
||||
pub clipboard_data: DeviceData<ZwlrDataControlOfferV1>,
|
||||
pub primary_selection_data: DeviceData<ZwlrDataControlOfferV1>,
|
||||
pub data: DataControlDeviceData<WlrDataControlIpc>,
|
||||
pub tracker: Tracker<Self>,
|
||||
}
|
||||
|
||||
|
|
@ -50,18 +40,20 @@ impl ZwlrDataControlDeviceV1 {
|
|||
) -> Self {
|
||||
Self {
|
||||
id,
|
||||
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(),
|
||||
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.client.event(DataOffer {
|
||||
self.data.client.event(DataOffer {
|
||||
self_id: self.id,
|
||||
id: offer.id,
|
||||
})
|
||||
|
|
@ -71,7 +63,7 @@ impl ZwlrDataControlDeviceV1 {
|
|||
let id = offer
|
||||
.map(|o| o.id)
|
||||
.unwrap_or(ZwlrDataControlOfferV1Id::NONE);
|
||||
self.client.event(Selection {
|
||||
self.data.client.event(Selection {
|
||||
self_id: self.id,
|
||||
id,
|
||||
})
|
||||
|
|
@ -81,44 +73,23 @@ impl ZwlrDataControlDeviceV1 {
|
|||
let id = offer
|
||||
.map(|o| o.id)
|
||||
.unwrap_or(ZwlrDataControlOfferV1Id::NONE);
|
||||
self.client.event(PrimarySelection {
|
||||
self.data.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)?;
|
||||
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> {
|
||||
destroy_data_device::<WlrClipboardIpc>(self);
|
||||
destroy_data_device::<WlrPrimarySelectionIpc>(self);
|
||||
self.seat.remove_data_control_device(self);
|
||||
self.client.remove_obj(self)?;
|
||||
logic::device_destroy(self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -127,157 +98,60 @@ impl ZwlrDataControlDeviceV1RequestHandler for ZwlrDataControlDeviceV1 {
|
|||
req: SetPrimarySelection,
|
||||
_slf: &Rc<Self>,
|
||||
) -> Result<(), Self::Error> {
|
||||
let src = self.use_source(req.source, IpcLocation::PrimarySelection)?;
|
||||
self.seat.set_primary_selection(src)?;
|
||||
logic::device_set_primary_selection(self, req.source.is_some().then_some(req.source))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
mod private {
|
||||
use std::marker::PhantomData;
|
||||
pub struct WlrDataControlIpc;
|
||||
|
||||
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 LOCATION: IpcLocation;
|
||||
|
||||
fn wlr_get_device_data(dd: &ZwlrDataControlDeviceV1) -> &DeviceData<ZwlrDataControlOfferV1>;
|
||||
|
||||
fn wlr_send_selection(dd: &ZwlrDataControlDeviceV1, offer: Option<&Rc<ZwlrDataControlOfferV1>>);
|
||||
|
||||
fn wlr_unset(seat: &Rc<WlSeatGlobal>);
|
||||
}
|
||||
|
||||
impl WlrIpc for WlrClipboardIpcCore {
|
||||
const LOCATION: IpcLocation = IpcLocation::Clipboard;
|
||||
|
||||
fn wlr_get_device_data(dd: &ZwlrDataControlDeviceV1) -> &DeviceData<ZwlrDataControlOfferV1> {
|
||||
&dd.clipboard_data
|
||||
}
|
||||
|
||||
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 LOCATION: IpcLocation = IpcLocation::PrimarySelection;
|
||||
|
||||
fn wlr_get_device_data(dd: &ZwlrDataControlDeviceV1) -> &DeviceData<ZwlrDataControlOfferV1> {
|
||||
&dd.primary_selection_data
|
||||
}
|
||||
|
||||
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> IpcVtable for WlrIpcImpl<T> {
|
||||
impl DataControlIpc for WlrDataControlIpc {
|
||||
const PRIMARY_SELECTION_SINCE: Version = PRIMARY_SELECTION_SINCE;
|
||||
type Device = ZwlrDataControlDeviceV1;
|
||||
type Source = ZwlrDataControlSourceV1;
|
||||
type OfferId = ZwlrDataControlOfferV1Id;
|
||||
type Offer = ZwlrDataControlOfferV1;
|
||||
type SourceId = ZwlrDataControlSourceV1Id;
|
||||
type Source = ZwlrDataControlSourceV1;
|
||||
|
||||
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 create_offer(
|
||||
device: &Rc<ZwlrDataControlDeviceV1>,
|
||||
offer_data: OfferData<Self::Device>,
|
||||
) -> Result<Rc<Self::Offer>, ClientError> {
|
||||
fn create_offer(id: Self::OfferId, data: DataControlOfferData<Self>) -> Rc<Self::Offer> {
|
||||
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,
|
||||
id,
|
||||
data,
|
||||
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
|
||||
track!(rc.data.client, rc);
|
||||
rc
|
||||
}
|
||||
}
|
||||
|
||||
impl DynDataControlDevice for ZwlrDataControlDeviceV1 {
|
||||
fn id(&self) -> DataControlDeviceId {
|
||||
self.data_control_device_id
|
||||
impl DataControlDevice for ZwlrDataControlDeviceV1 {
|
||||
type Ipc = WlrDataControlIpc;
|
||||
|
||||
fn data(&self) -> &DataControlDeviceData<Self::Ipc> {
|
||||
&self.data
|
||||
}
|
||||
|
||||
fn handle_new_source(
|
||||
self: Rc<Self>,
|
||||
location: IpcLocation,
|
||||
source: Option<Rc<dyn DynDataSource>>,
|
||||
) {
|
||||
match location {
|
||||
IpcLocation::Clipboard => match source {
|
||||
Some(src) => offer_source_to_data_control_device::<WlrClipboardIpc>(src, &self),
|
||||
_ => self.send_selection(None),
|
||||
},
|
||||
IpcLocation::PrimarySelection => {
|
||||
if self.version >= PRIMARY_SELECTION_SINCE {
|
||||
match source {
|
||||
Some(src) => offer_source_to_data_control_device::<WlrPrimarySelectionIpc>(
|
||||
src, &self,
|
||||
),
|
||||
_ => self.send_primary_selection(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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.version;
|
||||
version = self.data.version;
|
||||
}
|
||||
|
||||
impl Object for ZwlrDataControlDeviceV1 {
|
||||
fn break_loops(&self) {
|
||||
break_device_loops::<WlrClipboardIpc>(self);
|
||||
break_device_loops::<WlrPrimarySelectionIpc>(self);
|
||||
self.seat.remove_data_control_device(self);
|
||||
logic::data_device_break_loops(self);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -286,11 +160,5 @@ 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,
|
||||
DataControlError(#[from] DataControlError),
|
||||
}
|
||||
efrom!(ZwlrDataControlDeviceV1Error, ClientError);
|
||||
efrom!(ZwlrDataControlDeviceV1Error, WlSeatError);
|
||||
|
|
|
|||
|
|
@ -1,16 +1,11 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError, ClientId},
|
||||
ifs::{
|
||||
ipc::{
|
||||
break_offer_loops, cancel_offer,
|
||||
data_control::zwlr_data_control_device_v1::{
|
||||
WlrClipboardIpc, WlrPrimarySelectionIpc, ZwlrDataControlDeviceV1,
|
||||
},
|
||||
destroy_data_offer, receive_data_offer, DataOffer, DataOfferId, DynDataOffer,
|
||||
IpcLocation, OfferData,
|
||||
ifs::ipc::data_control::{
|
||||
private::{
|
||||
logic::{self, DataControlError},
|
||||
DataControlOffer, DataControlOfferData,
|
||||
},
|
||||
wl_seat::WlSeatGlobal,
|
||||
zwlr_data_control_device_v1::WlrDataControlIpc,
|
||||
},
|
||||
leaks::Tracker,
|
||||
object::Object,
|
||||
|
|
@ -22,54 +17,25 @@ use {
|
|||
|
||||
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 data: DataControlOfferData<WlrDataControlIpc>,
|
||||
pub tracker: Tracker<Self>,
|
||||
}
|
||||
|
||||
impl DataOffer for ZwlrDataControlOfferV1 {
|
||||
type Device = ZwlrDataControlDeviceV1;
|
||||
impl DataControlOffer for ZwlrDataControlOfferV1 {
|
||||
type Ipc = WlrDataControlIpc;
|
||||
|
||||
fn offer_data(&self) -> &OfferData<ZwlrDataControlDeviceV1> {
|
||||
fn data(&self) -> &DataControlOfferData<Self::Ipc> {
|
||||
&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
|
||||
self.send_offer(mime_type);
|
||||
}
|
||||
}
|
||||
|
||||
impl ZwlrDataControlOfferV1 {
|
||||
pub fn send_offer(&self, mime_type: &str) {
|
||||
self.client.event(Offer {
|
||||
self.data.client.event(Offer {
|
||||
self_id: self.id,
|
||||
mime_type,
|
||||
})
|
||||
|
|
@ -80,38 +46,24 @@ 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)
|
||||
}
|
||||
}
|
||||
logic::data_offer_receive(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)?;
|
||||
logic::data_offer_destroy(self)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
self = ZwlrDataControlOfferV1;
|
||||
version = self.device.version;
|
||||
version = self.data.device.data.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),
|
||||
}
|
||||
logic::data_offer_break_loops(self);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -120,6 +72,5 @@ simple_add_obj!(ZwlrDataControlOfferV1);
|
|||
#[derive(Debug, Error)]
|
||||
pub enum ZwlrDataControlOfferV1Error {
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
DataControlError(#[from] DataControlError),
|
||||
}
|
||||
efrom!(ZwlrDataControlOfferV1Error, ClientError);
|
||||
|
|
|
|||
|
|
@ -1,17 +1,15 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
ifs::{
|
||||
ipc::{
|
||||
add_data_source_mime_type, break_source_loops, cancel_offers,
|
||||
data_control::zwlr_data_control_device_v1::{
|
||||
WlrClipboardIpc, WlrPrimarySelectionIpc,
|
||||
client::Client,
|
||||
ifs::ipc::{
|
||||
data_control::{
|
||||
private::{
|
||||
logic::{self, DataControlError},
|
||||
DataControlSource, DataControlSourceData,
|
||||
},
|
||||
destroy_data_source, detach_seat, offer_source_to_x,
|
||||
x_data_device::{XClipboardIpc, XIpcDevice, XPrimarySelectionIpc},
|
||||
DataSource, DynDataSource, IpcLocation, SourceData,
|
||||
zwlr_data_control_device_v1::WlrDataControlIpc,
|
||||
},
|
||||
wl_seat::WlSeatGlobal,
|
||||
IpcLocation, SourceData,
|
||||
},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
|
|
@ -24,41 +22,23 @@ use {
|
|||
|
||||
pub struct ZwlrDataControlSourceV1 {
|
||||
pub id: ZwlrDataControlSourceV1Id,
|
||||
pub data: SourceData,
|
||||
pub version: Version,
|
||||
pub location: Cell<IpcLocation>,
|
||||
pub used: Cell<bool>,
|
||||
pub data: DataControlSourceData,
|
||||
pub tracker: Tracker<Self>,
|
||||
}
|
||||
|
||||
impl DataSource for ZwlrDataControlSourceV1 {
|
||||
fn send_cancelled(&self, _seat: &Rc<WlSeatGlobal>) {
|
||||
ZwlrDataControlSourceV1::send_cancelled(self);
|
||||
}
|
||||
}
|
||||
impl DataControlSource for ZwlrDataControlSourceV1 {
|
||||
type Ipc = WlrDataControlIpc;
|
||||
|
||||
impl DynDataSource for ZwlrDataControlSourceV1 {
|
||||
fn source_data(&self) -> &SourceData {
|
||||
fn data(&self) -> &DataControlSourceData {
|
||||
&self.data
|
||||
}
|
||||
|
||||
fn send_cancelled(&self) {
|
||||
self.send_cancelled();
|
||||
}
|
||||
|
||||
fn send_send(&self, mime_type: &str, fd: Rc<OwnedFd>) {
|
||||
ZwlrDataControlSourceV1::send_send(&self, mime_type, fd);
|
||||
}
|
||||
|
||||
fn offer_to_x(self: Rc<Self>, dd: &Rc<XIpcDevice>) {
|
||||
match self.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)
|
||||
self.send_send(mime_type, fd);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -66,16 +46,18 @@ 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(),
|
||||
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.data.data.client.event(Send {
|
||||
self_id: self.id,
|
||||
mime_type,
|
||||
fd,
|
||||
|
|
@ -83,7 +65,7 @@ impl ZwlrDataControlSourceV1 {
|
|||
}
|
||||
|
||||
pub fn send_cancelled(&self) {
|
||||
self.data.client.event(Cancelled { self_id: self.id })
|
||||
self.data.data.client.event(Cancelled { self_id: self.id })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -91,34 +73,24 @@ 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);
|
||||
logic::data_source_offer(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)?;
|
||||
logic::data_source_destroy(self)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
self = ZwlrDataControlSourceV1;
|
||||
version = self.version;
|
||||
version = self.data.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),
|
||||
}
|
||||
logic::data_source_break_loops(self);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -131,8 +103,5 @@ dedicated_add_obj!(
|
|||
#[derive(Debug, Error)]
|
||||
pub enum ZwlrDataControlSourceV1Error {
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
#[error("The source has already been used")]
|
||||
AlreadyUsed,
|
||||
DataControlError(#[from] DataControlError),
|
||||
}
|
||||
efrom!(ZwlrDataControlSourceV1Error, ClientError);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue