1use std::{
2 ops::{Deref, DerefMut},
3 os::unix::prelude::{AsFd, OwnedFd},
4 sync::{Arc, Mutex},
5};
6
7use log::warn;
8
9use crate::reexports::client::{
10 protocol::{
11 wl_data_device_manager::DndAction,
12 wl_data_offer::{self, WlDataOffer},
13 wl_surface::WlSurface,
14 },
15 Connection, Dispatch, Proxy, QueueHandle,
16};
17
18use super::{DataDeviceManagerState, ReadPipe};
19
20/// Handler trait for DataOffer events.
21///
22/// The functions defined in this trait are called as DataOffer events are received from the compositor.
23pub trait DataOfferHandler: Sized {
24 /// Called to advertise the available DnD Actions as set by the source.
25 fn source_actions(
26 &mut self,
27 conn: &Connection,
28 qh: &QueueHandle<Self>,
29 offer: &mut DragOffer,
30 actions: DndAction,
31 );
32
33 /// Called to advertise the action selected by the compositor after matching
34 /// the source/destination side actions. Only one action or none will be
35 /// selected in the actions sent by the compositor. This may be called
36 /// multiple times during a DnD operation. The most recent DndAction is the
37 /// only valid one.
38 ///
39 /// At the time of a `drop` event on the data device, this action must be
40 /// used except in the case of an ask action. In the case that the last
41 /// action received is `ask`, the destination asks the user for their
42 /// preference, then calls set_actions & accept each one last time. Finally,
43 /// the destination may then request data to be sent and finishing the data
44 /// offer
45 fn selected_action(
46 &mut self,
47 conn: &Connection,
48 qh: &QueueHandle<Self>,
49 offer: &mut DragOffer,
50 actions: DndAction,
51 );
52}
53
54/// An error that may occur when working with data offers.
55#[derive(Debug, thiserror::Error)]
56pub enum DataOfferError {
57 #[error("offer is not valid to receive from yet")]
58 InvalidReceive,
59
60 #[error("IO error")]
61 Io(std::io::Error),
62}
63
64#[derive(Debug, Clone)]
65pub struct DragOffer {
66 /// the wl_data offer if it exists
67 pub(crate) data_offer: WlDataOffer,
68 /// the serial for this data offer's enter event
69 pub serial: u32,
70 /// the surface that this DnD is active on
71 pub surface: WlSurface,
72 /// the x position on the surface
73 pub x: f64,
74 /// the y position on this surface
75 pub y: f64,
76 /// the timestamp a motion event was received in millisecond granularity
77 pub time: Option<u32>,
78 /// the advertised drag actions
79 pub source_actions: DndAction,
80 /// the compositor selected drag action
81 pub selected_action: DndAction,
82 /// whether or not the drag has been dropped
83 pub dropped: bool,
84 /// whether or not the drag has left
85 pub left: bool,
86}
87
88impl DragOffer {
89 pub fn finish(&self) {
90 if self.data_offer.version() >= 3 {
91 self.data_offer.finish();
92 }
93 }
94
95 /// Inspect the mime types available on the given offer.
96 pub fn with_mime_types<T, F: Fn(&[String]) -> T>(&self, callback: F) -> T {
97 let mime_types =
98 &self.data_offer.data::<DataOfferData>().unwrap().inner.lock().unwrap().mime_types;
99 callback(mime_types)
100 }
101
102 /// Set the accepted and preferred drag and drop actions.
103 /// This request determines the final result of the drag-and-drop operation.
104 /// If the end result is that no action is accepted, the drag source will receive wl_data_source.cancelled.
105 pub fn set_actions(&self, actions: DndAction, preferred_action: DndAction) {
106 if self.data_offer.version() >= 3 && !self.left {
107 self.data_offer.set_actions(actions, preferred_action);
108 }
109 }
110
111 /// Receive data with the given mime type.
112 /// This request may happen multiple times for different mime types, both before and after wl_data_device.drop.
113 /// Drag-and-drop destination clients may preemptively fetch data or examine it more closely to determine acceptance.
114 pub fn receive(&self, mime_type: String) -> std::io::Result<ReadPipe> {
115 // When the data device has left, we can't receive unless it was previously dropped.
116 if !self.left || self.dropped {
117 receive(&self.data_offer, mime_type)
118 } else {
119 Err(std::io::Error::new(std::io::ErrorKind::Other, "offer has left"))
120 }
121 }
122
123 /// Accept the given mime type, or None to reject the offer.
124 /// In version 2, this request is used for feedback, but doesn't affect the final result of the drag-and-drop operation.
125 /// In version 3, this request determines the final result of the drag-and-drop operation.
126 pub fn accept_mime_type(&self, serial: u32, mime_type: Option<String>) {
127 if !self.left {
128 self.data_offer.accept(serial, mime_type);
129 }
130 }
131
132 /// Destroy the data offer.
133 pub fn destroy(&self) {
134 self.data_offer.destroy();
135 }
136
137 /// Retrieve a reference to the inner wl_data_offer.
138 pub fn inner(&self) -> &WlDataOffer {
139 &self.data_offer
140 }
141}
142
143impl PartialEq for DragOffer {
144 fn eq(&self, other: &Self) -> bool {
145 self.data_offer == other.data_offer
146 }
147}
148
149#[derive(Debug, Clone)]
150pub struct SelectionOffer {
151 /// the wl_data offer
152 pub(crate) data_offer: WlDataOffer,
153}
154
155impl SelectionOffer {
156 /// Inspect the mime types available on the given offer.
157 pub fn with_mime_types<T, F: Fn(&[String]) -> T>(&self, callback: F) -> T {
158 let mime_types: &[String] =
159 &self.data_offer.data::<DataOfferData>().unwrap().inner.lock().unwrap().mime_types;
160 callback(mime_types)
161 }
162
163 pub fn receive(&self, mime_type: String) -> Result<ReadPipe, DataOfferError> {
164 receive(&self.data_offer, mime_type).map_err(op:DataOfferError::Io)
165 }
166
167 pub fn destroy(&self) {
168 self.data_offer.destroy();
169 }
170
171 pub fn inner(&self) -> &WlDataOffer {
172 &self.data_offer
173 }
174}
175
176impl PartialEq for SelectionOffer {
177 fn eq(&self, other: &Self) -> bool {
178 self.data_offer == other.data_offer
179 }
180}
181
182#[derive(Debug, Default)]
183pub struct DataOfferData {
184 pub(crate) inner: Arc<Mutex<DataDeviceOfferInner>>,
185}
186
187impl DataOfferData {
188 /// Inspect the mime types available on the given offer.
189 pub fn with_mime_types<T, F: Fn(&[String]) -> T>(&self, callback: F) -> T {
190 let mime_types = &self.inner.lock().unwrap().mime_types;
191 callback(mime_types)
192 }
193
194 pub(crate) fn push_mime_type(&self, mime_type: String) {
195 self.inner.lock().unwrap().mime_types.push(mime_type);
196 }
197
198 pub(crate) fn set_source_action(&self, action: DndAction) {
199 let mut inner = self.inner.lock().unwrap();
200 match &mut inner.deref_mut().offer {
201 DataDeviceOffer::Drag(ref mut o) => o.source_actions = action,
202 DataDeviceOffer::Selection(_) => {}
203 DataDeviceOffer::Undetermined(ref mut o) => o.actions = action,
204 };
205 }
206
207 pub(crate) fn set_selected_action(&self, action: DndAction) {
208 let mut inner = self.inner.lock().unwrap();
209 match &mut inner.deref_mut().offer {
210 DataDeviceOffer::Drag(ref mut o) => o.selected_action = action,
211 DataDeviceOffer::Selection(_) => {} // error?
212 DataDeviceOffer::Undetermined(_) => {} // error?
213 };
214 }
215
216 pub(crate) fn to_selection_offer(&self) {
217 let mut inner = self.inner.lock().unwrap();
218 match &mut inner.deref_mut().offer {
219 DataDeviceOffer::Drag(o) => {
220 inner.offer =
221 DataDeviceOffer::Selection(SelectionOffer { data_offer: o.data_offer.clone() });
222 }
223 DataDeviceOffer::Selection(_) => {}
224 DataDeviceOffer::Undetermined(o) => {
225 inner.offer = DataDeviceOffer::Selection(SelectionOffer {
226 data_offer: o.data_offer.clone().unwrap(),
227 });
228 }
229 }
230 }
231
232 pub(crate) fn init_undetermined_offer(&self, offer: &WlDataOffer) {
233 let mut inner = self.inner.lock().unwrap();
234 match &mut inner.deref_mut().offer {
235 DataDeviceOffer::Drag(o) => {
236 inner.offer = DataDeviceOffer::Undetermined(UndeterminedOffer {
237 data_offer: Some(offer.clone()),
238 actions: o.source_actions,
239 });
240 }
241 DataDeviceOffer::Selection(_) => {
242 inner.offer = DataDeviceOffer::Undetermined(UndeterminedOffer {
243 data_offer: Some(offer.clone()),
244 actions: DndAction::empty(),
245 });
246 }
247 DataDeviceOffer::Undetermined(o) => {
248 o.data_offer = Some(offer.clone());
249 }
250 }
251 }
252
253 pub(crate) fn to_dnd_offer(
254 &self,
255 serial: u32,
256 surface: WlSurface,
257 x: f64,
258 y: f64,
259 time: Option<u32>,
260 ) {
261 let mut inner = self.inner.lock().unwrap();
262 match &mut inner.deref_mut().offer {
263 DataDeviceOffer::Drag(_) => {}
264 DataDeviceOffer::Selection(o) => {
265 inner.offer = DataDeviceOffer::Drag(DragOffer {
266 data_offer: o.data_offer.clone(),
267 source_actions: DndAction::empty(),
268 selected_action: DndAction::empty(),
269 serial,
270 surface,
271 x,
272 y,
273 time,
274 dropped: false,
275 left: false,
276 });
277 }
278 DataDeviceOffer::Undetermined(o) => {
279 inner.offer = DataDeviceOffer::Drag(DragOffer {
280 data_offer: o.data_offer.clone().unwrap(),
281 source_actions: o.actions,
282 selected_action: DndAction::empty(),
283 serial,
284 surface,
285 x,
286 y,
287 time,
288 dropped: false,
289 left: false,
290 });
291 }
292 }
293 }
294
295 pub(crate) fn motion(&self, x: f64, y: f64, time: u32) {
296 let mut inner = self.inner.lock().unwrap();
297 match &mut inner.deref_mut().offer {
298 DataDeviceOffer::Drag(o) => {
299 o.x = x;
300 o.y = y;
301 o.time = Some(time);
302 }
303 DataDeviceOffer::Selection(_) => {}
304 DataDeviceOffer::Undetermined(_) => {}
305 }
306 }
307
308 pub(crate) fn as_drag_offer(&self) -> Option<DragOffer> {
309 match &self.inner.lock().unwrap().deref().offer {
310 DataDeviceOffer::Drag(o) => Some(o.clone()),
311 _ => None,
312 }
313 }
314
315 pub(crate) fn leave(&self) -> bool {
316 let mut inner = self.inner.lock().unwrap();
317 match &mut inner.deref_mut().offer {
318 DataDeviceOffer::Drag(o) => {
319 o.left = true;
320 if !o.dropped {
321 o.data_offer.destroy();
322 }
323 !o.dropped
324 }
325 _ => {
326 warn!("DataDeviceOffer::leave called on non-drag offer");
327 false
328 }
329 }
330 }
331
332 pub(crate) fn as_selection_offer(&self) -> Option<SelectionOffer> {
333 match &self.inner.lock().unwrap().deref().offer {
334 DataDeviceOffer::Selection(o) => Some(o.clone()),
335 _ => None,
336 }
337 }
338}
339
340#[derive(Debug, Default)]
341pub struct DataDeviceOfferInner {
342 pub(crate) offer: DataDeviceOffer,
343 pub(crate) mime_types: Vec<String>,
344}
345
346#[derive(Debug, Clone, PartialEq)]
347pub(crate) enum DataDeviceOffer {
348 Drag(DragOffer),
349 Selection(SelectionOffer),
350 Undetermined(UndeterminedOffer),
351}
352
353impl Default for DataDeviceOffer {
354 fn default() -> Self {
355 DataDeviceOffer::Undetermined(UndeterminedOffer {
356 data_offer: None,
357 actions: DndAction::empty(),
358 })
359 }
360}
361
362impl<D> Dispatch<wl_data_offer::WlDataOffer, DataOfferData, D> for DataDeviceManagerState
363where
364 D: Dispatch<wl_data_offer::WlDataOffer, DataOfferData> + DataOfferHandler,
365{
366 fn event(
367 state: &mut D,
368 _offer: &wl_data_offer::WlDataOffer,
369 event: <wl_data_offer::WlDataOffer as wayland_client::Proxy>::Event,
370 data: &DataOfferData,
371 conn: &wayland_client::Connection,
372 qh: &wayland_client::QueueHandle<D>,
373 ) {
374 match event {
375 wl_data_offer::Event::Offer { mime_type } => {
376 data.push_mime_type(mime_type);
377 }
378 wl_data_offer::Event::SourceActions { source_actions } => {
379 match source_actions {
380 wayland_client::WEnum::Value(a) => {
381 data.set_source_action(a);
382 match &mut data.inner.lock().unwrap().offer {
383 DataDeviceOffer::Drag(o) => {
384 state.source_actions(conn, qh, o, a);
385 }
386 DataDeviceOffer::Selection(_) => {}
387 DataDeviceOffer::Undetermined(_) => {}
388 }
389 }
390 wayland_client::WEnum::Unknown(_) => {} // Ignore
391 }
392 }
393 wl_data_offer::Event::Action { dnd_action } => {
394 match dnd_action {
395 wayland_client::WEnum::Value(a) => {
396 data.set_selected_action(a);
397 match &mut data.inner.lock().unwrap().offer {
398 DataDeviceOffer::Drag(o) => {
399 state.selected_action(conn, qh, o, a);
400 }
401 DataDeviceOffer::Selection(_) => {}
402 DataDeviceOffer::Undetermined(_) => {}
403 }
404 }
405 wayland_client::WEnum::Unknown(_) => {} // Ignore
406 }
407 }
408 _ => unimplemented!(),
409 };
410 }
411}
412
413#[derive(Debug, Clone)]
414pub(crate) struct UndeterminedOffer {
415 pub(crate) data_offer: Option<WlDataOffer>,
416 pub actions: DndAction,
417}
418
419impl PartialEq for UndeterminedOffer {
420 fn eq(&self, other: &Self) -> bool {
421 self.data_offer == other.data_offer
422 }
423}
424
425/// Request to receive the data of a given mime type.
426///
427/// You can do this several times, as a reaction to motion of
428/// the dnd cursor, or to inspect the data in order to choose your
429/// response.
430///
431/// Note that you should *not* read the contents right away in a
432/// blocking way, as you may deadlock your application doing so.
433/// At least make sure you flush your events to the server before
434/// doing so.
435///
436/// Fails if too many file descriptors were already open and a pipe
437/// could not be created.
438pub fn receive(offer: &WlDataOffer, mime_type: String) -> std::io::Result<ReadPipe> {
439 use rustix::pipe::{pipe_with, PipeFlags};
440 // create a pipe
441 let (readfd: OwnedFd, writefd: OwnedFd) = pipe_with(flags:PipeFlags::CLOEXEC)?;
442
443 receive_to_fd(offer, mime_type, writefd);
444
445 Ok(ReadPipe::from(readfd))
446}
447
448/// Receive data to the write end of a raw file descriptor. If you have the read end, you can read from it.
449///
450/// You can do this several times, as a reaction to motion of
451/// the dnd cursor, or to inspect the data in order to choose your
452/// response.
453///
454/// Note that you should *not* read the contents right away in a
455/// blocking way, as you may deadlock your application doing so.
456/// At least make sure you flush your events to the server before
457/// doing so.
458///
459/// The provided file destructor must be a valid FD for writing, and will be closed
460/// once the contents are written.
461pub fn receive_to_fd(offer: &WlDataOffer, mime_type: String, writefd: OwnedFd) {
462 offer.receive(mime_type, writefd.as_fd());
463}
464