Skip to content

selection: instantly notify clients when source dies #1762

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
12 changes: 6 additions & 6 deletions anvil/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,8 @@ pub struct DndIcon {
delegate_compositor!(@<BackendData: Backend + 'static> AnvilState<BackendData>);

impl<BackendData: Backend> DataDeviceHandler for AnvilState<BackendData> {
fn data_device_state(&self) -> &DataDeviceState {
&self.data_device_state
fn data_device_state(&mut self) -> &mut DataDeviceState {
&mut self.data_device_state
}
}

Expand Down Expand Up @@ -255,15 +255,15 @@ impl<BackendData: Backend> SelectionHandler for AnvilState<BackendData> {
}

impl<BackendData: Backend> PrimarySelectionHandler for AnvilState<BackendData> {
fn primary_selection_state(&self) -> &PrimarySelectionState {
&self.primary_selection_state
fn primary_selection_state(&mut self) -> &mut PrimarySelectionState {
&mut self.primary_selection_state
}
}
delegate_primary_selection!(@<BackendData: Backend + 'static> AnvilState<BackendData>);

impl<BackendData: Backend> DataControlHandler for AnvilState<BackendData> {
fn data_control_state(&self) -> &DataControlState {
&self.data_control_state
fn data_control_state(&mut self) -> &mut DataControlState {
&mut self.data_control_state
}
}

Expand Down
4 changes: 2 additions & 2 deletions examples/minimal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ impl SelectionHandler for App {
}

impl DataDeviceHandler for App {
fn data_device_state(&self) -> &DataDeviceState {
&self.data_device_state
fn data_device_state(&mut self) -> &mut DataDeviceState {
&mut self.data_device_state
}
}

Expand Down
4 changes: 2 additions & 2 deletions smallvil/src/handlers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ impl SelectionHandler for Smallvil {
}

impl DataDeviceHandler for Smallvil {
fn data_device_state(&self) -> &DataDeviceState {
&self.data_device_state
fn data_device_state(&mut self) -> &mut DataDeviceState {
&mut self.data_device_state
}
}

Expand Down
33 changes: 33 additions & 0 deletions src/wayland/selection/data_device/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,22 @@ where
icon,
serial,
} => {
// Each source can only be used once.
if let Some(source) = source.as_ref() {
if handler
.data_device_state()
.used_sources
.insert(source.clone(), data.wl_seat.clone())
.is_some()
{
resource.post_error(
wl_data_device::Error::UsedSource,
"selection source can be used only once.",
);
return;
}
}

let serial = Serial::from(serial);
if let Some(pointer) = seat.get_pointer() {
if pointer.has_grab(serial) {
Expand Down Expand Up @@ -129,6 +145,23 @@ where
return;
}
};

// Each source can only be used once.
if let Some(source) = source.as_ref() {
if handler
.data_device_state()
.used_sources
.insert(source.clone(), data.wl_seat.clone())
.is_some()
{
resource.post_error(
wl_data_device::Error::UsedSource,
"selection source can be used only once.",
);
return;
}
}

let source = source.map(SelectionSourceProvider::DataDevice);

handler.new_selection(
Expand Down
20 changes: 15 additions & 5 deletions src/wayland/selection/data_device/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
//! type SelectionUserData = ();
//! }
//! impl DataDeviceHandler for State {
//! fn data_device_state(&self) -> &DataDeviceState { &self.data_device_state }
//! fn data_device_state(&mut self) -> &mut DataDeviceState { &mut self.data_device_state }
//! // ... override default implementations here to customize handling ...
//! }
//! delegate_data_device!(State);
Expand All @@ -70,6 +70,7 @@

use std::{
cell::{Ref, RefCell},
collections::HashMap,
os::unix::io::OwnedFd,
};

Expand All @@ -79,6 +80,7 @@ use wayland_server::{
protocol::{
wl_data_device_manager::{DndAction, WlDataDeviceManager},
wl_data_source::WlDataSource,
wl_seat::WlSeat,
wl_surface::WlSurface,
},
Client, DisplayHandle, GlobalDispatch,
Expand Down Expand Up @@ -113,7 +115,7 @@ use super::{
#[allow(unused_variables)]
pub trait DataDeviceHandler: Sized + SelectionHandler + ClientDndGrabHandler + ServerDndGrabHandler {
/// [DataDeviceState] getter
fn data_device_state(&self) -> &DataDeviceState;
fn data_device_state(&mut self) -> &mut DataDeviceState;

/// Action chooser for DnD negociation
fn action_choice(&mut self, available: DndAction, preferred: DndAction) -> DndAction {
Expand Down Expand Up @@ -195,6 +197,11 @@ pub trait ServerDndGrabHandler: SeatHandler {
#[derive(Debug)]
pub struct DataDeviceState {
manager_global: GlobalId,
/// Used sources.
///
/// Protocol states that each source can only be used once. We
/// also use it during destruction to get seat data.
pub(crate) used_sources: HashMap<WlDataSource, WlSeat>,
}

impl DataDeviceState {
Expand All @@ -206,7 +213,10 @@ impl DataDeviceState {
{
let manager_global = display.create_global::<D, WlDataDeviceManager, _>(3, ());

Self { manager_global }
Self {
manager_global,
used_sources: Default::default(),
}
}

/// [WlDataDeviceManager] GlobalId getter
Expand Down Expand Up @@ -467,12 +477,12 @@ mod handlers {
_resource: &WlDataDeviceManager,
request: wl_data_device_manager::Request,
_data: &(),
_dhandle: &DisplayHandle,
dhandle: &DisplayHandle,
data_init: &mut wayland_server::DataInit<'_, D>,
) {
match request {
wl_data_device_manager::Request::CreateDataSource { id } => {
data_init.init(id, DataSourceUserData::new());
data_init.init(id, DataSourceUserData::new(dhandle.clone()));
}
wl_data_device_manager::Request::GetDataDevice { id, seat: wl_seat } => {
match Seat::<D>::from_resource(&wl_seat) {
Expand Down
39 changes: 36 additions & 3 deletions src/wayland/selection/data_device/source.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::sync::Mutex;
use std::{cell::RefCell, sync::Mutex};
use tracing::error;

use wayland_server::{
Expand All @@ -8,7 +8,11 @@ use wayland_server::{
Dispatch, DisplayHandle, Resource,
};

use crate::input::Seat;
use crate::utils::{alive_tracker::AliveTracker, IsAlive};
use crate::wayland::selection::offer::OfferReplySource;
use crate::wayland::selection::seat_data::SeatData;
use crate::wayland::selection::source::SelectionSourceProvider;

use super::{DataDeviceHandler, DataDeviceState};

Expand All @@ -35,13 +39,15 @@ impl Default for SourceMetadata {
pub struct DataSourceUserData {
pub(crate) inner: Mutex<SourceMetadata>,
alive_tracker: AliveTracker,
display_handle: DisplayHandle,
}

impl DataSourceUserData {
pub(super) fn new() -> Self {
pub(super) fn new(display_handle: DisplayHandle) -> Self {
Self {
inner: Default::default(),
alive_tracker: Default::default(),
display_handle,
}
}
}
Expand Down Expand Up @@ -80,8 +86,35 @@ where
}
}

fn destroyed(_state: &mut D, _client: ClientId, _resource: &WlDataSource, data: &DataSourceUserData) {
fn destroyed(state: &mut D, _client: ClientId, source: &WlDataSource, data: &DataSourceUserData) {
data.alive_tracker.destroy_notify();

// Remove the source from the used ones.
let seat = match state
.data_device_state()
.used_sources
.remove(source)
.as_ref()
.and_then(Seat::<D>::from_resource)
{
Some(seat) => seat,
None => return,
};

let mut seat_data = seat
.user_data()
.get::<RefCell<SeatData<D::SelectionUserData>>>()
.unwrap()
.borrow_mut();

match seat_data.get_clipboard_selection() {
Some(OfferReplySource::Client(SelectionSourceProvider::DataDevice(set_source)))
if set_source == source =>
{
seat_data.set_clipboard_selection::<D>(&data.display_handle, None)
}
_ => (),
}
}
}

Expand Down
17 changes: 17 additions & 0 deletions src/wayland/selection/ext_data_control/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ 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::Resource;
use wayland_server::{Client, Dispatch, DisplayHandle};

use crate::input::Seat;
Expand Down Expand Up @@ -44,6 +45,22 @@ where

match request {
ext_data_control_device_v1::Request::SetSelection { source, .. } => {
// Each source can only be used once.
if let Some(source) = source.as_ref() {
if handler
.data_control_state()
.used_sources
.insert(source.clone(), data.wl_seat.clone())
.is_some()
{
resource.post_error(
ext_data_control_device_v1::Error::UsedSource,
"selection source can be used only once.",
);
return;
}
}

seat.user_data()
.insert_if_missing(|| RefCell::new(SeatData::<D::SelectionUserData>::new()));

Expand Down
20 changes: 16 additions & 4 deletions src/wayland/selection/ext_data_control/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
//! type SelectionUserData = ();
//! }
//! impl DataControlHandler for State {
//! fn data_control_state(&self) -> &DataControlState { &self.data_control_state }
//! fn data_control_state(&mut self) -> &mut DataControlState { &mut self.data_control_state }
//! // ... override default implementations here to customize handling ...
//! }
//! delegate_ext_data_control!(State);
Expand All @@ -47,8 +47,12 @@
//! Be aware that data control clients rely on other selection providers to be implemneted, like
//! wl_data_device or zwp_primary_selection.

use std::collections::HashMap;

use wayland_protocols::ext::data_control::v1::server::ext_data_control_manager_v1::ExtDataControlManagerV1;
use wayland_protocols::ext::data_control::v1::server::ext_data_control_source_v1::ExtDataControlSourceV1;
use wayland_server::backend::GlobalId;
use wayland_server::protocol::wl_seat::WlSeat;
use wayland_server::{Client, DisplayHandle, GlobalDispatch};

mod device;
Expand All @@ -63,13 +67,18 @@ use super::SelectionHandler;
/// Access the data control state.
pub trait DataControlHandler: Sized + SelectionHandler {
/// [`DataControlState`] getter.
fn data_control_state(&self) -> &DataControlState;
fn data_control_state(&mut self) -> &mut DataControlState;
}

/// State of the data control.
#[derive(Debug)]
pub struct DataControlState {
manager_global: GlobalId,
/// Used sources.
///
/// Protocol states that each source can only be used once. We
/// also use it during destruction to get seat data.
pub(crate) used_sources: HashMap<ExtDataControlSourceV1, WlSeat>,
}

impl DataControlState {
Expand All @@ -90,7 +99,10 @@ impl DataControlState {
filter: Box::new(filter),
};
let manager_global = display.create_global::<D, ExtDataControlManagerV1, _>(1, data);
Self { manager_global }
Self {
manager_global,
used_sources: Default::default(),
}
}

/// [ExtDataControlManagerV1] GlobalId getter.
Expand Down Expand Up @@ -188,7 +200,7 @@ mod handlers {
) {
match request {
ext_data_control_manager_v1::Request::CreateDataSource { id } => {
data_init.init(id, ExtDataControlSourceUserData::new());
data_init.init(id, ExtDataControlSourceUserData::new(dh.clone()));
}
ext_data_control_manager_v1::Request::GetDataDevice { id, seat: wl_seat } => {
match Seat::<D>::from_resource(&wl_seat) {
Expand Down
Loading
Loading