smithay/wayland/selection/data_device/
dnd_grab.rs

1use std::{
2    cell::RefCell,
3    fmt,
4    os::unix::io::{AsFd, OwnedFd},
5    sync::{Arc, Mutex},
6};
7
8use wayland_server::{
9    backend::{protocol::Message, ClientId, Handle, ObjectData, ObjectId},
10    protocol::{
11        wl_data_device_manager::DndAction,
12        wl_data_offer::{self, WlDataOffer},
13        wl_data_source::{self, WlDataSource},
14        wl_surface::WlSurface,
15    },
16    DisplayHandle, Resource,
17};
18
19use crate::{
20    input::{
21        pointer::{
22            AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent,
23            GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent, GestureSwipeEndEvent,
24            GestureSwipeUpdateEvent, GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab,
25            PointerInnerHandle, RelativeMotionEvent,
26        },
27        touch::{GrabStartData as TouchGrabStartData, TouchGrab},
28        Seat, SeatHandler,
29    },
30    utils::{IsAlive, Logical, Point, Serial, SERIAL_COUNTER},
31    wayland::{seat::WaylandFocus, selection::seat_data::SeatData},
32};
33
34use super::{with_source_metadata, ClientDndGrabHandler, DataDeviceHandler};
35
36/// Grab during a client-initiated DnD operation.
37pub struct DnDGrab<D: SeatHandler> {
38    dh: DisplayHandle,
39    pointer_start_data: Option<PointerGrabStartData<D>>,
40    touch_start_data: Option<TouchGrabStartData<D>>,
41    data_source: Option<wl_data_source::WlDataSource>,
42    current_focus: Option<WlSurface>,
43    pending_offers: Vec<wl_data_offer::WlDataOffer>,
44    offer_data: Option<Arc<Mutex<OfferData>>>,
45    icon: Option<WlSurface>,
46    origin: WlSurface,
47    seat: Seat<D>,
48}
49
50impl<D: SeatHandler + 'static> fmt::Debug for DnDGrab<D> {
51    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52        f.debug_struct("DnDGrab")
53            .field("dh", &self.dh)
54            .field("pointer_start_data", &self.pointer_start_data)
55            .field("touch_start_data", &self.touch_start_data)
56            .field("data_source", &self.data_source)
57            .field("current_focus", &self.current_focus)
58            .field("pending_offers", &self.pending_offers)
59            .field("offer_data", &self.offer_data)
60            .field("icon", &self.icon)
61            .field("origin", &self.origin)
62            .field("seat", &self.seat)
63            .finish()
64    }
65}
66
67impl<D: SeatHandler> DnDGrab<D> {
68    pub(crate) fn new_pointer(
69        dh: &DisplayHandle,
70        start_data: PointerGrabStartData<D>,
71        source: Option<wl_data_source::WlDataSource>,
72        origin: WlSurface,
73        seat: Seat<D>,
74        icon: Option<WlSurface>,
75    ) -> Self {
76        Self {
77            dh: dh.clone(),
78            pointer_start_data: Some(start_data),
79            touch_start_data: None,
80            data_source: source,
81            current_focus: None,
82            pending_offers: Vec::with_capacity(1),
83            offer_data: None,
84            origin,
85            icon,
86            seat,
87        }
88    }
89
90    pub(crate) fn new_touch(
91        dh: &DisplayHandle,
92        start_data: TouchGrabStartData<D>,
93        source: Option<wl_data_source::WlDataSource>,
94        origin: WlSurface,
95        seat: Seat<D>,
96        icon: Option<WlSurface>,
97    ) -> Self {
98        Self {
99            dh: dh.clone(),
100            pointer_start_data: None,
101            touch_start_data: Some(start_data),
102            data_source: source,
103            current_focus: None,
104            pending_offers: Vec::with_capacity(1),
105            offer_data: None,
106            origin,
107            icon,
108            seat,
109        }
110    }
111}
112
113impl<D> DnDGrab<D>
114where
115    D: DataDeviceHandler,
116    D: SeatHandler,
117    D: 'static,
118{
119    fn update_focus<F: WaylandFocus>(
120        &mut self,
121        focus: Option<(F, Point<f64, Logical>)>,
122        location: Point<f64, Logical>,
123        serial: Serial,
124        time: u32,
125    ) {
126        let seat_data = self
127            .seat
128            .user_data()
129            .get::<RefCell<SeatData<D::SelectionUserData>>>()
130            .unwrap()
131            .borrow_mut();
132        if focus.as_ref().and_then(|(s, _)| s.wl_surface()).as_deref() != self.current_focus.as_ref() {
133            // focus changed, we need to make a leave if appropriate
134            if let Some(surface) = self.current_focus.take() {
135                // only leave if there is a data source or we are on the original client
136                if self.data_source.is_some() || self.origin.id().same_client_as(&surface.id()) {
137                    for device in seat_data.known_data_devices() {
138                        if device.id().same_client_as(&surface.id()) {
139                            device.leave();
140                        }
141                    }
142                    // disable the offers
143                    self.pending_offers.clear();
144                    if let Some(offer_data) = self.offer_data.take() {
145                        offer_data.lock().unwrap().active = false;
146                    }
147                }
148            }
149        }
150        if let Some((surface, surface_location)) = focus
151            .as_ref()
152            .and_then(|(h, loc)| h.wl_surface().map(|s| (s, loc)))
153        {
154            // early return if the surface is no longer valid
155            let client = match self.dh.get_client(surface.id()) {
156                Ok(c) => c,
157                Err(_) => return,
158            };
159            let (x, y) = (location - *surface_location).into();
160            if self.current_focus.is_none() {
161                // We entered a new surface, send the data offer if appropriate
162                if let Some(ref source) = self.data_source {
163                    let offer_data = Arc::new(Mutex::new(OfferData {
164                        active: true,
165                        dropped: false,
166                        accepted: true,
167                        finished: false,
168                        chosen_action: DndAction::empty(),
169                    }));
170                    for device in seat_data
171                        .known_data_devices()
172                        .filter(|d| d.id().same_client_as(&surface.id()))
173                    {
174                        let handle = self.dh.backend_handle();
175
176                        // create a data offer
177                        let offer = handle
178                            .create_object::<D>(
179                                client.id(),
180                                WlDataOffer::interface(),
181                                device.version(),
182                                Arc::new(DndDataOffer {
183                                    offer_data: offer_data.clone(),
184                                    source: source.clone(),
185                                }),
186                            )
187                            .unwrap();
188                        let offer = WlDataOffer::from_id(&self.dh, offer).unwrap();
189
190                        // advertize the offer to the client
191                        device.data_offer(&offer);
192                        with_source_metadata(source, |meta| {
193                            for mime_type in meta.mime_types.iter().cloned() {
194                                offer.offer(mime_type);
195                            }
196                            offer.source_actions(meta.dnd_action);
197                        })
198                        .unwrap();
199                        device.enter(serial.into(), &surface, x, y, Some(&offer));
200                        self.pending_offers.push(offer);
201                    }
202                    self.offer_data = Some(offer_data);
203                } else {
204                    // only send if we are on a surface of the same client
205                    if self.origin.id().same_client_as(&surface.id()) {
206                        for device in seat_data.known_data_devices() {
207                            if device.id().same_client_as(&surface.id()) {
208                                device.enter(serial.into(), &surface, x, y, None);
209                            }
210                        }
211                    }
212                }
213                self.current_focus = Some(surface.into_owned());
214            } else {
215                // make a move
216                if self.data_source.is_some() || self.origin.id().same_client_as(&surface.id()) {
217                    for device in seat_data.known_data_devices() {
218                        if device.id().same_client_as(&surface.id()) {
219                            device.motion(time, x, y);
220                        }
221                    }
222                }
223            }
224        }
225    }
226
227    fn drop(&mut self, data: &mut D) {
228        // the user dropped, proceed to the drop
229        let seat_data = self
230            .seat
231            .user_data()
232            .get::<RefCell<SeatData<D::SelectionUserData>>>()
233            .unwrap()
234            .borrow_mut();
235        let validated = if let Some(ref data) = self.offer_data {
236            let data = data.lock().unwrap();
237            data.accepted && (!data.chosen_action.is_empty())
238        } else {
239            false
240        };
241        if let Some(ref surface) = self.current_focus {
242            if self.data_source.is_some() || self.origin.id().same_client_as(&surface.id()) {
243                for device in seat_data.known_data_devices() {
244                    if device.id().same_client_as(&surface.id()) && validated {
245                        device.drop();
246                    }
247                }
248            }
249        }
250        if let Some(ref offer_data) = self.offer_data {
251            let mut data = offer_data.lock().unwrap();
252            if validated {
253                data.dropped = true;
254            } else {
255                data.active = false;
256            }
257        }
258        if let Some(ref source) = self.data_source {
259            if !validated {
260                source.cancelled();
261            } else if source.version() >= wl_data_source::EVT_DND_DROP_PERFORMED_SINCE {
262                source.dnd_drop_performed();
263            }
264        }
265
266        ClientDndGrabHandler::dropped(data, self.current_focus.clone(), validated, self.seat.clone());
267        self.icon = None;
268        // in all cases abandon the drop
269        // no more buttons are pressed, release the grab
270        if let Some(ref surface) = self.current_focus {
271            for device in seat_data.known_data_devices() {
272                if device.id().same_client_as(&surface.id()) {
273                    device.leave();
274                }
275            }
276        }
277    }
278}
279
280impl<D> PointerGrab<D> for DnDGrab<D>
281where
282    D: DataDeviceHandler,
283    D: SeatHandler,
284    <D as SeatHandler>::PointerFocus: WaylandFocus,
285    D: 'static,
286{
287    fn motion(
288        &mut self,
289        data: &mut D,
290        handle: &mut PointerInnerHandle<'_, D>,
291        focus: Option<(<D as SeatHandler>::PointerFocus, Point<f64, Logical>)>,
292        event: &MotionEvent,
293    ) {
294        // While the grab is active, no client has pointer focus
295        handle.motion(data, None, event);
296
297        self.update_focus(focus, event.location, event.serial, event.time);
298    }
299
300    fn relative_motion(
301        &mut self,
302        data: &mut D,
303        handle: &mut PointerInnerHandle<'_, D>,
304        focus: Option<(<D as SeatHandler>::PointerFocus, Point<f64, Logical>)>,
305        event: &RelativeMotionEvent,
306    ) {
307        handle.relative_motion(data, focus, event);
308    }
309
310    fn button(&mut self, data: &mut D, handle: &mut PointerInnerHandle<'_, D>, event: &ButtonEvent) {
311        if handle.current_pressed().is_empty() {
312            // the user dropped, proceed to the drop
313            handle.unset_grab(self, data, event.serial, event.time, true);
314        }
315    }
316
317    fn axis(&mut self, data: &mut D, handle: &mut PointerInnerHandle<'_, D>, details: AxisFrame) {
318        // we just forward the axis events as is
319        handle.axis(data, details);
320    }
321
322    fn frame(&mut self, data: &mut D, handle: &mut PointerInnerHandle<'_, D>) {
323        handle.frame(data);
324    }
325
326    fn gesture_swipe_begin(
327        &mut self,
328        data: &mut D,
329        handle: &mut PointerInnerHandle<'_, D>,
330        event: &GestureSwipeBeginEvent,
331    ) {
332        handle.gesture_swipe_begin(data, event);
333    }
334
335    fn gesture_swipe_update(
336        &mut self,
337        data: &mut D,
338        handle: &mut PointerInnerHandle<'_, D>,
339        event: &GestureSwipeUpdateEvent,
340    ) {
341        handle.gesture_swipe_update(data, event);
342    }
343
344    fn gesture_swipe_end(
345        &mut self,
346        data: &mut D,
347        handle: &mut PointerInnerHandle<'_, D>,
348        event: &GestureSwipeEndEvent,
349    ) {
350        handle.gesture_swipe_end(data, event);
351    }
352
353    fn gesture_pinch_begin(
354        &mut self,
355        data: &mut D,
356        handle: &mut PointerInnerHandle<'_, D>,
357        event: &GesturePinchBeginEvent,
358    ) {
359        handle.gesture_pinch_begin(data, event);
360    }
361
362    fn gesture_pinch_update(
363        &mut self,
364        data: &mut D,
365        handle: &mut PointerInnerHandle<'_, D>,
366        event: &GesturePinchUpdateEvent,
367    ) {
368        handle.gesture_pinch_update(data, event);
369    }
370
371    fn gesture_pinch_end(
372        &mut self,
373        data: &mut D,
374        handle: &mut PointerInnerHandle<'_, D>,
375        event: &GesturePinchEndEvent,
376    ) {
377        handle.gesture_pinch_end(data, event);
378    }
379
380    fn gesture_hold_begin(
381        &mut self,
382        data: &mut D,
383        handle: &mut PointerInnerHandle<'_, D>,
384        event: &GestureHoldBeginEvent,
385    ) {
386        handle.gesture_hold_begin(data, event);
387    }
388
389    fn gesture_hold_end(
390        &mut self,
391        data: &mut D,
392        handle: &mut PointerInnerHandle<'_, D>,
393        event: &GestureHoldEndEvent,
394    ) {
395        handle.gesture_hold_end(data, event);
396    }
397
398    fn start_data(&self) -> &PointerGrabStartData<D> {
399        self.pointer_start_data.as_ref().unwrap()
400    }
401
402    fn unset(&mut self, data: &mut D) {
403        self.drop(data);
404    }
405}
406
407impl<D> TouchGrab<D> for DnDGrab<D>
408where
409    D: DataDeviceHandler,
410    D: SeatHandler,
411    <D as SeatHandler>::TouchFocus: WaylandFocus,
412    D: 'static,
413{
414    fn down(
415        &mut self,
416        _data: &mut D,
417        _handle: &mut crate::input::touch::TouchInnerHandle<'_, D>,
418        _focus: Option<(<D as SeatHandler>::TouchFocus, Point<f64, Logical>)>,
419        _event: &crate::input::touch::DownEvent,
420        _seq: crate::utils::Serial,
421    ) {
422        // Ignore
423    }
424
425    fn up(
426        &mut self,
427        data: &mut D,
428        handle: &mut crate::input::touch::TouchInnerHandle<'_, D>,
429        event: &crate::input::touch::UpEvent,
430        _seq: crate::utils::Serial,
431    ) {
432        if event.slot != self.start_data().slot {
433            return;
434        }
435
436        handle.unset_grab(self, data);
437    }
438
439    fn motion(
440        &mut self,
441        _data: &mut D,
442        _handle: &mut crate::input::touch::TouchInnerHandle<'_, D>,
443        focus: Option<(<D as SeatHandler>::TouchFocus, Point<f64, Logical>)>,
444        event: &crate::input::touch::MotionEvent,
445        _seq: crate::utils::Serial,
446    ) {
447        if event.slot != self.start_data().slot {
448            return;
449        }
450
451        self.update_focus(focus, event.location, SERIAL_COUNTER.next_serial(), event.time);
452    }
453
454    fn frame(
455        &mut self,
456        _data: &mut D,
457        _handle: &mut crate::input::touch::TouchInnerHandle<'_, D>,
458        _seq: crate::utils::Serial,
459    ) {
460    }
461
462    fn cancel(
463        &mut self,
464        data: &mut D,
465        handle: &mut crate::input::touch::TouchInnerHandle<'_, D>,
466        _seq: crate::utils::Serial,
467    ) {
468        // TODO: should we cancel something here?
469        handle.unset_grab(self, data);
470    }
471
472    fn shape(
473        &mut self,
474        _data: &mut D,
475        _handle: &mut crate::input::touch::TouchInnerHandle<'_, D>,
476        _event: &crate::input::touch::ShapeEvent,
477        _seq: Serial,
478    ) {
479    }
480
481    fn orientation(
482        &mut self,
483        _data: &mut D,
484        _handle: &mut crate::input::touch::TouchInnerHandle<'_, D>,
485        _event: &crate::input::touch::OrientationEvent,
486        _seq: Serial,
487    ) {
488    }
489
490    fn start_data(&self) -> &TouchGrabStartData<D> {
491        self.touch_start_data.as_ref().unwrap()
492    }
493
494    fn unset(&mut self, data: &mut D) {
495        self.drop(data);
496    }
497}
498
499#[derive(Debug)]
500struct OfferData {
501    active: bool,
502    dropped: bool,
503    accepted: bool,
504    finished: bool,
505    chosen_action: DndAction,
506}
507
508#[derive(Debug)]
509struct DndDataOffer {
510    offer_data: Arc<Mutex<OfferData>>,
511    source: WlDataSource,
512}
513
514impl<D> ObjectData<D> for DndDataOffer
515where
516    D: DataDeviceHandler,
517    D: 'static,
518{
519    fn request(
520        self: Arc<Self>,
521        dh: &Handle,
522        handler: &mut D,
523        _client_id: ClientId,
524        msg: Message<ObjectId, OwnedFd>,
525    ) -> Option<Arc<dyn ObjectData<D>>> {
526        let dh = DisplayHandle::from(dh.clone());
527        if let Ok((resource, request)) = WlDataOffer::parse_request(&dh, msg) {
528            handle_dnd(handler, &resource, request, &self);
529        }
530
531        None
532    }
533
534    fn destroyed(
535        self: Arc<Self>,
536        _handle: &Handle,
537        _data: &mut D,
538        _client_id: ClientId,
539        _object_id: ObjectId,
540    ) {
541    }
542}
543
544fn handle_dnd<D>(handler: &mut D, offer: &WlDataOffer, request: wl_data_offer::Request, data: &DndDataOffer)
545where
546    D: DataDeviceHandler,
547    D: 'static,
548{
549    use self::wl_data_offer::Request;
550    let source = &data.source;
551    let mut data = data.offer_data.lock().unwrap();
552    match request {
553        Request::Accept { mime_type, .. } => {
554            if let Some(mtype) = mime_type {
555                if let Err(crate::utils::UnmanagedResource) = with_source_metadata(source, |meta| {
556                    data.accepted = meta.mime_types.contains(&mtype);
557                }) {
558                    data.accepted = false;
559                }
560            } else {
561                data.accepted = false;
562            }
563        }
564        Request::Receive { mime_type, fd } => {
565            // check if the source and associated mime type is still valid
566            let valid = with_source_metadata(source, |meta| meta.mime_types.contains(&mime_type))
567                .unwrap_or(false)
568                && source.alive()
569                && data.active;
570            if valid {
571                source.send(mime_type, fd.as_fd());
572            }
573        }
574        Request::Destroy => {
575            if source.version() >= 3 && data.dropped && !data.finished {
576                source.cancelled();
577            }
578        }
579        Request::Finish => {
580            if !data.active {
581                offer.post_error(
582                    wl_data_offer::Error::InvalidFinish,
583                    "Cannot finish a data offer that is no longer active.",
584                );
585                return;
586            }
587            if !data.accepted {
588                offer.post_error(
589                    wl_data_offer::Error::InvalidFinish,
590                    "Cannot finish a data offer that has not been accepted.",
591                );
592                return;
593            }
594            if !data.dropped {
595                offer.post_error(
596                    wl_data_offer::Error::InvalidFinish,
597                    "Cannot finish a data offer that has not been dropped.",
598                );
599                return;
600            }
601            if data.chosen_action.is_empty() {
602                offer.post_error(
603                    wl_data_offer::Error::InvalidFinish,
604                    "Cannot finish a data offer with no valid action.",
605                );
606                return;
607            }
608            source.dnd_finished();
609            data.active = false;
610            data.finished = true;
611        }
612        Request::SetActions {
613            dnd_actions,
614            preferred_action,
615        } => {
616            let dnd_actions = dnd_actions.into_result().unwrap_or(DndAction::None);
617            let preferred_action = preferred_action.into_result().unwrap_or(DndAction::None);
618
619            // preferred_action must only contain one bitflag at the same time
620            if ![DndAction::None, DndAction::Move, DndAction::Copy, DndAction::Ask]
621                .contains(&preferred_action)
622            {
623                offer.post_error(wl_data_offer::Error::InvalidAction, "Invalid preferred action.");
624                return;
625            }
626
627            let source_actions =
628                with_source_metadata(source, |meta| meta.dnd_action).unwrap_or_else(|_| DndAction::empty());
629            let possible_actions = source_actions & dnd_actions;
630            let chosen_action = handler.action_choice(possible_actions, preferred_action);
631            // check that the user provided callback respects that one precise action should be chosen
632            debug_assert!(
633                [DndAction::None, DndAction::Move, DndAction::Copy, DndAction::Ask].contains(&chosen_action),
634                "Only one precise action should be chosen"
635            );
636            if chosen_action != data.chosen_action {
637                data.chosen_action = chosen_action;
638                offer.action(chosen_action);
639                source.action(chosen_action);
640            }
641        }
642        _ => unreachable!(),
643    }
644}