Skip to main content

smithay_client_toolkit/data_device_manager/
data_device.rs

1use std::{
2    ops::DerefMut,
3    sync::{Arc, Mutex},
4};
5
6use wayland_client::protocol::wl_surface::WlSurface;
7
8use crate::{
9    data_device_manager::data_offer::DataDeviceOffer,
10    dispatch2::Dispatch2,
11    reexports::client::{
12        event_created_child,
13        protocol::{
14            wl_data_device::{self, WlDataDevice},
15            wl_data_offer::{self, WlDataOffer},
16            wl_seat::WlSeat,
17        },
18        Connection, Dispatch, Proxy, QueueHandle,
19    },
20};
21
22use super::data_offer::{DataOfferData, DataOfferHandler, DragOffer, SelectionOffer};
23
24/// Handler trait for DataDevice events.
25///
26/// The functions defined in this trait are called as DataDevice events are received from the compositor.
27pub trait DataDeviceHandler: Sized {
28    // Introduces a new data offer
29    // ASHLEY left out because the data offer will be introduced to the user once the type is known
30    // either through the enter method or the selection method.
31    // fn data_offer(
32    //     &mut self,
33    //     conn: &Connection,
34    //     qh: &QueueHandle<Self>,
35    //     data_device: DataDevice,
36    //     offer: WlDataOffer,
37    // );
38
39    /// The data device pointer has entered a surface at the provided location
40    fn enter(
41        &mut self,
42        conn: &Connection,
43        qh: &QueueHandle<Self>,
44        data_device: &WlDataDevice,
45        x: f64,
46        y: f64,
47        wl_surface: &WlSurface,
48    );
49
50    /// The drag and drop pointer has left the surface and the session ends.
51    /// The offer will be destroyed unless it was previously dropped.
52    /// In the case of a dropped offer, the client must destroy it manually after it is finished.
53    fn leave(&mut self, conn: &Connection, qh: &QueueHandle<Self>, data_device: &WlDataDevice);
54
55    /// Drag and Drop motion.
56    fn motion(
57        &mut self,
58        conn: &Connection,
59        qh: &QueueHandle<Self>,
60        data_device: &WlDataDevice,
61        x: f64,
62        y: f64,
63    );
64
65    /// Advertises a new selection.
66    fn selection(&mut self, conn: &Connection, qh: &QueueHandle<Self>, data_device: &WlDataDevice);
67
68    /// Drop performed.
69    /// After the next data offer action event, data may be able to be received, unless the action is "ask".
70    fn drop_performed(
71        &mut self,
72        conn: &Connection,
73        qh: &QueueHandle<Self>,
74        data_device: &WlDataDevice,
75    );
76}
77
78#[derive(Debug, Eq, PartialEq)]
79pub struct DataDevice {
80    pub(crate) device: WlDataDevice,
81}
82
83impl DataDevice {
84    pub fn data(&self) -> &DataDeviceData {
85        self.device.data().unwrap()
86    }
87
88    /// Unset the selection of the provided data device as a response to the event with with provided serial.
89    pub fn unset_selection(&self, serial: u32) {
90        self.device.set_selection(None, serial);
91    }
92
93    pub fn inner(&self) -> &WlDataDevice {
94        &self.device
95    }
96}
97
98impl Drop for DataDevice {
99    fn drop(&mut self) {
100        if self.device.version() >= 2 {
101            self.device.release()
102        }
103    }
104}
105
106impl<D> Dispatch2<wl_data_device::WlDataDevice, D> for DataDeviceData
107where
108    D: Dispatch<wl_data_offer::WlDataOffer, DataOfferData>
109        + DataDeviceHandler
110        + DataOfferHandler
111        + 'static,
112{
113    event_created_child!(D, WlDataDevice, [
114        0 => (WlDataOffer, Default::default())
115    ]);
116
117    fn event(
118        &self,
119        state: &mut D,
120        data_device: &wl_data_device::WlDataDevice,
121        event: wl_data_device::Event,
122        conn: &Connection,
123        qh: &QueueHandle<D>,
124    ) {
125        use wayland_client::protocol::wl_data_device::Event;
126        let mut inner = self.inner.lock().unwrap();
127
128        match event {
129            Event::DataOffer { id } => {
130                inner.undetermined_offers.push(id.clone());
131                let data = id.data::<DataOfferData>().unwrap();
132                data.init_undetermined_offer(&id);
133            }
134            Event::Enter { serial, surface, x, y, id } => {
135                // XXX the spec isn't clear here.
136                if let Some(offer) = inner.drag_offer.take() {
137                    offer.destroy();
138                }
139
140                if let Some(offer) = id {
141                    if let Some(i) = inner.undetermined_offers.iter().position(|o| o == &offer) {
142                        inner.undetermined_offers.remove(i);
143                    }
144
145                    let data = offer.data::<DataOfferData>().unwrap();
146                    data.to_dnd_offer(serial, surface.clone(), x, y, None);
147
148                    inner.drag_offer = Some(offer.clone());
149                }
150                // XXX Drop done here to prevent Mutex deadlocks.
151                drop(inner);
152                state.enter(conn, qh, data_device, x, y, &surface);
153            }
154            Event::Leave => {
155                // We must destroy the offer we've got on enter.
156                if let Some(offer) = inner.drag_offer.take() {
157                    let data = offer.data::<DataOfferData>().unwrap();
158                    if !data.leave() {
159                        inner.drag_offer = Some(offer);
160                    }
161                }
162                // XXX Drop done here to prevent Mutex deadlocks.
163                drop(inner);
164                state.leave(conn, qh, data_device);
165            }
166            Event::Motion { time, x, y } => {
167                if let Some(offer) = inner.drag_offer.take() {
168                    let data = offer.data::<DataOfferData>().unwrap();
169                    // Update the data offer location.
170                    data.motion(x, y, time);
171                    inner.drag_offer = Some(offer);
172                }
173
174                // XXX Drop done here to prevent Mutex deadlocks.
175                drop(inner);
176                state.motion(conn, qh, data_device, x, y);
177            }
178            Event::Drop => {
179                if let Some(offer) = inner.drag_offer.take() {
180                    let data = offer.data::<DataOfferData>().unwrap();
181
182                    let mut drag_inner = data.inner.lock().unwrap();
183
184                    if let DataDeviceOffer::Drag(ref mut o) = drag_inner.deref_mut().offer {
185                        o.dropped = true;
186                    }
187                    drop(drag_inner);
188
189                    inner.drag_offer = Some(offer);
190                }
191                // XXX Drop done here to prevent Mutex deadlocks.
192                drop(inner);
193                // Pass the info about the drop to the user.
194                state.drop_performed(conn, qh, data_device);
195            }
196            Event::Selection { id } => {
197                // We must drop the current offer regardless.
198                if let Some(offer) = inner.selection_offer.take() {
199                    offer.destroy();
200                }
201
202                if let Some(offer) = id {
203                    if let Some(i) = inner.undetermined_offers.iter().position(|o| o == &offer) {
204                        inner.undetermined_offers.remove(i);
205                    }
206
207                    let data = offer.data::<DataOfferData>().unwrap();
208                    data.to_selection_offer();
209                    inner.selection_offer = Some(offer.clone());
210                    // XXX Drop done here to prevent Mutex deadlocks.
211                    drop(inner);
212                    state.selection(conn, qh, data_device);
213                }
214            }
215            _ => unreachable!(),
216        }
217    }
218}
219
220#[derive(Debug)]
221pub struct DataDeviceData {
222    /// The seat associated with this device.
223    pub(crate) seat: WlSeat,
224    /// The inner mutable storage.
225    pub(crate) inner: Arc<Mutex<DataDeviceInner>>,
226}
227
228impl DataDeviceData {
229    pub(crate) fn new(seat: WlSeat) -> Self {
230        Self { seat, inner: Default::default() }
231    }
232
233    /// Get the seat associated with this data device.
234    pub fn seat(&self) -> &WlSeat {
235        &self.seat
236    }
237
238    /// Get the active dnd offer if it exists.
239    pub fn drag_offer(&self) -> Option<DragOffer> {
240        self.inner.lock().unwrap().drag_offer.as_ref().and_then(|offer| {
241            let data = offer.data::<DataOfferData>().unwrap();
242            data.as_drag_offer()
243        })
244    }
245
246    /// Get the active selection offer if it exists.
247    pub fn selection_offer(&self) -> Option<SelectionOffer> {
248        self.inner.lock().unwrap().selection_offer.as_ref().and_then(|offer| {
249            let data = offer.data::<DataOfferData>().unwrap();
250            data.as_selection_offer()
251        })
252    }
253}
254
255#[derive(Debug, Default)]
256pub(crate) struct DataDeviceInner {
257    /// the active dnd offer and its data
258    pub drag_offer: Option<WlDataOffer>,
259    /// the active selection offer and its data
260    pub selection_offer: Option<WlDataOffer>,
261    /// the active undetermined offers and their data
262    pub undetermined_offers: Vec<WlDataOffer>,
263}