Watch
1
0
Fork
You've already forked smithay
0

wayland.selection: ExtDataControl

This commit is contained in:
PolyMeilex 2024-11-03 16:02:05 +01:00 • committed by Victoria Brekenfeld
commit dbd088dae0
11 changed files with 669 additions and 51 deletions

View file

@ -53,15 +53,15 @@ thiserror = "1.0.25"
udev = { version = "0.9.0", optional = true }
# Require never `wayland-client`/`wayland-cursor` than winit uses to fix `-Z minimal-versions`
# due to issue in older version.
wayland-client = { version = "0.31.3", optional = true }
wayland-cursor = { version = "0.31.3", optional = true }
wayland-egl = { version = "0.32.0", optional = true }
wayland-protocols = { version = "0.32.5", features = ["unstable", "staging", "server"], optional = true }
wayland-protocols-wlr = { version = "0.3.1", features = ["server"], optional = true }
wayland-protocols-misc = { version = "0.3.1", features = ["server"], optional = true }
wayland-server = { version = "0.31.0", optional = true }
wayland-sys = { version = "0.31", optional = true }
wayland-backend = { version = "0.3.5", optional = true }
wayland-client = { version = "0.31.8", optional = true }
wayland-cursor = { version = "0.31.8", optional = true }
wayland-egl = { version = "0.32.5", optional = true }
wayland-protocols = { version = "0.32.6", features = ["unstable", "staging", "server"], optional = true }
wayland-protocols-wlr = { version = "0.3.6", features = ["server"], optional = true }
wayland-protocols-misc = { version = "0.3.6", features = ["server"], optional = true }
wayland-server = { version = "0.31.7", optional = true }
wayland-sys = { version = "0.31.6", optional = true }
wayland-backend = { version = "0.3.8", optional = true }
winit = { version = "0.30.0", default-features = false, features = ["wayland", "wayland-dlopen", "x11", "rwh_06"], optional = true }
x11rb = { version = "0.13.0", optional = true }
xkbcommon = { version = "0.8.0", features = ["wayland"]}

View file

