1use std::{
2 ops::{Deref, DerefMut},
3 os::unix::prelude::{AsFd, OwnedFd},
4 sync::{Arc, Mutex},
5};
67use log::warn;
89use 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};
1718use super::{DataDeviceManagerState, ReadPipe};
1920/// 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.
25fn source_actions(
26&mut self,
27 conn: &Connection,
28 qh: &QueueHandle<Self>,
29 offer: &mut DragOffer,
30 actions: DndAction,
31 );
3233/// 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
45fn selected_action(
46&mut self,
47 conn: &Connection,
48 qh: &QueueHandle<Self>,
49 offer: &mut DragOffer,
50 actions: DndAction,
51 );
52}
5354/// 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")]
58InvalidReceive,
5960#[error("IO error")]
61Io(std::io::Error),
62}
6364#[derive(Debug, Clone)]
65pub struct DragOffer {
66/// the wl_data offer if it exists
67pub(crate) data_offer: WlDataOffer,
68/// the serial for this data offer's enter event
69pub serial: u32,
70/// the surface that this DnD is active on
71pub surface: WlSurface,
72/// the x position on the surface
73pub x: f64,
74/// the y position on this surface
75pub y: f64,
76/// the timestamp a motion event was received in millisecond granularity
77pub time: Option<u32>,
78/// the advertised drag actions
79pub source_actions: DndAction,
80/// the compositor selected drag action
81pub selected_action: DndAction,
82/// whether or not the drag has been dropped
83pub dropped: bool,
84/// whether or not the drag has left
85pub left: bool,
86}
8788impl DragOffer {
89pub fn finish(&self) {
90if self.data_offer.version() >= 3 {
91self.data_offer.finish();
92 }
93 }
9495/// Inspect the mime types available on the given offer.
96pub fn with_mime_types<T, F: Fn(&[String]) -> T>(&self, callback: F) -> T {
97let mime_types =
98&self.data_offer.data::<DataOfferData>().unwrap().inner.lock().unwrap().mime_types;
99 callback(mime_types)
100 }
101102/// 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.
105pub fn set_actions(&self, actions: DndAction, preferred_action: DndAction) {
106if self.data_offer.version() >= 3 && !self.left {
107self.data_offer.set_actions(actions, preferred_action);
108 }
109 }
110111/// 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.
114pub 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.
116if !self.left || self.dropped {
117 receive(&self.data_offer, mime_type)
118 } else {
119Err(std::io::Error::new(std::io::ErrorKind::Other, "offer has left"))
120 }
121 }
122123/// 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.
126pub fn accept_mime_type(&self, serial: u32, mime_type: Option<String>) {
127if !self.left {
128self.data_offer.accept(serial, mime_type);
129 }
130 }
131132/// Destroy the data offer.
133pub fn destroy(&self) {
134self.data_offer.destroy();
135 }
136137/// Retrieve a reference to the inner wl_data_offer.
138pub fn inner(&self) -> &WlDataOffer {
139&self.data_offer
140 }
141}
142143impl PartialEq for DragOffer {
144fn eq(&self, other: &Self) -> bool {
145self.data_offer == other.data_offer
146 }
147}
148149#[derive(Debug, Clone)]
150pub struct SelectionOffer {
151/// the wl_data offer
152pub(crate) data_offer: WlDataOffer,
153}
154155impl SelectionOffer {
156/// Inspect the mime types available on the given offer.
157pub fn with_mime_types<T, F: Fn(&[String]) -> T>(&self, callback: F) -> T {
158let mime_types =
159&self.data_offer.data::<DataOfferData>().unwrap().inner.lock().unwrap().mime_types;
160 callback(mime_types)
161 }
162163pub fn receive(&self, mime_type: String) -> Result<ReadPipe, DataOfferError> {
164 receive(&self.data_offer, mime_type).map_err(DataOfferError::Io)
165 }
166167pub fn destroy(&self) {
168self.data_offer.destroy();
169 }
170171pub fn inner(&self) -> &WlDataOffer {
172&self.data_offer
173 }
174}
175176impl PartialEq for SelectionOffer {
177fn eq(&self, other: &Self) -> bool {
178self.data_offer == other.data_offer
179 }
180}
181182#[derive(Debug, Default)]
183pub struct DataOfferData {
184pub(crate) inner: Arc<Mutex<DataDeviceOfferInner>>,
185}
186187impl DataOfferData {
188/// Inspect the mime types available on the given offer.
189pub fn with_mime_types<T, F: Fn(&[String]) -> T>(&self, callback: F) -> T {
190let mime_types = &self.inner.lock().unwrap().mime_types;
191 callback(mime_types)
192 }
193194pub(crate) fn push_mime_type(&self, mime_type: String) {
195self.inner.lock().unwrap().mime_types.push(mime_type);
196 }
197198pub(crate) fn set_source_action(&self, action: DndAction) {
199let mut inner = self.inner.lock().unwrap();
200match &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 }
206207pub(crate) fn set_selected_action(&self, action: DndAction) {
208let mut inner = self.inner.lock().unwrap();
209match &mut inner.deref_mut().offer {
210 DataDeviceOffer::Drag(ref mut o) => o.selected_action = action,
211 DataDeviceOffer::Selection(_) => {} // error?
212DataDeviceOffer::Undetermined(_) => {} // error?
213};
214 }
215216pub(crate) fn to_selection_offer(&self) {
217let mut inner = self.inner.lock().unwrap();
218match &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 }
231232pub(crate) fn init_undetermined_offer(&self, offer: &WlDataOffer) {
233let mut inner = self.inner.lock().unwrap();
234match &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 }
252253pub(crate) fn to_dnd_offer(
254&self,
255 serial: u32,
256 surface: WlSurface,
257 x: f64,
258 y: f64,
259 time: Option<u32>,
260 ) {
261let mut inner = self.inner.lock().unwrap();
262match &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 }
294295pub(crate) fn motion(&self, x: f64, y: f64, time: u32) {
296let mut inner = self.inner.lock().unwrap();
297match &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 }
307308pub(crate) fn as_drag_offer(&self) -> Option<DragOffer> {
309match &self.inner.lock().unwrap().deref().offer {
310 DataDeviceOffer::Drag(o) => Some(o.clone()),
311_ => None,
312 }
313 }
314315pub(crate) fn leave(&self) -> bool {
316let mut inner = self.inner.lock().unwrap();
317match &mut inner.deref_mut().offer {
318 DataDeviceOffer::Drag(o) => {
319 o.left = true;
320if !o.dropped {
321 o.data_offer.destroy();
322 }
323 !o.dropped
324 }
325_ => {
326warn!("DataDeviceOffer::leave called on non-drag offer");
327false
328}
329 }
330 }
331332pub(crate) fn as_selection_offer(&self) -> Option<SelectionOffer> {
333match &self.inner.lock().unwrap().deref().offer {
334 DataDeviceOffer::Selection(o) => Some(o.clone()),
335_ => None,
336 }
337 }
338}
339340#[derive(Debug, Default)]
341pub struct DataDeviceOfferInner {
342pub(crate) offer: DataDeviceOffer,
343pub(crate) mime_types: Vec<String>,
344}
345346#[derive(Debug, Clone, PartialEq)]
347pub(crate) enum DataDeviceOffer {
348 Drag(DragOffer),
349 Selection(SelectionOffer),
350 Undetermined(UndeterminedOffer),
351}
352353impl Default for DataDeviceOffer {
354fn default() -> Self {
355 DataDeviceOffer::Undetermined(UndeterminedOffer {
356 data_offer: None,
357 actions: DndAction::empty(),
358 })
359 }
360}
361362impl<D> Dispatch<wl_data_offer::WlDataOffer, DataOfferData, D> for DataDeviceManagerState
363where
364D: Dispatch<wl_data_offer::WlDataOffer, DataOfferData> + DataOfferHandler,
365{
366fn 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 ) {
374match 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 } => {
379match source_actions {
380 wayland_client::WEnum::Value(a) => {
381 data.set_source_action(a);
382match &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 } => {
394match dnd_action {
395 wayland_client::WEnum::Value(a) => {
396 data.set_selected_action(a);
397match &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}
412413#[derive(Debug, Clone)]
414pub(crate) struct UndeterminedOffer {
415pub(crate) data_offer: Option<WlDataOffer>,
416pub actions: DndAction,
417}
418419impl PartialEq for UndeterminedOffer {
420fn eq(&self, other: &Self) -> bool {
421self.data_offer == other.data_offer
422 }
423}
424425/// 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> {
439use rustix::pipe::{pipe_with, PipeFlags};
440// create a pipe
441let (readfd, writefd) = pipe_with(PipeFlags::CLOEXEC)?;
442443 receive_to_fd(offer, mime_type, writefd);
444445Ok(ReadPipe::from(readfd))
446}
447448/// 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}