1 | use std::{ |
2 | ops::{Deref, DerefMut}, |
3 | os::unix::prelude::{AsFd, OwnedFd}, |
4 | sync::{Arc, Mutex}, |
5 | }; |
6 | |
7 | use log::warn; |
8 | |
9 | use 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 | |
18 | use 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. |
23 | pub 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)] |
56 | pub 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)] |
65 | pub 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 | |
88 | impl 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 | |
143 | impl PartialEq for DragOffer { |
144 | fn eq(&self, other: &Self) -> bool { |
145 | self.data_offer == other.data_offer |
146 | } |
147 | } |
148 | |
149 | #[derive (Debug, Clone)] |
150 | pub struct SelectionOffer { |
151 | /// the wl_data offer |
152 | pub(crate) data_offer: WlDataOffer, |
153 | } |
154 | |
155 | impl 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 | |
176 | impl PartialEq for SelectionOffer { |
177 | fn eq(&self, other: &Self) -> bool { |
178 | self.data_offer == other.data_offer |
179 | } |
180 | } |
181 | |
182 | #[derive (Debug, Default)] |
183 | pub struct DataOfferData { |
184 | pub(crate) inner: Arc<Mutex<DataDeviceOfferInner>>, |
185 | } |
186 | |
187 | impl 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)] |
341 | pub struct DataDeviceOfferInner { |
342 | pub(crate) offer: DataDeviceOffer, |
343 | pub(crate) mime_types: Vec<String>, |
344 | } |
345 | |
346 | #[derive (Debug, Clone, PartialEq)] |
347 | pub(crate) enum DataDeviceOffer { |
348 | Drag(DragOffer), |
349 | Selection(SelectionOffer), |
350 | Undetermined(UndeterminedOffer), |
351 | } |
352 | |
353 | impl Default for DataDeviceOffer { |
354 | fn default() -> Self { |
355 | DataDeviceOffer::Undetermined(UndeterminedOffer { |
356 | data_offer: None, |
357 | actions: DndAction::empty(), |
358 | }) |
359 | } |
360 | } |
361 | |
362 | impl<D> Dispatch<wl_data_offer::WlDataOffer, DataOfferData, D> for DataDeviceManagerState |
363 | where |
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)] |
414 | pub(crate) struct UndeterminedOffer { |
415 | pub(crate) data_offer: Option<WlDataOffer>, |
416 | pub actions: DndAction, |
417 | } |
418 | |
419 | impl 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. |
438 | pub 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. |
461 | pub fn receive_to_fd(offer: &WlDataOffer, mime_type: String, writefd: OwnedFd) { |
462 | offer.receive(mime_type, writefd.as_fd()); |
463 | } |
464 | |