@ -1,6 +1,7 @@
use std::any::Any;
use std::any::TypeId;
use wayland_protocols::ext::data_control::v1::server::ext_data_control_device_v1::ExtDataControlDeviceV1;
use wayland_protocols::wp::primary_selection::zv1::server::zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1 as PrimaryDevice;
use wayland_protocols_wlr::data_control::v1::server::zwlr_data_control_device_v1::ZwlrDataControlDeviceV1;
use wayland_server::backend::ObjectId;
@ -9,16 +10,81 @@ use wayland_server::protocol::wl_seat::WlSeat;
use wayland_server::Resource;
use super::data_device::DataDeviceUserData;
use super::ext_data_control::ExtDataControlDeviceUserData;
use super::offer::DataControlOffer;
use super::offer::SelectionOffer;
use super::primary_selection::PrimaryDeviceUserData;
use super::private::selection_dispatch;
use super::wlr_data_control::DataControlDeviceUserData;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SelectionDevice {
pub(crate) enum DataControlDevice {
Wlr(ZwlrDataControlDeviceV1),
Ext(ExtDataControlDeviceV1),
}
impl DataControlDevice {
fn data_offer(&self, offer: &DataControlOffer) {
match (self, offer) {
(Self::Wlr(obj), DataControlOffer::Wlr(offer)) => obj.data_offer(offer),
(Self::Ext(obj), DataControlOffer::Ext(offer)) => obj.data_offer(offer),
_ => unreachable!(),
}
}
fn selection(&self, offer: Option<&DataControlOffer>) {
match (self, offer) {
(Self::Wlr(obj), Some(DataControlOffer::Wlr(offer))) => obj.selection(Some(offer)),
(Self::Ext(obj), Some(DataControlOffer::Ext(offer))) => obj.selection(Some(offer)),
(Self::Wlr(obj), None) => obj.selection(None),
(Self::Ext(obj), None) => obj.selection(None),
_ => unreachable!(),
}
}
fn primary_selection(&self, offer: Option<&DataControlOffer>) {
match (self, offer) {
(Self::Wlr(obj), Some(DataControlOffer::Wlr(offer))) => obj.primary_selection(Some(offer)),
(Self::Ext(obj), Some(DataControlOffer::Ext(offer))) => obj.primary_selection(Some(offer)),
(Self::Wlr(obj), None) => obj.primary_selection(None),
(Self::Ext(obj), None) => obj.primary_selection(None),
_ => unreachable!(),
}
}
fn wl_seat(&self) -> WlSeat {
match self {
Self::Wlr(device) => {
let data: &DataControlDeviceUserData = device.data().unwrap();
data.wl_seat.clone()
}
Self::Ext(device) => {
let data: &ExtDataControlDeviceUserData = device.data().unwrap();
data.wl_seat.clone()
}
}
}
fn id(&self) -> ObjectId {
match self {
Self::Wlr(obj) => obj.id(),
Self::Ext(obj) => obj.id(),
}
}
fn version(&self) -> u32 {
match self {
Self::Wlr(obj) => obj.version(),
Self::Ext(obj) => obj.version(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum SelectionDevice {
DataDevice(WlDataDevice),
Primary(PrimaryDevice),
DataControl(ZwlrDataControlDeviceV1),
DataControl(DataControlDevice),
}
impl SelectionDevice {
@ -36,7 +102,12 @@ impl SelectionDevice {
/// Get the [`TypeId`] of the underlying data device provider.
pub fn inner_type_id(&self) -> TypeId {
selection_dispatch!(self; Self(device) => device.type_id())
match self {
Self::DataDevice(device) => device.type_id(),
Self::Primary(device) => device.type_id(),
Self::DataControl(DataControlDevice::Wlr(device)) => device.type_id(),
Self::DataControl(DataControlDevice::Ext(device)) => device.type_id(),
}
}
/// [`WlSeat`] associated with this device.
@ -50,10 +121,7 @@ impl SelectionDevice {
let data: &PrimaryDeviceUserData = device.data().unwrap();
data.wl_seat.clone()
}
SelectionDevice::DataControl(device) => {
let data: &DataControlDeviceUserData = device.data().unwrap();
data.wl_seat.clone()
}
SelectionDevice::DataControl(device) => device.wl_seat(),
}
}

View file

@ -0,0 +1,104 @@
use std::cell::RefCell;
use wayland_protocols::ext::data_control::v1::server::ext_data_control_device_v1::{
self, ExtDataControlDeviceV1,
};
use wayland_server::protocol::wl_seat::WlSeat;
use wayland_server::{Client, Dispatch, DisplayHandle};
use crate::input::Seat;
use crate::wayland::selection::device::{DataControlDevice, SelectionDevice};
use crate::wayland::selection::offer::OfferReplySource;
use crate::wayland::selection::seat_data::SeatData;
use crate::wayland::selection::source::{DataControlSource, SelectionSourceProvider};
use crate::wayland::selection::{SelectionSource, SelectionTarget};
use super::{DataControlHandler, DataControlState};
#[doc(hidden)]
#[derive(Debug)]
pub struct ExtDataControlDeviceUserData {
pub(crate) primary: bool,
pub(crate) wl_seat: WlSeat,
}
impl<D> Dispatch<ExtDataControlDeviceV1, ExtDataControlDeviceUserData, D> for DataControlState
where
D: Dispatch<ExtDataControlDeviceV1, ExtDataControlDeviceUserData>,
D: DataControlHandler,
D: 'static,
{
fn request(
handler: &mut D,
_client: &Client,
resource: &ExtDataControlDeviceV1,
request: <ExtDataControlDeviceV1 as wayland_server::Resource>::Request,
data: &ExtDataControlDeviceUserData,
dh: &DisplayHandle,
_: &mut wayland_server::DataInit<'_, D>,
) {
let seat = match Seat::<D>::from_resource(&data.wl_seat) {
Some(seat) => seat,
None => return,
};
match request {
ext_data_control_device_v1::Request::SetSelection { source, .. } => {
seat.user_data()
.insert_if_missing(|| RefCell::new(SeatData::<D::SelectionUserData>::new()));
let source = source
.map(DataControlSource::Ext)
.map(SelectionSourceProvider::DataControl);
handler.new_selection(
SelectionTarget::Clipboard,
source.clone().map(|provider| SelectionSource { provider }),
seat.clone(),
);
seat.user_data()
.get::<RefCell<SeatData<D::SelectionUserData>>>()
.unwrap()
.borrow_mut()
.set_clipboard_selection::<D>(dh, source.map(OfferReplySource::Client));
}
ext_data_control_device_v1::Request::SetPrimarySelection { source, .. } => {
// When the primary selection is disabled, we should simply ignore the requests.
if !data.primary {
return;
}
seat.user_data()
.insert_if_missing(|| RefCell::new(SeatData::<D::SelectionUserData>::new()));
let source = source
.map(DataControlSource::Ext)
.map(SelectionSourceProvider::DataControl);
handler.new_selection(
SelectionTarget::Primary,
source.clone().map(|provider| SelectionSource { provider }),
seat.clone(),
);
seat.user_data()
.get::<RefCell<SeatData<D::SelectionUserData>>>()
.unwrap()
.borrow_mut()
.set_primary_selection::<D>(dh, source.map(OfferReplySource::Client));
}
ext_data_control_device_v1::Request::Destroy => seat
.user_data()
.get::<RefCell<SeatData<D::SelectionUserData>>>()
.unwrap()
.borrow_mut()
.retain_devices(|ndd| match ndd {
SelectionDevice::DataControl(DataControlDevice::Ext(ndd)) => ndd != resource,
_ => true,
}),
_ => unreachable!(),
}
}
}

View file

@ -0,0 +1,257 @@
//! Automatic handling of the `ext_data_control` protocol
//!
//! ## Initialization
//!
//! To initialize this implementation, create [`DataControlState`], store it in your `State`
//! struct, and implement the required trait, as shown in the example:
//!
//! ```
//! # extern crate wayland_server;
//! # #[macro_use] extern crate smithay;
//! use smithay::wayland::selection::SelectionHandler;
//! use smithay::wayland::selection::ext_data_control::{DataControlState, DataControlHandler};
//! # use smithay::input::{Seat, SeatHandler, SeatState, pointer::CursorImageStatus};
//! # use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
//!
//! # struct State { data_control_state: DataControlState }
//! # let mut display = wayland_server::Display::<State>::new().unwrap();
//! // Create the data_control state
//! let data_control_state = DataControlState::new::<State, _>(
//! &display.handle(), None, |_| true
//! );
//!
//! // insert the DataControlState into your state
//! // ..
//!
//! // implement the necessary traits
//! # impl SeatHandler for State {
//! # type KeyboardFocus = WlSurface;
//! # type PointerFocus = WlSurface;
//! # type TouchFocus = WlSurface;
//! # fn seat_state(&mut self) -> &mut SeatState<Self> { unimplemented!() }
//! # fn focus_changed(&mut self, seat: &Seat<Self>, focused: Option<&WlSurface>) { unimplemented!() }
//! # fn cursor_image(&mut self, seat: &Seat<Self>, image: CursorImageStatus) { unimplemented!() }
//! # }
//! impl SelectionHandler for State {
//! type SelectionUserData = ();
//! }
//! impl DataControlHandler for State {
//! fn data_control_state(&self) -> &DataControlState { &self.data_control_state }
//! // ... override default implementations here to customize handling ...
//! }
//! delegate_ext_data_control!(State);
//!
//! // You're now ready to go!
//! ```
//!
//! Be aware that data control clients rely on other selection providers to be implemneted, like
//! wl_data_device or zwp_primary_selection.
use wayland_protocols::ext::data_control::v1::server::ext_data_control_manager_v1::ExtDataControlManagerV1;
use wayland_server::backend::GlobalId;
use wayland_server::{Client, DisplayHandle, GlobalDispatch};
mod device;
mod source;
pub use device::ExtDataControlDeviceUserData;
pub use source::ExtDataControlSourceUserData;
use super::primary_selection::PrimarySelectionState;
use super::SelectionHandler;
/// Access the data control state.
pub trait DataControlHandler: Sized + SelectionHandler {
/// [`DataControlState`] getter.
fn data_control_state(&self) -> &DataControlState;
}
/// State of the data control.
#[derive(Debug)]
pub struct DataControlState {
manager_global: GlobalId,
}
impl DataControlState {
/// Register new [ExtDataControlManagerV1] global.
///
/// Passing `primary_selection` will enable support for primary selection as well.
pub fn new<D, F>(
display: &DisplayHandle,
primary_selection: Option<&PrimarySelectionState>,
filter: F,
) -> Self
where
D: GlobalDispatch<ExtDataControlManagerV1, ExtDataControlManagerGlobalData> + 'static,
F: for<'c> Fn(&'c Client) -> bool + Send + Sync + 'static,
{
let data = ExtDataControlManagerGlobalData {
primary: primary_selection.is_some(),
filter: Box::new(filter),
};
let manager_global = display.create_global::<D, ExtDataControlManagerV1, _>(1, data);
Self { manager_global }
}
/// [ExtDataControlManagerV1] GlobalId getter.
pub fn global(&self) -> GlobalId {
self.manager_global.clone()
}
}
#[allow(missing_debug_implementations)]
#[doc(hidden)]
pub struct ExtDataControlManagerGlobalData {
/// Whether to allow primary selection.
primary: bool,
/// Filter whether the clients can view global.
filter: Box<dyn for<'c> Fn(&'c Client) -> bool + Send + Sync>,
}
#[doc(hidden)]
#[derive(Debug, Clone, Copy)]
pub struct ExtDataControlManagerUserData {
/// Whether to allow primary selection.
primary: bool,
}
mod handlers {
use std::cell::RefCell;
use crate::wayland::selection::device::DataControlDevice;
use tracing::error;
use wayland_protocols::ext::data_control::v1::server::{
ext_data_control_device_v1::ExtDataControlDeviceV1,
ext_data_control_manager_v1::{self, ExtDataControlManagerV1},
ext_data_control_source_v1::ExtDataControlSourceV1,
};
use wayland_server::{Client, Dispatch, DisplayHandle, GlobalDispatch};
use crate::input::Seat;
use crate::wayland::selection::device::SelectionDevice;
use crate::wayland::selection::seat_data::SeatData;
use crate::wayland::selection::SelectionTarget;
use super::DataControlHandler;
use super::DataControlState;
use super::ExtDataControlDeviceUserData;
use super::ExtDataControlManagerGlobalData;
use super::ExtDataControlManagerUserData;
use super::ExtDataControlSourceUserData;
impl<D> GlobalDispatch<ExtDataControlManagerV1, ExtDataControlManagerGlobalData, D> for DataControlState
where
D: GlobalDispatch<ExtDataControlManagerV1, ExtDataControlManagerGlobalData>,
D: Dispatch<ExtDataControlManagerV1, ExtDataControlManagerUserData>,
D: Dispatch<ExtDataControlDeviceV1, ExtDataControlDeviceUserData>,
D: Dispatch<ExtDataControlSourceV1, ExtDataControlSourceUserData>,
D: DataControlHandler,
D: 'static,
{
fn bind(
_state: &mut D,
_handle: &DisplayHandle,
_client: &wayland_server::Client,
resource: wayland_server::New<ExtDataControlManagerV1>,
global_data: &ExtDataControlManagerGlobalData,
data_init: &mut wayland_server::DataInit<'_, D>,
) {
data_init.init(
resource,
ExtDataControlManagerUserData {
primary: global_data.primary,
},
);
}
fn can_view(client: Client, global_data: &ExtDataControlManagerGlobalData) -> bool {
(global_data.filter)(&client)
}
}
impl<D> Dispatch<ExtDataControlManagerV1, ExtDataControlManagerUserData, D> for DataControlState
where
D: Dispatch<ExtDataControlManagerV1, ExtDataControlManagerUserData>,
D: Dispatch<ExtDataControlDeviceV1, ExtDataControlDeviceUserData>,
D: Dispatch<ExtDataControlSourceV1, ExtDataControlSourceUserData>,
D: DataControlHandler,
D: 'static,
{
fn request(
_handler: &mut D,
client: &wayland_server::Client,
_resource: &ExtDataControlManagerV1,
request: <ExtDataControlManagerV1 as wayland_server::Resource>::Request,
data: &ExtDataControlManagerUserData,
dh: &DisplayHandle,
data_init: &mut wayland_server::DataInit<'_, D>,
) {
match request {
ext_data_control_manager_v1::Request::CreateDataSource { id } => {
data_init.init(id, ExtDataControlSourceUserData::new());
}
ext_data_control_manager_v1::Request::GetDataDevice { id, seat: wl_seat } => {
match Seat::<D>::from_resource(&wl_seat) {
Some(seat) => {
seat.user_data()
.insert_if_missing(|| RefCell::new(SeatData::<D::SelectionUserData>::new()));
let device =
SelectionDevice::DataControl(DataControlDevice::Ext(data_init.init(
id,
ExtDataControlDeviceUserData {
wl_seat,
primary: data.primary,
},
)));
let mut seat_data = seat
.user_data()
.get::<RefCell<SeatData<D::SelectionUserData>>>()
.unwrap()
.borrow_mut();
seat_data.add_device(device.clone());
// NOTE: broadcast selection only to the newly created device.
let device = Some(&device);
seat_data.send_selection::<D>(dh, SelectionTarget::Clipboard, device, true);
if data.primary {
seat_data.send_selection::<D>(dh, SelectionTarget::Primary, device, true);
}
}
None => {
error!(
data_control_device = ?id,
client = ?client,
"Unmanaged seat given to a primary selection device."
);
}
}
}
ext_data_control_manager_v1::Request::Destroy => (),
_ => unreachable!(),
}
}
}
}
/// Macro to delegate implementation of the ext_data_control protocol
#[macro_export]
macro_rules! delegate_ext_data_control {
($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
$crate::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
$crate::reexports::wayland_protocols::ext::data_control::v1::server::ext_data_control_manager_v1::ExtDataControlManagerV1: $crate::wayland::selection::ext_data_control::ExtDataControlManagerGlobalData
] => $crate::wayland::selection::ext_data_control::DataControlState);
$crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
$crate::reexports::wayland_protocols::ext::data_control::v1::server::ext_data_control_manager_v1::ExtDataControlManagerV1: $crate::wayland::selection::ext_data_control::ExtDataControlManagerUserData
] => $crate::wayland::selection::ext_data_control::DataControlState);
$crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
$crate::reexports::wayland_protocols::ext::data_control::v1::server::ext_data_control_device_v1::ExtDataControlDeviceV1: $crate::wayland::selection::ext_data_control::ExtDataControlDeviceUserData
] => $crate::wayland::selection::ext_data_control::DataControlState);
$crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
$crate::reexports::wayland_protocols::ext::data_control::v1::server::ext_data_control_source_v1::ExtDataControlSourceV1: $crate::wayland::selection::ext_data_control::ExtDataControlSourceUserData
] => $crate::wayland::selection::ext_data_control::DataControlState);
};
}

