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
36pub 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 if let Some(surface) = self.current_focus.take() {
135 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 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 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 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 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 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 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 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 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 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 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 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 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 }
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 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 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 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 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}