1
0
Fork 0
forked from wry/wry

ipc: implement ext-data-control

This commit is contained in:
Julian Orth 2024-10-08 16:07:31 +02:00
parent be1462d0ef
commit 4abbe94995
13 changed files with 575 additions and 3 deletions

View file

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

View file

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

View file

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

View file

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

View file

@ -3,6 +3,10 @@ use {
std::rc::Rc, 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; mod private;
pub mod zwlr_data_control_device_v1; pub mod zwlr_data_control_device_v1;
pub mod zwlr_data_control_manager_v1; pub mod zwlr_data_control_manager_v1;

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,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 {
}