View file

@ -0,0 +1,76 @@
use std::sync::Mutex;
use wayland_server::backend::ClientId;
use wayland_server::{Dispatch, DisplayHandle, Resource};
use crate::utils::alive_tracker::AliveTracker;
use crate::utils::IsAlive;
use wayland_protocols::ext::data_control::v1::server::ext_data_control_source_v1::{
self, ExtDataControlSourceV1,
};
use super::{DataControlHandler, DataControlState};
#[doc(hidden)]
#[derive(Default, Debug)]
pub struct ExtDataControlSourceUserData {
pub(crate) inner: Mutex<SourceMetadata>,
alive_tracker: AliveTracker,
}
impl ExtDataControlSourceUserData {
pub(crate) fn new() -> Self {
Self::default()
}
}
/// The metadata describing a data source
#[derive(Debug, Default, Clone)]
pub struct SourceMetadata {
/// The MIME types supported by this source
pub mime_types: Vec<String>,
}
impl<D> Dispatch<ExtDataControlSourceV1, ExtDataControlSourceUserData, D> for DataControlState
where
D: Dispatch<ExtDataControlSourceV1, ExtDataControlSourceUserData>,
D: DataControlHandler,
D: 'static,
{
fn request(
_state: &mut D,
_client: &wayland_server::Client,
_resource: &ExtDataControlSourceV1,
request: <ExtDataControlSourceV1 as wayland_server::Resource>::Request,
data: &ExtDataControlSourceUserData,
_dhandle: &DisplayHandle,
_data_init: &mut wayland_server::DataInit<'_, D>,
) {
match request {
ext_data_control_source_v1::Request::Offer { mime_type } => {
let mut data = data.inner.lock().unwrap();
data.mime_types.push(mime_type);
}
ext_data_control_source_v1::Request::Destroy => (),
_ => unreachable!(),
}
}
fn destroyed(
_state: &mut D,
_client: ClientId,
_resource: &ExtDataControlSourceV1,
data: &ExtDataControlSourceUserData,
) {
data.alive_tracker.destroy_notify();
}
}
impl IsAlive for ExtDataControlSourceV1 {
#[inline]
fn alive(&self) -> bool {
let data: &ExtDataControlSourceUserData = self.data().unwrap();
data.alive_tracker.alive()
}
}

