Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add primary-selection filter #1641

Merged
merged 2 commits into from
Jan 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 49 additions & 9 deletions src/wayland/selection/primary_selection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
use std::{
cell::{Ref, RefCell},
os::unix::io::OwnedFd,
sync::Arc,
};

use tracing::instrument;
Expand Down Expand Up @@ -94,21 +95,48 @@ pub trait PrimarySelectionHandler: Sized + SeatHandler + SelectionHandler {
}

/// State of the primary selection.
#[derive(Debug)]
pub struct PrimarySelectionState {
manager_global: GlobalId,
pub(super) filter: Arc<Box<dyn for<'c> Fn(&'c Client) -> bool + Send + Sync>>,
}

impl std::fmt::Debug for PrimarySelectionState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PrimarySelectionState")
.field("manager_global", &self.manager_global)
.finish()
}
}

impl PrimarySelectionState {
/// Register new [`PrimaryDeviceManager`] global
pub fn new<D>(display: &DisplayHandle) -> Self
where
D: GlobalDispatch<PrimaryDeviceManager, ()> + 'static,
D: GlobalDispatch<PrimaryDeviceManager, PrimaryDeviceManagerGlobalData> + 'static,
D: PrimarySelectionHandler,
{
Self::new_with_filter::<D, _>(display, |_| true)
}

/// Register new [`PrimaryDeviceManager`] global with a filter.
///
/// Filters can be used to limit visibility of a global to certain clients.
pub fn new_with_filter<D, F>(display: &DisplayHandle, filter: F) -> Self
where
D: GlobalDispatch<PrimaryDeviceManager, PrimaryDeviceManagerGlobalData> + 'static,
D: PrimarySelectionHandler,
F: for<'c> Fn(&'c Client) -> bool + Send + Sync + 'static,
{
let manager_global = display.create_global::<D, PrimaryDeviceManager, _>(1, ());
let data = PrimaryDeviceManagerGlobalData {
filter: Arc::new(Box::new(filter)),
};
let filter = Arc::clone(&data.filter);
let manager_global = display.create_global::<D, PrimaryDeviceManager, _>(1, data);

Self { manager_global }
Self {
manager_global,
filter,
}
}

/// [`PrimaryDeviceManager`] GlobalId getter
Expand All @@ -117,6 +145,12 @@ impl PrimarySelectionState {
}
}

#[allow(missing_debug_implementations)]
#[doc(hidden)]
pub struct PrimaryDeviceManagerGlobalData {
filter: Arc<Box<dyn for<'c> Fn(&'c Client) -> bool + Send + Sync>>,
}

/// Set the primary selection focus to a certain client for a given seat
///
/// If the focus is different from the existing focus, the current selection will be offered to the client.
Expand Down Expand Up @@ -271,12 +305,14 @@ mod handlers {
wayland::selection::{device::SelectionDevice, seat_data::SeatData},
};

use super::{device::PrimaryDeviceUserData, source::PrimarySourceUserData};
use super::{
device::PrimaryDeviceUserData, source::PrimarySourceUserData, PrimaryDeviceManagerGlobalData,
};
use super::{PrimarySelectionHandler, PrimarySelectionState};

impl<D> GlobalDispatch<PrimaryDeviceManager, (), D> for PrimarySelectionState
impl<D> GlobalDispatch<PrimaryDeviceManager, PrimaryDeviceManagerGlobalData, D> for PrimarySelectionState
where
D: GlobalDispatch<PrimaryDeviceManager, ()>,
D: GlobalDispatch<PrimaryDeviceManager, PrimaryDeviceManagerGlobalData>,
D: Dispatch<PrimaryDeviceManager, ()>,
D: Dispatch<PrimarySource, PrimarySourceUserData>,
D: Dispatch<PrimaryDevice, PrimaryDeviceUserData>,
Expand All @@ -288,11 +324,15 @@ mod handlers {
_handle: &DisplayHandle,
_client: &wayland_server::Client,
resource: wayland_server::New<PrimaryDeviceManager>,
_global_data: &(),
_global_data: &PrimaryDeviceManagerGlobalData,
data_init: &mut wayland_server::DataInit<'_, D>,
) {
data_init.init(resource, ());
}

fn can_view(client: wayland_server::Client, global_data: &PrimaryDeviceManagerGlobalData) -> bool {
(global_data.filter)(&client)
}
}

