use std::{ cell::RefCell, fmt, os::unix::io::OwnedFd, sync::{Arc, Mutex}, }; use wayland_server::{ backend::{protocol::Message, ClientId, Handle, ObjectData, ObjectId}, protocol::{ wl_data_device_manager::DndAction, wl_data_offer::{self, WlDataOffer}, wl_seat::WlSeat, wl_surface::WlSurface, }, DisplayHandle, Resource, }; use crate::utils::{Logical, Point, Serial, SERIAL_COUNTER}; use crate::wayland::seat::WaylandFocus; use crate::{ input::{ pointer::{ AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab, PointerInnerHandle, RelativeMotionEvent, }, touch::{GrabStartData as TouchGrabStartData, TouchGrab}, Seat, SeatHandler, }, wayland::selection::seat_data::SeatData, }; use super::{DataDeviceHandler, DataDeviceUserData, ServerDndGrabHandler, SourceMetadata}; /// Grab during a compositor-initiated DnD operation. pub struct ServerDnDGrab { dh: DisplayHandle, pointer_start_data: Option>, touch_start_data: Option>, metadata: super::SourceMetadata, current_focus: Option, pending_offers: Vec, offer_data: Option>>, seat: Seat, } impl fmt::Debug for ServerDnDGrab { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ServerDnDGrab") .field("dh", &self.dh) .field("pointer_start_data", &self.pointer_start_data) .field("touch_start_data", &self.touch_start_data) .field("metadata", &self.metadata) .field("current_focus", &self.current_focus) .field("pending_offers", &self.pending_offers) .field("offer_data", &self.offer_data) .field("seat", &self.seat) .finish() } } impl ServerDnDGrab { pub(crate) fn new_pointer( dh: &DisplayHandle, start_data: PointerGrabStartData, metadata: super::SourceMetadata, seat: Seat, ) -> Self { Self { dh: dh.clone(), pointer_start_data: Some(start_data), touch_start_data: None, metadata, current_focus: None, pending_offers: Vec::with_capacity(1), offer_data: None, seat, } } pub(crate) fn new_touch( dh: &DisplayHandle, start_data: TouchGrabStartData, metadata: super::SourceMetadata, seat: Seat, ) -> Self { Self { dh: dh.clone(), pointer_start_data: None, touch_start_data: Some(start_data), metadata, current_focus: None, pending_offers: Vec::with_capacity(1), offer_data: None, seat, } } } impl ServerDnDGrab where D: DataDeviceHandler, D: SeatHandler, D: 'static, { fn update_focus( &mut self, focus: Option<(F, Point)>, location: Point, serial: Serial, time: u32, ) { let seat_data = self .seat .user_data() .get::>>() .unwrap() .borrow_mut(); if focus.as_ref().and_then(|(s, _)| s.wl_surface()).as_deref() != self.current_focus.as_ref() { // focus changed, we need to make a leave if appropriate if let Some(surface) = self.current_focus.take() { for device in seat_data.known_data_devices() { if device.id().same_client_as(&surface.id()) { device.leave(); } } // disable the offers self.pending_offers.clear(); if let Some(offer_data) = self.offer_data.take() { offer_data.lock().unwrap().active = false; } } } if let Some((surface, surface_location)) = focus .as_ref() .and_then(|(h, loc)| h.wl_surface().map(|s| (s, loc))) { // early return if the surface is no longer valid let client = match self.dh.get_client(surface.id()) { Ok(c) => c, _ => return, }; let (x, y) = (location - *surface_location).into(); if self.current_focus.is_none() { // We entered a new surface, send the data offer let offer_data = Arc::new(Mutex::new(ServerDndOfferData { active: true, dropped: false, accepted: true, chosen_action: DndAction::empty(), })); for device in seat_data .known_data_devices() .filter(|d| d.id().same_client_as(&surface.id())) { let handle = self.dh.backend_handle(); let wl_seat = match device.data::() { Some(data) => data.wl_seat.clone(), None => continue, }; // create a data offer let offer = handle .create_object::( client.id(), WlDataOffer::interface(), device.version(), Arc::new(ServerDndData { metadata: self.metadata.clone(), offer_data: offer_data.clone(), wl_seat, }), ) .unwrap(); let offer = WlDataOffer::from_id(&self.dh, offer).unwrap(); // advertize the offer to the client device.data_offer(&offer); for mime_type in self.metadata.mime_types.iter().cloned() { offer.offer(mime_type); } offer.source_actions(self.metadata.dnd_action); device.enter(serial.into(), &surface, x, y, Some(&offer)); self.pending_offers.push(offer); } self.offer_data = Some(offer_data); self.current_focus = Some(surface.into_owned()); } else { // make a move for device in seat_data.known_data_devices() { if device.id().same_client_as(&surface.id()) { device.motion(time, x, y); } } } } } fn drop(&mut self, data: &mut D) { // the user dropped, proceed to the drop let seat_data = self .seat .user_data() .get::>>() .unwrap() .borrow_mut(); let validated = if let Some(ref data) = self.offer_data { let data = data.lock().unwrap(); data.accepted && (!data.chosen_action.is_empty()) } else { false }; if let Some(ref surface) = self.current_focus { for device in seat_data.known_data_devices() { if device.id().same_client_as(&surface.id()) && validated { device.drop(); } } } if let Some(ref offer_data) = self.offer_data { let mut data = offer_data.lock().unwrap(); if validated { data.dropped = true; } else { data.active = false; } } ServerDndGrabHandler::dropped(data, self.seat.clone()); if !validated { data.cancelled(self.seat.clone()); } // in all cases abandon the drop // no more buttons are pressed, release the grab if let Some(ref surface) = self.current_focus { for device in seat_data.known_data_devices() { if device.id().same_client_as(&surface.id()) { device.leave(); } } } } } impl PointerGrab for ServerDnDGrab where D: DataDeviceHandler, D: SeatHandler, ::PointerFocus: WaylandFocus, D: 'static, { fn motion( &mut self, data: &mut D, handle: &mut PointerInnerHandle<'_, D>, focus: Option<(::PointerFocus, Point)>, event: &MotionEvent, ) { let location = event.location; let serial = event.serial; let time = event.time; // While the grab is active, no client has pointer focus handle.motion(data, None, event); self.update_focus(focus, location, serial, time); } fn relative_motion( &mut self, data: &mut D, handle: &mut PointerInnerHandle<'_, D>, focus: Option<(::PointerFocus, Point)>, event: &RelativeMotionEvent, ) { handle.relative_motion(data, focus, event); } fn button(&mut self, data: &mut D, handle: &mut PointerInnerHandle<'_, D>, event: &ButtonEvent) { let serial = event.serial; let time = event.time; if handle.current_pressed().is_empty() { // the user dropped, proceed to the drop handle.unset_grab(self, data, serial, time, true); } } fn axis(&mut self, data: &mut D, handle: &mut PointerInnerHandle<'_, D>, details: AxisFrame) { // we just forward the axis events as is handle.axis(data, details); } fn frame(&mut self, data: &mut D, handle: &mut PointerInnerHandle<'_, D>) { handle.frame(data); } fn gesture_swipe_begin( &mut self, data: &mut D, handle: &mut PointerInnerHandle<'_, D>, event: &GestureSwipeBeginEvent, ) { handle.gesture_swipe_begin(data, event); } fn gesture_swipe_update( &mut self, data: &mut D, handle: &mut PointerInnerHandle<'_, D>, event: &GestureSwipeUpdateEvent, ) { handle.gesture_swipe_update(data, event); } fn gesture_swipe_end( &mut self, data: &mut D, handle: &mut PointerInnerHandle<'_, D>, event: &GestureSwipeEndEvent, ) { handle.gesture_swipe_end(data, event); } fn gesture_pinch_begin( &mut self, data: &mut D, handle: &mut PointerInnerHandle<'_, D>, event: &GesturePinchBeginEvent, ) { handle.gesture_pinch_begin(data, event); } fn gesture_pinch_update( &mut self, data: &mut D, handle: &mut PointerInnerHandle<'_, D>, event: &GesturePinchUpdateEvent, ) { handle.gesture_pinch_update(data, event); } fn gesture_pinch_end( &mut self, data: &mut D, handle: &mut PointerInnerHandle<'_, D>, event: &GesturePinchEndEvent, ) { handle.gesture_pinch_end(data, event); } fn gesture_hold_begin( &mut self, data: &mut D, handle: &mut PointerInnerHandle<'_, D>, event: &GestureHoldBeginEvent, ) { handle.gesture_hold_begin(data, event); } fn gesture_hold_end( &mut self, data: &mut D, handle: &mut PointerInnerHandle<'_, D>, event: &GestureHoldEndEvent, ) { handle.gesture_hold_end(data, event); } fn start_data(&self) -> &PointerGrabStartData { self.pointer_start_data.as_ref().unwrap() } fn unset(&mut self, data: &mut D) { self.drop(data); } } impl TouchGrab for ServerDnDGrab where D: DataDeviceHandler, D: SeatHandler, ::TouchFocus: WaylandFocus, D: 'static, { fn down( &mut self, _data: &mut D, _handle: &mut crate::input::touch::TouchInnerHandle<'_, D>, _focus: Option<(::TouchFocus, Point)>, _event: &crate::input::touch::DownEvent, _seq: Serial, ) { // Ignore } fn up( &mut self, data: &mut D, handle: &mut crate::input::touch::TouchInnerHandle<'_, D>, event: &crate::input::touch::UpEvent, _seq: Serial, ) { if event.slot != self.start_data().slot { return; } // the user dropped, proceed to the drop handle.unset_grab(self, data); } fn motion( &mut self, _data: &mut D, _handle: &mut crate::input::touch::TouchInnerHandle<'_, D>, focus: Option<(::TouchFocus, Point)>, event: &crate::input::touch::MotionEvent, _seq: Serial, ) { if event.slot != self.start_data().slot { return; } let location = event.location; let time = event.time; self.update_focus(focus, location, SERIAL_COUNTER.next_serial(), time); } fn frame( &mut self, _data: &mut D, _handle: &mut crate::input::touch::TouchInnerHandle<'_, D>, _seq: Serial, ) { } fn cancel( &mut self, data: &mut D, handle: &mut crate::input::touch::TouchInnerHandle<'_, D>, _seq: Serial, ) { // TODO: should we cancel something here? handle.unset_grab(self, data); } fn shape( &mut self, _data: &mut D, _handle: &mut crate::input::touch::TouchInnerHandle<'_, D>, _event: &crate::input::touch::ShapeEvent, _seq: Serial, ) { } fn orientation( &mut self, _data: &mut D, _handle: &mut crate::input::touch::TouchInnerHandle<'_, D>, _event: &crate::input::touch::OrientationEvent, _seq: Serial, ) { } fn start_data(&self) -> &TouchGrabStartData { self.touch_start_data.as_ref().unwrap() } fn unset(&mut self, data: &mut D) { self.drop(data); } } #[derive(Debug)] struct ServerDndOfferData { active: bool, dropped: bool, accepted: bool, chosen_action: DndAction, } struct ServerDndData { metadata: SourceMetadata, offer_data: Arc>, wl_seat: WlSeat, } impl ObjectData for ServerDndData where D: DataDeviceHandler + SeatHandler + 'static, { fn request( self: Arc, dh: &Handle, handler: &mut D, _client_id: ClientId, msg: Message, ) -> Option>> { let dh = DisplayHandle::from(dh.clone()); if let Ok((resource, request)) = WlDataOffer::parse_request(&dh, msg) { handle_server_dnd(handler, &resource, request, &self); } None } fn destroyed( self: Arc, _handle: &Handle, _data: &mut D, _client_id: ClientId, _object_id: ObjectId, ) { } } fn handle_server_dnd( handler: &mut D, offer: &WlDataOffer, request: wl_data_offer::Request, data: &ServerDndData, ) where D: DataDeviceHandler + SeatHandler + 'static, { use self::wl_data_offer::Request; let metadata = &data.metadata; let offer_data = &data.offer_data; let wl_seat = &data.wl_seat; if !wl_seat.is_alive() { return; } let seat = match Seat::::from_resource(wl_seat) { Some(s) => s, None => return, }; let mut data = offer_data.lock().unwrap(); match request { Request::Accept { mime_type, .. } => { if let Some(mtype) = &mime_type { data.accepted = metadata.mime_types.contains(mtype); } else { data.accepted = false; } handler.accept(mime_type, seat) } Request::Receive { mime_type, fd } => { // check if the source and associated mime type is still valid if metadata.mime_types.contains(&mime_type) && data.active { handler.send(mime_type, fd, seat); } } Request::Destroy => {} Request::Finish => { if !data.active { offer.post_error( wl_data_offer::Error::InvalidFinish as u32, "Cannot finish a data offer that is no longer active.", ); return; } if !data.accepted { offer.post_error( wl_data_offer::Error::InvalidFinish, "Cannot finish a data offer that has not been accepted.", ); return; } if !data.dropped { offer.post_error( wl_data_offer::Error::InvalidFinish, "Cannot finish a data offer that has not been dropped.", ); return; } if data.chosen_action.is_empty() { offer.post_error( wl_data_offer::Error::InvalidFinish, "Cannot finish a data offer with no valid action.", ); return; } handler.finished(seat); data.active = false; } Request::SetActions { dnd_actions, preferred_action, } => { let dnd_actions = dnd_actions.into_result().unwrap_or(DndAction::None); let preferred_action = preferred_action.into_result().unwrap_or(DndAction::None); // preferred_action must only contain one bitflag at the same time if ![DndAction::None, DndAction::Move, DndAction::Copy, DndAction::Ask] .contains(&preferred_action) { offer.post_error(wl_data_offer::Error::InvalidAction, "Invalid preferred action."); return; } let possible_actions = metadata.dnd_action & dnd_actions; let chosen_action = handler.action_choice(possible_actions, preferred_action); // check that the user provided callback respects that one precise action should be chosen debug_assert!( [DndAction::None, DndAction::Move, DndAction::Copy, DndAction::Ask].contains(&chosen_action), "Only one precise action should be chosen" ); if chosen_action != data.chosen_action { data.chosen_action = chosen_action; offer.action(chosen_action); handler.action(chosen_action, seat); } } _ => unreachable!(), } }