View file

@ -16,6 +16,7 @@ use std::os::unix::io::OwnedFd;
use crate::input::{Seat, SeatHandler};
pub mod data_device;
pub mod ext_data_control;
pub mod primary_selection;
pub mod wlr_data_control;

View file

@ -3,12 +3,17 @@ use std::os::unix::io::OwnedFd;
use std::sync::Arc;
use tracing::debug;
use wayland_protocols::ext::data_control::v1::server::ext_data_control_device_v1::ExtDataControlDeviceV1;
use wayland_protocols::ext::data_control::v1::server::ext_data_control_offer_v1::{
self, ExtDataControlOfferV1,
};
use wayland_protocols::wp::primary_selection::zv1::server::zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1 as PrimaryDevice;
use wayland_protocols::wp::primary_selection::zv1::server::zwp_primary_selection_offer_v1::{
self, ZwpPrimarySelectionOfferV1 as PrimaryOffer,
};
use wayland_protocols_wlr::data_control::v1::server::zwlr_data_control_device_v1::ZwlrDataControlDeviceV1;
use wayland_protocols_wlr::data_control::v1::server::zwlr_data_control_offer_v1::{
self, ZwlrDataControlOfferV1 as DataControlOffer,
self, ZwlrDataControlOfferV1,
};
use wayland_server::backend::protocol::Message;
use wayland_server::backend::ObjectId;
@ -55,9 +60,27 @@ impl<U: Clone + Send + Sync + 'static> OfferReplySource<U> {
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DataControlOffer {
Wlr(ZwlrDataControlOfferV1),
Ext(ExtDataControlOfferV1),
}
impl DataControlOffer {
/// Advertise offered MIME type
///
/// Sent immediately after creating the wlr_data_control_offer object. One event per offered MIME type.
fn offer(&self, mime_type: String) {
match self {
Self::Wlr(obj) => obj.offer(mime_type),
Self::Ext(obj) => obj.offer(mime_type),
}
}
}
/// Offer representing various selection offers.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SelectionOffer {
pub(crate) enum SelectionOffer {
DataDevice(WlDataOffer),
Primary(PrimaryOffer),
DataControl(DataControlOffer),
@ -88,8 +111,12 @@ impl SelectionOffer {
WlDataOffer::interface()
} else if type_id == TypeId::of::<PrimaryDevice>() {
PrimaryOffer::interface()
} else if type_id == TypeId::of::<ZwlrDataControlDeviceV1>() {
ZwlrDataControlOfferV1::interface()
} else if type_id == TypeId::of::<ExtDataControlDeviceV1>() {
ExtDataControlOfferV1::interface()
} else {
DataControlOffer::interface()
unreachable!()
};
let offer = backend
@ -100,8 +127,16 @@ impl SelectionOffer {
Self::DataDevice(WlDataOffer::from_id(dh, offer).unwrap())
} else if type_id == TypeId::of::<PrimaryDevice>() {
Self::Primary(PrimaryOffer::from_id(dh, offer).unwrap())
} else if type_id == TypeId::of::<ZwlrDataControlDeviceV1>() {
Self::DataControl(DataControlOffer::Wlr(
ZwlrDataControlOfferV1::from_id(dh, offer).unwrap(),
))
} else if type_id == TypeId::of::<ExtDataControlDeviceV1>() {
Self::DataControl(DataControlOffer::Ext(
ExtDataControlOfferV1::from_id(dh, offer).unwrap(),
))
} else {
Self::DataControl(DataControlOffer::from_id(dh, offer).unwrap())
unreachable!()
}
}
@ -149,10 +184,22 @@ where
} else {
return None;
}
} else if let Ok((_resource, DataControlRequest::Receive { mime_type, fd })) =
DataControlOffer::parse_request(&dh, msg)
{
(mime_type, fd, "data_control_offer")
} else if type_id == TypeId::of::<ZwlrDataControlDeviceV1>() {
if let Ok((_resource, DataControlRequest::Receive { mime_type, fd })) =
ZwlrDataControlOfferV1::parse_request(&dh, msg)
{
(mime_type, fd, "wlr_data_control_offer")
} else {
return None;
}
} else if type_id == TypeId::of::<ExtDataControlDeviceV1>() {
if let Ok((_resource, ext_data_control_offer_v1::Request::Receive { mime_type, fd })) =
ExtDataControlOfferV1::parse_request(&dh, msg)
{
(mime_type, fd, "ext_data_control_offer")
} else {
return None;
}
} else {
return None;
};

