Skip to content

Commit c521b10

Browse files
committed
selection: instantly notify clients when source dies
Smithay uses lazy notify when there's actual interaction, which is unexpected and doesn't properly reflect the state of the clipboard to clipboard managers. Instead immediately notify clients when the source was destroyed by sending `null`. Links: YaLTeR/niri#1831
1 parent 52107d3 commit c521b10

File tree

16 files changed

+327
-115
lines changed

16 files changed

+327
-115
lines changed

anvil/src/state.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,8 @@ pub struct DndIcon {
188188
delegate_compositor!(@<BackendData: Backend + 'static> AnvilState<BackendData>);
189189

190190
impl<BackendData: Backend> DataDeviceHandler for AnvilState<BackendData> {
191-
fn data_device_state(&self) -> &DataDeviceState {
192-
&self.data_device_state
191+
fn data_device_state(&mut self) -> &mut DataDeviceState {
192+
&mut self.data_device_state
193193
}
194194
}
195195

@@ -255,15 +255,15 @@ impl<BackendData: Backend> SelectionHandler for AnvilState<BackendData> {
255255
}
256256

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

264264
impl<BackendData: Backend> DataControlHandler for AnvilState<BackendData> {
265-
fn data_control_state(&self) -> &DataControlState {
266-
&self.data_control_state
265+
fn data_control_state(&mut self) -> &mut DataControlState {
266+
&mut self.data_control_state
267267
}
268268
}
269269

examples/minimal.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ impl SelectionHandler for App {
7777
}
7878

7979
impl DataDeviceHandler for App {
80-
fn data_device_state(&self) -> &DataDeviceState {
81-
&self.data_device_state
80+
fn data_device_state(&mut self) -> &mut DataDeviceState {
81+
&mut self.data_device_state
8282
}
8383
}
8484

smallvil/src/handlers/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ impl SelectionHandler for Smallvil {
4646
}
4747

4848
impl DataDeviceHandler for Smallvil {
49-
fn data_device_state(&self) -> &DataDeviceState {
50-
&self.data_device_state
49+
fn data_device_state(&mut self) -> &mut DataDeviceState {
50+
&mut self.data_device_state
5151
}
5252
}
5353

src/wayland/selection/data_device/device.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,22 @@ where
6767
icon,
6868
serial,
6969
} => {
70+
// Each source can only be used once.
71+
if let Some(source) = source.as_ref() {
72+
if handler
73+
.data_device_state()
74+
.used_sources
75+
.insert(source.clone(), data.wl_seat.clone())
76+
.is_some()
77+
{
78+
resource.post_error(
79+
wl_data_device::Error::UsedSource,
80+
"selection source can be used only once.",
81+
);
82+
return;
83+
}
84+
}
85+
7086
let serial = Serial::from(serial);
7187
if let Some(pointer) = seat.get_pointer() {
7288
if pointer.has_grab(serial) {
@@ -129,6 +145,23 @@ where
129145
return;
130146
}
131147
};
148+
149+
// Each source can only be used once.
150+
if let Some(source) = source.as_ref() {
151+
if handler
152+
.data_device_state()
153+
.used_sources
154+
.insert(source.clone(), data.wl_seat.clone())
155+
.is_some()
156+
{
157+
resource.post_error(
158+
wl_data_device::Error::UsedSource,
159+
"selection source can be used only once.",
160+
);
161+
return;
162+
}
163+
}
164+
132165
let source = source.map(SelectionSourceProvider::DataDevice);
133166

134167
handler.new_selection(

src/wayland/selection/data_device/mod.rs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
//! type SelectionUserData = ();
6161
//! }
6262
//! impl DataDeviceHandler for State {
63-
//! fn data_device_state(&self) -> &DataDeviceState { &self.data_device_state }
63+
//! fn data_device_state(&mut self) -> &mut DataDeviceState { &mut self.data_device_state }
6464
//! // ... override default implementations here to customize handling ...
6565
//! }
6666
//! delegate_data_device!(State);
@@ -70,6 +70,7 @@
7070
7171
use std::{
7272
cell::{Ref, RefCell},
73+
collections::HashMap,
7374
os::unix::io::OwnedFd,
7475
};
7576

@@ -79,6 +80,7 @@ use wayland_server::{
7980
protocol::{
8081
wl_data_device_manager::{DndAction, WlDataDeviceManager},
8182
wl_data_source::WlDataSource,
83+
wl_seat::WlSeat,
8284
wl_surface::WlSurface,
8385
},
8486
Client, DisplayHandle, GlobalDispatch,
@@ -113,7 +115,7 @@ use super::{
113115
#[allow(unused_variables)]
114116
pub trait DataDeviceHandler: Sized + SelectionHandler + ClientDndGrabHandler + ServerDndGrabHandler {
115117
/// [DataDeviceState] getter
116-
fn data_device_state(&self) -> &DataDeviceState;
118+
fn data_device_state(&mut self) -> &mut DataDeviceState;
117119

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

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

209-
Self { manager_global }
216+
Self {
217+
manager_global,
218+
used_sources: Default::default(),
219+
}
210220
}
211221

212222
/// [WlDataDeviceManager] GlobalId getter
@@ -467,12 +477,12 @@ mod handlers {
467477
_resource: &WlDataDeviceManager,
468478
request: wl_data_device_manager::Request,
469479
_data: &(),
470-
_dhandle: &DisplayHandle,
480+
dhandle: &DisplayHandle,
471481
data_init: &mut wayland_server::DataInit<'_, D>,
472482
) {
473483
match request {
474484
wl_data_device_manager::Request::CreateDataSource { id } => {
475-
data_init.init(id, DataSourceUserData::new());
485+
data_init.init(id, DataSourceUserData::new(dhandle.clone()));
476486
}
477487
wl_data_device_manager::Request::GetDataDevice { id, seat: wl_seat } => {
478488
match Seat::<D>::from_resource(&wl_seat) {

src/wayland/selection/data_device/source.rs

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::sync::Mutex;
1+
use std::{cell::RefCell, sync::Mutex};
22
use tracing::error;
33

44
use wayland_server::{
@@ -8,7 +8,11 @@ use wayland_server::{
88
Dispatch, DisplayHandle, Resource,
99
};
1010

11+
use crate::input::Seat;
1112
use crate::utils::{alive_tracker::AliveTracker, IsAlive};
13+
use crate::wayland::selection::offer::OfferReplySource;
14+
use crate::wayland::selection::seat_data::SeatData;
15+
use crate::wayland::selection::source::SelectionSourceProvider;
1216

1317
use super::{DataDeviceHandler, DataDeviceState};
1418

@@ -35,13 +39,15 @@ impl Default for SourceMetadata {
3539
pub struct DataSourceUserData {
3640
pub(crate) inner: Mutex<SourceMetadata>,
3741
alive_tracker: AliveTracker,
42+
display_handle: DisplayHandle,
3843
}
3944

4045
impl DataSourceUserData {
41-
pub(super) fn new() -> Self {
46+
pub(super) fn new(display_handle: DisplayHandle) -> Self {
4247
Self {
4348
inner: Default::default(),
4449
alive_tracker: Default::default(),
50+
display_handle,
4551
}
4652
}
4753
}
@@ -80,8 +86,35 @@ where
8086
}
8187
}
8288

83-
fn destroyed(_state: &mut D, _client: ClientId, _resource: &WlDataSource, data: &DataSourceUserData) {
89+
fn destroyed(state: &mut D, _client: ClientId, source: &WlDataSource, data: &DataSourceUserData) {
8490
data.alive_tracker.destroy_notify();
91+
92+
// Remove the source from the used ones.
93+
let seat = match state
94+
.data_device_state()
95+
.used_sources
96+
.remove(source)
97+
.as_ref()
98+
.and_then(Seat::<D>::from_resource)
99+
{
100+
Some(seat) => seat,
101+
None => return,
102+
};
103+
104+
let mut seat_data = seat
105+
.user_data()
106+
.get::<RefCell<SeatData<D::SelectionUserData>>>()
107+
.unwrap()
108+
.borrow_mut();
109+
110+
match seat_data.get_clipboard_selection() {
111+
Some(OfferReplySource::Client(SelectionSourceProvider::DataDevice(set_source)))
112+
if set_source == source =>
113+
{
114+
seat_data.set_clipboard_selection::<D>(&data.display_handle, None)
115+
}
116+
_ => (),
117+
}
85118
}
86119
}
87120

src/wayland/selection/ext_data_control/device.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use wayland_protocols::ext::data_control::v1::server::ext_data_control_device_v1
44
self, ExtDataControlDeviceV1,
55
};
66
use wayland_server::protocol::wl_seat::WlSeat;
7+
use wayland_server::Resource;
78
use wayland_server::{Client, Dispatch, DisplayHandle};
89

910
use crate::input::Seat;
@@ -44,6 +45,22 @@ where
4445

4546
match request {
4647
ext_data_control_device_v1::Request::SetSelection { source, .. } => {
48+
// Each source can only be used once.
49+
if let Some(source) = source.as_ref() {
50+
if handler
51+
.data_control_state()
52+
.used_sources
53+
.insert(source.clone(), data.wl_seat.clone())
54+
.is_some()
55+
{
56+
resource.post_error(
57+
ext_data_control_device_v1::Error::UsedSource,
58+
"selection source can be used only once.",
59+
);
60+
return;
61+
}
62+
}
63+
4764
seat.user_data()
4865
.insert_if_missing(|| RefCell::new(SeatData::<D::SelectionUserData>::new()));
4966

src/wayland/selection/ext_data_control/mod.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
//! type SelectionUserData = ();
3737
//! }
3838
//! impl DataControlHandler for State {
39-
//! fn data_control_state(&self) -> &DataControlState { &self.data_control_state }
39+
//! fn data_control_state(&mut self) -> &mut DataControlState { &mut self.data_control_state }
4040
//! // ... override default implementations here to customize handling ...
4141
//! }
4242
//! delegate_ext_data_control!(State);
@@ -47,8 +47,12 @@
4747
//! Be aware that data control clients rely on other selection providers to be implemneted, like
4848
//! wl_data_device or zwp_primary_selection.
4949
50+
use std::collections::HashMap;
51+
5052
use wayland_protocols::ext::data_control::v1::server::ext_data_control_manager_v1::ExtDataControlManagerV1;
53+
use wayland_protocols::ext::data_control::v1::server::ext_data_control_source_v1::ExtDataControlSourceV1;
5154
use wayland_server::backend::GlobalId;
55+
use wayland_server::protocol::wl_seat::WlSeat;
5256
use wayland_server::{Client, DisplayHandle, GlobalDispatch};
5357

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

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

7584
impl DataControlState {
@@ -90,7 +99,10 @@ impl DataControlState {
9099
filter: Box::new(filter),
91100
};
92101
let manager_global = display.create_global::<D, ExtDataControlManagerV1, _>(1, data);
93-
Self { manager_global }
102+
Self {
103+
manager_global,
104+
used_sources: Default::default(),
105+
}
94106
}
95107

96108
/// [ExtDataControlManagerV1] GlobalId getter.
@@ -188,7 +200,7 @@ mod handlers {
188200
) {
189201
match request {
190202
ext_data_control_manager_v1::Request::CreateDataSource { id } => {
191-
data_init.init(id, ExtDataControlSourceUserData::new());
203+
data_init.init(id, ExtDataControlSourceUserData::new(dh.clone()));
192204
}
193205
ext_data_control_manager_v1::Request::GetDataDevice { id, seat: wl_seat } => {
194206
match Seat::<D>::from_resource(&wl_seat) {

0 commit comments

Comments
 (0)