wayland.selection: ExtDataControl
This commit is contained in:
parent
b9aea201c8
commit
dbd088dae0
11 changed files with 669 additions and 51 deletions
18
Cargo.toml
18
Cargo.toml
|
|
@ -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"]}
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
104
src/wayland/selection/ext_data_control/device.rs
Normal file
104
src/wayland/selection/ext_data_control/device.rs
Normal 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!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
257
src/wayland/selection/ext_data_control/mod.rs
Normal file
257
src/wayland/selection/ext_data_control/mod.rs
Normal 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);
|
||||
};
|
||||
}
|
||||
76
src/wayland/selection/ext_data_control/source.rs
Normal file
76
src/wayland/selection/ext_data_control/source.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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()),
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}),
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Reference in a new issue