View file

@ -4,14 +4,14 @@ use wayland_server::{Client, DisplayHandle, Resource};
use crate::utils::IsAlive;
use super::device::SelectionDevice;
use super::device::{DataControlDevice, SelectionDevice};
use super::offer::{OfferReplySource, SelectionOffer};
use super::{SelectionHandler, SelectionTarget};
/// Seat data used to handle regular selection operations.
///
/// The data is shared accross primary, data device, and data control selections.
pub struct SeatData<U: Clone + Sync + Send + 'static> {
/// The data is shared across primary, data device, and data control selections.
pub(crate) struct SeatData<U: Clone + Sync + Send + 'static> {
known_devices: Vec<SelectionDevice>,
clipboard_selection: Option<OfferReplySource<U>>,
clipboard_selection_focus: Option<Client>,
@ -20,7 +20,7 @@ pub struct SeatData<U: Clone + Sync + Send + 'static> {
}
impl<U: Clone + Send + Sync + 'static> SeatData<U> {
/// Create a new [`SeatData`] with emply selections and without focusing any client.
/// Create a new [`SeatData`] with empty selections and without focusing any client.
pub fn new() -> Self {
Default::default()
}
@ -166,12 +166,13 @@ impl<U: Clone + Send + Sync + 'static> SeatData<U> {
// later on.
SelectionDevice::DataDevice(_) => ty == SelectionTarget::Clipboard,
SelectionDevice::Primary(_) => ty == SelectionTarget::Primary,
SelectionDevice::DataControl(data_control) => {
SelectionDevice::DataControl(DataControlDevice::Wlr(data_control)) => {
// Primary selection is available for data control only since v2.
update_data_control
&& (data_control.version() >= EVT_PRIMARY_SELECTION_SINCE
|| ty != SelectionTarget::Primary)
}
SelectionDevice::DataControl(DataControlDevice::Ext(_)) => update_data_control,
})
{
// Data control doesn't require focus and should always get selection updates, unless
@ -198,7 +199,10 @@ impl<U: Clone + Send + Sync + 'static> SeatData<U> {
// DataControl devices is the client itself, however other devices use
// the currently focused one as a client.
let client_id = match device {
SelectionDevice::DataControl(device) => {
SelectionDevice::DataControl(DataControlDevice::Wlr(device)) => {
dh.get_client(device.id()).ok().map(|c| c.id())
}
SelectionDevice::DataControl(DataControlDevice::Ext(device)) => {
dh.get_client(device.id()).ok().map(|c| c.id())
}
_ => client.map(|c| c.id()),

View file

@ -1,13 +1,16 @@
use std::os::fd::BorrowedFd;
use std::os::unix::io::{AsFd, OwnedFd};
use wayland_protocols::ext::data_control::v1::server::ext_data_control_source_v1::ExtDataControlSourceV1;
use wayland_protocols::wp::primary_selection::zv1::server::zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1 as PrimarySource;
use wayland_protocols_wlr::data_control::v1::server::zwlr_data_control_source_v1::ZwlrDataControlSourceV1 as DataControlSource;
use wayland_protocols_wlr::data_control::v1::server::zwlr_data_control_source_v1::ZwlrDataControlSourceV1;
use wayland_server::{protocol::wl_data_source::WlDataSource, Resource};
use crate::utils::IsAlive;
use crate::wayland::selection::primary_selection::PrimarySourceUserData;
use super::data_device::DataSourceUserData;
use super::ext_data_control::ExtDataControlSourceUserData;
use super::private::selection_dispatch;
use super::wlr_data_control::DataControlSourceUserData;
use super::SelectionTarget;
@ -26,6 +29,64 @@ impl SelectionSource {
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DataControlSource {
Wlr(ZwlrDataControlSourceV1),
Ext(ExtDataControlSourceV1),
}
impl DataControlSource {
fn send(&self, mime_type: String, fd: BorrowedFd<'_>) {
match self {
Self::Wlr(obj) => obj.send(mime_type, fd),
Self::Ext(obj) => obj.send(mime_type, fd),
}
}
fn cancelled(&self) {
match self {
Self::Wlr(obj) => obj.cancelled(),
Self::Ext(obj) => obj.cancelled(),
}
}
fn mime_types(&self) -> Vec<String> {
match self {
Self::Wlr(source) => {
let data: &DataControlSourceUserData = source.data().unwrap();
data.inner.lock().unwrap().mime_types.clone()
}
Self::Ext(source) => {
let data: &ExtDataControlSourceUserData = source.data().unwrap();
data.inner.lock().unwrap().mime_types.clone()
}
}
}
fn contains_mime_type(&self, mime_type: &String) -> bool {
match self {
Self::Wlr(source) => {
let data: &DataControlSourceUserData = source.data().unwrap();
data.inner.lock().unwrap().mime_types.contains(mime_type)
}
Self::Ext(source) => {
let data: &ExtDataControlSourceUserData = source.data().unwrap();
data.inner.lock().unwrap().mime_types.contains(mime_type)
}
}
}
}
impl IsAlive for DataControlSource {
#[inline]
fn alive(&self) -> bool {
match self {
Self::Wlr(obj) => obj.alive(),
Self::Ext(obj) => obj.alive(),
}
}
}
/// Provider of the selection data.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SelectionSourceProvider {
@ -59,10 +120,7 @@ impl SelectionSourceProvider {
let data: &PrimarySourceUserData = source.data().unwrap();
data.inner.lock().unwrap().mime_types.contains(mime_type)
}
Self::DataControl(source) => {
let data: &DataControlSourceUserData = source.data().unwrap();
data.inner.lock().unwrap().mime_types.contains(mime_type)
}
Self::DataControl(source) => source.contains_mime_type(mime_type),
}
}
@ -77,10 +135,7 @@ impl SelectionSourceProvider {
let data: &PrimarySourceUserData = source.data().unwrap();
data.inner.lock().unwrap().mime_types.clone()
}
Self::DataControl(source) => {
let data: &DataControlSourceUserData = source.data().unwrap();
data.inner.lock().unwrap().mime_types.clone()
}
Self::DataControl(source) => source.mime_types(),
}
}
}

