smithay_client_toolkit/data_device_manager/
data_offer.rs1use std::{
2 ops::{Deref, DerefMut},
3 os::unix::prelude::{AsFd, OwnedFd},
4 sync::{Arc, Mutex},
5};
6
7use log::warn;
8
9use crate::dispatch2::Dispatch2;
10use crate::reexports::client::{
11 protocol::{
12 wl_data_device_manager::DndAction,
13 wl_data_offer::{self, WlDataOffer},
14 wl_surface::WlSurface,
15 },
16 Connection, Proxy, QueueHandle,
17};
18
19use super::ReadPipe;
20
21pub trait DataOfferHandler: Sized {
25 fn source_actions(
27 &mut self,
28 conn: &Connection,
29 qh: &QueueHandle<Self>,
30 offer: &mut DragOffer,
31 actions: DndAction,
32 );
33
34 fn selected_action(
47 &mut self,
48 conn: &Connection,
49 qh: &QueueHandle<Self>,
50 offer: &mut DragOffer,
51 actions: DndAction,
52 );
53}
54
55#[derive(Debug, thiserror::Error)]
57pub enum DataOfferError {
58 #[error("offer is not valid to receive from yet")]
59 InvalidReceive,
60
61 #[error("IO error")]
62 Io(std::io::Error),
63}
64
65#[derive(Debug, Clone)]
66pub struct DragOffer {
67 pub(crate) data_offer: WlDataOffer,
69 pub serial: u32,
71 pub surface: WlSurface,
73 pub x: f64,
75 pub y: f64,
77 pub time: Option<u32>,
79 pub source_actions: DndAction,
81 pub selected_action: DndAction,
83 pub dropped: bool,
85 pub left: bool,
87}
88
89impl DragOffer {
90 pub fn finish(&self) {
91 if self.data_offer.version() >= 3 {
92 self.data_offer.finish();
93 }
94 }
95
96 pub fn with_mime_types<T, F: Fn(&[String]) -> T>(&self, callback: F) -> T {
98 let mime_types =
99 &self.data_offer.data::<DataOfferData>().unwrap().inner.lock().unwrap().mime_types;
100 callback(mime_types)
101 }
102
103 pub fn set_actions(&self, actions: DndAction, preferred_action: DndAction) {
107 if self.data_offer.version() >= 3 && !self.left {
108 self.data_offer.set_actions(actions, preferred_action);
109 }
110 }
111
112 pub fn receive(&self, mime_type: String) -> std::io::Result<ReadPipe> {
116 if !self.left || self.dropped {
118 receive(&self.data_offer, mime_type)
119 } else {
120 Err(std::io::Error::other("offer has left"))
121 }
122 }
123
124 pub fn accept_mime_type(&self, serial: u32, mime_type: Option<String>) {
128 if !self.left {
129 self.data_offer.accept(serial, mime_type);
130 }
131 }
132
133 pub fn destroy(&self) {
135 self.data_offer.destroy();
136 }
137
138 pub fn inner(&self) -> &WlDataOffer {
140 &self.data_offer
141 }
142}
143
144impl PartialEq for DragOffer {
145 fn eq(&self, other: &Self) -> bool {
146 self.data_offer == other.data_offer
147 }
148}
149
150#[derive(Debug, Clone)]
151pub struct SelectionOffer {
152 pub(crate) data_offer: WlDataOffer,
154}
155
156impl SelectionOffer {
157 pub fn with_mime_types<T, F: Fn(&[String]) -> T>(&self, callback: F) -> T {
159 let mime_types =
160 &self.data_offer.data::<DataOfferData>().unwrap().inner.lock().unwrap().mime_types;
161 callback(mime_types)
162 }
163
164 pub fn receive(&self, mime_type: String) -> Result<ReadPipe, DataOfferError> {
165 receive(&self.data_offer, mime_type).map_err(DataOfferError::Io)
166 }
167
168 pub fn destroy(&self) {
169 self.data_offer.destroy();
170 }
171
172 pub fn inner(&self) -> &WlDataOffer {
173 &self.data_offer
174 }
175}
176
177impl PartialEq for SelectionOffer {
178 fn eq(&self, other: &Self) -> bool {
179 self.data_offer == other.data_offer
180 }
181}
182
183#[derive(Debug, Default)]
184pub struct DataOfferData {
185 pub(crate) inner: Arc<Mutex<DataDeviceOfferInner>>,
186}
187
188impl DataOfferData {
189 pub fn with_mime_types<T, F: Fn(&[String]) -> T>(&self, callback: F) -> T {
191 let mime_types = &self.inner.lock().unwrap().mime_types;
192 callback(mime_types)
193 }
194
195 pub(crate) fn push_mime_type(&self, mime_type: String) {
196 self.inner.lock().unwrap().mime_types.push(mime_type);
197 }
198
199 pub(crate) fn set_source_action(&self, action: DndAction) {
200 let mut inner = self.inner.lock().unwrap();
201 match &mut inner.deref_mut().offer {
202 DataDeviceOffer::Drag(ref mut o) => o.source_actions = action,
203 DataDeviceOffer::Selection(_) => {}
204 DataDeviceOffer::Undetermined(ref mut o) => o.actions = action,
205 };
206 }
207
208 pub(crate) fn set_selected_action(&self, action: DndAction) {
209 let mut inner = self.inner.lock().unwrap();
210 match &mut inner.deref_mut().offer {
211 DataDeviceOffer::Drag(ref mut o) => o.selected_action = action,
212 DataDeviceOffer::Selection(_) => {} DataDeviceOffer::Undetermined(_) => {} };
215 }
216
217 pub(crate) fn to_selection_offer(&self) {
218 let mut inner = self.inner.lock().unwrap();
219 match &mut inner.deref_mut().offer {
220 DataDeviceOffer::Drag(o) => {
221 inner.offer =
222 DataDeviceOffer::Selection(SelectionOffer { data_offer: o.data_offer.clone() });
223 }
224 DataDeviceOffer::Selection(_) => {}
225 DataDeviceOffer::Undetermined(o) => {
226 inner.offer = DataDeviceOffer::Selection(SelectionOffer {
227 data_offer: o.data_offer.clone().unwrap(),
228 });
229 }
230 }
231 }
232
233 pub(crate) fn init_undetermined_offer(&self, offer: &WlDataOffer) {
234 let mut inner = self.inner.lock().unwrap();
235 match &mut inner.deref_mut().offer {
236 DataDeviceOffer::Drag(o) => {
237 inner.offer = DataDeviceOffer::Undetermined(UndeterminedOffer {
238 data_offer: Some(offer.clone()),
239 actions: o.source_actions,
240 });
241 }
242 DataDeviceOffer::Selection(_) => {
243 inner.offer = DataDeviceOffer::Undetermined(UndeterminedOffer {
244 data_offer: Some(offer.clone()),
245 actions: DndAction::empty(),
246 });
247 }
248 DataDeviceOffer::Undetermined(o) => {
249 o.data_offer = Some(offer.clone());
250 }
251 }
252 }
253
254 pub(crate) fn to_dnd_offer(
255 &self,
256 serial: u32,
257 surface: WlSurface,
258 x: f64,
259 y: f64,
260 time: Option<u32>,
261 ) {
262 let mut inner = self.inner.lock().unwrap();
263 match &mut inner.deref_mut().offer {
264 DataDeviceOffer::Drag(_) => {}
265 DataDeviceOffer::Selection(o) => {
266 inner.offer = DataDeviceOffer::Drag(DragOffer {
267 data_offer: o.data_offer.clone(),
268 source_actions: DndAction::empty(),
269 selected_action: DndAction::empty(),
270 serial,
271 surface,
272 x,
273 y,
274 time,
275 dropped: false,
276 left: false,
277 });
278 }
279 DataDeviceOffer::Undetermined(o) => {
280 inner.offer = DataDeviceOffer::Drag(DragOffer {
281 data_offer: o.data_offer.clone().unwrap(),
282 source_actions: o.actions,
283 selected_action: DndAction::empty(),
284 serial,
285 surface,
286 x,
287 y,
288 time,
289 dropped: false,
290 left: false,
291 });
292 }
293 }
294 }
295
296 pub(crate) fn motion(&self, x: f64, y: f64, time: u32) {
297 let mut inner = self.inner.lock().unwrap();
298 match &mut inner.deref_mut().offer {
299 DataDeviceOffer::Drag(o) => {
300 o.x = x;
301 o.y = y;
302 o.time = Some(time);
303 }
304 DataDeviceOffer::Selection(_) => {}
305 DataDeviceOffer::Undetermined(_) => {}
306 }
307 }
308
309 pub(crate) fn as_drag_offer(&self) -> Option<DragOffer> {
310 match &self.inner.lock().unwrap().deref().offer {
311 DataDeviceOffer::Drag(o) => Some(o.clone()),
312 _ => None,
313 }
314 }
315
316 pub(crate) fn leave(&self) -> bool {
317 let mut inner = self.inner.lock().unwrap();
318 match &mut inner.deref_mut().offer {
319 DataDeviceOffer::Drag(o) => {
320 o.left = true;
321 if !o.dropped {
322 o.data_offer.destroy();
323 }
324 !o.dropped
325 }
326 _ => {
327 warn!("DataDeviceOffer::leave called on non-drag offer");
328 false
329 }
330 }
331 }
332
333 pub(crate) fn as_selection_offer(&self) -> Option<SelectionOffer> {
334 match &self.inner.lock().unwrap().deref().offer {
335 DataDeviceOffer::Selection(o) => Some(o.clone()),
336 _ => None,
337 }
338 }
339}
340
341#[derive(Debug, Default)]
342pub struct DataDeviceOfferInner {
343 pub(crate) offer: DataDeviceOffer,
344 pub(crate) mime_types: Vec<String>,
345}
346
347#[derive(Debug, Clone, PartialEq)]
348pub(crate) enum DataDeviceOffer {
349 Drag(DragOffer),
350 Selection(SelectionOffer),
351 Undetermined(UndeterminedOffer),
352}
353
354impl Default for DataDeviceOffer {
355 fn default() -> Self {
356 DataDeviceOffer::Undetermined(UndeterminedOffer {
357 data_offer: None,
358 actions: DndAction::empty(),
359 })
360 }
361}
362
363impl<D> Dispatch2<wl_data_offer::WlDataOffer, D> for DataOfferData
364where
365 D: DataOfferHandler,
366{
367 fn event(
368 &self,
369 state: &mut D,
370 _offer: &wl_data_offer::WlDataOffer,
371 event: <wl_data_offer::WlDataOffer as wayland_client::Proxy>::Event,
372 conn: &wayland_client::Connection,
373 qh: &wayland_client::QueueHandle<D>,
374 ) {
375 match event {
376 wl_data_offer::Event::Offer { mime_type } => {
377 self.push_mime_type(mime_type);
378 }
379 wl_data_offer::Event::SourceActions { source_actions } => {
380 match source_actions {
381 wayland_client::WEnum::Value(a) => {
382 self.set_source_action(a);
383 match &mut self.inner.lock().unwrap().offer {
384 DataDeviceOffer::Drag(o) => {
385 state.source_actions(conn, qh, o, a);
386 }
387 DataDeviceOffer::Selection(_) => {}
388 DataDeviceOffer::Undetermined(_) => {}
389 }
390 }
391 wayland_client::WEnum::Unknown(_) => {} }
393 }
394 wl_data_offer::Event::Action { dnd_action } => {
395 match dnd_action {
396 wayland_client::WEnum::Value(a) => {
397 self.set_selected_action(a);
398 match &mut self.inner.lock().unwrap().offer {
399 DataDeviceOffer::Drag(o) => {
400 state.selected_action(conn, qh, o, a);
401 }
402 DataDeviceOffer::Selection(_) => {}
403 DataDeviceOffer::Undetermined(_) => {}
404 }
405 }
406 wayland_client::WEnum::Unknown(_) => {} }
408 }
409 _ => unimplemented!(),
410 };
411 }
412}
413
414#[derive(Debug, Clone)]
415pub(crate) struct UndeterminedOffer {
416 pub(crate) data_offer: Option<WlDataOffer>,
417 pub actions: DndAction,
418}
419
420impl PartialEq for UndeterminedOffer {
421 fn eq(&self, other: &Self) -> bool {
422 self.data_offer == other.data_offer
423 }
424}
425
426pub fn receive(offer: &WlDataOffer, mime_type: String) -> std::io::Result<ReadPipe> {
440 use rustix::pipe::{pipe_with, PipeFlags};
441 let (readfd, writefd) = pipe_with(PipeFlags::CLOEXEC)?;
443
444 receive_to_fd(offer, mime_type, writefd);
445
446 Ok(ReadPipe::from(readfd))
447}
448
449pub fn receive_to_fd(offer: &WlDataOffer, mime_type: String, writefd: OwnedFd) {
463 offer.receive(mime_type, writefd.as_fd());
464}