impl<D> Dispatch<PrimaryDeviceManager, (), D> for PrimarySelectionState
Expand Down Expand Up @@ -355,7 +395,7 @@ mod handlers {
macro_rules! delegate_primary_selection {
($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
$crate::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
$crate::reexports::wayland_protocols::wp::primary_selection::zv1::server::zwp_primary_selection_device_manager_v1::ZwpPrimarySelectionDeviceManagerV1: ()
$crate::reexports::wayland_protocols::wp::primary_selection::zv1::server::zwp_primary_selection_device_manager_v1::ZwpPrimarySelectionDeviceManagerV1: $crate::wayland::selection::primary_selection::PrimaryDeviceManagerGlobalData
] => $crate::wayland::selection::primary_selection::PrimarySelectionState);

$crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
Expand Down
9 changes: 5 additions & 4 deletions src/wayland/selection/wlr_data_control/device.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::cell::RefCell;
use std::sync::Arc;

use wayland_protocols_wlr::data_control::v1::server::zwlr_data_control_device_v1::{
self, ZwlrDataControlDeviceV1,
Expand All @@ -15,10 +16,10 @@ use crate::wayland::selection::{SelectionSource, SelectionTarget};

use super::{DataControlHandler, DataControlState};

#[allow(missing_debug_implementations)]
#[doc(hidden)]
#[derive(Debug)]
pub struct DataControlDeviceUserData {
pub(crate) primary: bool,
pub(crate) primary_selection_filter: Arc<Box<dyn for<'c> Fn(&'c Client) -> bool + Send + Sync>>,
pub(crate) wl_seat: WlSeat,
}

Expand All @@ -30,7 +31,7 @@ where
{
fn request(
handler: &mut D,
_client: &Client,
client: &Client,
resource: &ZwlrDataControlDeviceV1,
request: <ZwlrDataControlDeviceV1 as wayland_server::Resource>::Request,
data: &DataControlDeviceUserData,
Expand Down Expand Up @@ -63,7 +64,7 @@ where
}
zwlr_data_control_device_v1::Request::SetPrimarySelection { source, .. } => {
// When the primary selection is disabled, we should simply ignore the requests.
if !data.primary {
if !(*data.primary_selection_filter)(client) {
return;
}

Expand Down
24 changes: 15 additions & 9 deletions src/wayland/selection/wlr_data_control/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@
//! Be aware that data control clients rely on other selection providers to be implemneted, like
//! wl_data_device or zwp_primary_selection.

use std::sync::Arc;

use wayland_protocols_wlr::data_control::v1::server::zwlr_data_control_manager_v1::ZwlrDataControlManagerV1;
use wayland_server::backend::GlobalId;
use wayland_server::{Client, DisplayHandle, GlobalDispatch};
Expand Down Expand Up @@ -86,7 +88,9 @@ impl DataControlState {
F: for<'c> Fn(&'c Client) -> bool + Send + Sync + 'static,
{
let data = DataControlManagerGlobalData {
primary: primary_selection.is_some(),
primary_selection_filter: primary_selection
.map(|x| Arc::clone(&x.filter))
.unwrap_or_else(|| Arc::new(Box::new(|_| false))),
filter: Box::new(filter),
};
let manager_global = display.create_global::<D, ZwlrDataControlManagerV1, _>(2, data);
Expand All @@ -102,22 +106,24 @@ impl DataControlState {
#[allow(missing_debug_implementations)]
#[doc(hidden)]
pub struct DataControlManagerGlobalData {
/// Whether to allow primary selection.
primary: bool,
/// Filter whether the clients can read/modify the primary selection.
primary_selection_filter: Arc<Box<dyn for<'c> Fn(&'c Client) -> bool + Send + Sync>>,

/// Filter whether the clients can view global.
filter: Box<dyn for<'c> Fn(&'c Client) -> bool + Send + Sync>,
}

#[allow(missing_debug_implementations)]
#[doc(hidden)]
#[derive(Debug, Clone, Copy)]
#[derive(Clone)]
pub struct DataControlManagerUserData {
/// Whether to allow primary selection.
primary: bool,
/// Filter whether the clients can read/modify the primary selection.
primary_selection_filter: Arc<Box<dyn for<'c> Fn(&'c Client) -> bool + Send + Sync>>,
}

mod handlers {
use std::cell::RefCell;
use std::sync::Arc;

use tracing::error;
use wayland_protocols_wlr::data_control::v1::server::zwlr_data_control_device_v1::ZwlrDataControlDeviceV1;
Expand Down Expand Up @@ -158,7 +164,7 @@ mod handlers {
data_init.init(
resource,
DataControlManagerUserData {
primary: global_data.primary,
primary_selection_filter: Arc::clone(&global_data.primary_selection_filter),
},
);
}
Expand Down Expand Up @@ -199,7 +205,7 @@ mod handlers {
id,
DataControlDeviceUserData {
wl_seat,
primary: data.primary,
primary_selection_filter: Arc::clone(&data.primary_selection_filter),
},
));

Expand All @@ -214,7 +220,7 @@ mod handlers {
// 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 {
if (*data.primary_selection_filter)(client) {
seat_data.send_selection::<D>(dh, SelectionTarget::Primary, device, true);
}
}
Expand Down
Loading