View file

@ -8,10 +8,10 @@ use wayland_server::protocol::wl_seat::WlSeat;
use wayland_server::{Client, Dispatch, DisplayHandle};
use crate::input::Seat;
use crate::wayland::selection::device::SelectionDevice;
use crate::wayland::selection::device::{DataControlDevice, SelectionDevice};
use crate::wayland::selection::offer::OfferReplySource;
use crate::wayland::selection::seat_data::SeatData;
use crate::wayland::selection::source::SelectionSourceProvider;
use crate::wayland::selection::source::{DataControlSource, SelectionSourceProvider};
use crate::wayland::selection::{SelectionSource, SelectionTarget};
use super::{DataControlHandler, DataControlState};
@ -48,7 +48,9 @@ where
seat.user_data()
.insert_if_missing(|| RefCell::new(SeatData::<D::SelectionUserData>::new()));
let source = source.map(SelectionSourceProvider::DataControl);
let source = source
.map(DataControlSource::Wlr)
.map(SelectionSourceProvider::DataControl);
handler.new_selection(
SelectionTarget::Clipboard,
@ -71,7 +73,9 @@ where
seat.user_data()
.insert_if_missing(|| RefCell::new(SeatData::<D::SelectionUserData>::new()));
let source = source.map(SelectionSourceProvider::DataControl);
let source = source
.map(DataControlSource::Wlr)
.map(SelectionSourceProvider::DataControl);
handler.new_selection(
SelectionTarget::Primary,
@ -91,7 +95,7 @@ where
.unwrap()
.borrow_mut()
.retain_devices(|ndd| match ndd {
SelectionDevice::DataControl(ndd) => ndd != resource,
SelectionDevice::DataControl(DataControlDevice::Wlr(ndd)) => ndd != resource,
_ => true,
}),

View file

@ -133,6 +133,7 @@ mod handlers {
use wayland_server::{Client, Dispatch, DisplayHandle, GlobalDispatch};
use crate::input::Seat;
use crate::wayland::selection::device::DataControlDevice;
use crate::wayland::selection::device::SelectionDevice;
use crate::wayland::selection::seat_data::SeatData;
use crate::wayland::selection::SelectionTarget;
@ -201,13 +202,14 @@ mod handlers {
seat.user_data()
.insert_if_missing(|| RefCell::new(SeatData::<D::SelectionUserData>::new()));
let device = SelectionDevice::DataControl(data_init.init(
id,
DataControlDeviceUserData {
wl_seat,
primary_selection_filter: Arc::clone(&data.primary_selection_filter),
},
));
let device =
SelectionDevice::DataControl(DataControlDevice::Wlr(data_init.init(
id,
DataControlDeviceUserData {
wl_seat,
primary_selection_filter: Arc::clone(&data.primary_selection_filter),
},
)));
let mut seat_data = seat
.user_data()