use std::cell::RefCell; use tracing::debug; use wayland_server::{ protocol::{ wl_data_device::{self, WlDataDevice}, wl_seat::WlSeat, }, Client, DataInit, Dispatch, DisplayHandle, Resource, }; use crate::{ input::{pointer::Focus, Seat, SeatHandler}, utils::Serial, wayland::{ compositor, seat::WaylandFocus, selection::{ device::SelectionDevice, offer::OfferReplySource, seat_data::SeatData, source::{SelectionSource, SelectionSourceProvider}, SelectionTarget, }, }, }; use super::{dnd_grab, DataDeviceHandler, DataDeviceState}; /// WlSurface role of drag and drop icon pub const DND_ICON_ROLE: &str = "dnd_icon"; #[doc(hidden)] #[derive(Debug)] pub struct DataDeviceUserData { pub(crate) wl_seat: WlSeat, } impl Dispatch for DataDeviceState where D: Dispatch, D: DataDeviceHandler, D: SeatHandler, ::PointerFocus: WaylandFocus, ::TouchFocus: WaylandFocus, ::KeyboardFocus: WaylandFocus, D: 'static, { fn request( handler: &mut D, client: &Client, resource: &WlDataDevice, request: wl_data_device::Request, data: &DataDeviceUserData, dh: &DisplayHandle, _data_init: &mut DataInit<'_, D>, ) { let seat = match Seat::::from_resource(&data.wl_seat) { Some(seat) => seat, None => return, }; match request { wl_data_device::Request::StartDrag { source, origin, icon, serial, } => { let serial = Serial::from(serial); if let Some(pointer) = seat.get_pointer() { if pointer.has_grab(serial) { if let Some(ref icon) = icon { if compositor::give_role(icon, DND_ICON_ROLE).is_err() { resource.post_error( wl_data_device::Error::Role, "Given surface already has an other role", ); return; } } // The StartDrag is in response to a pointer implicit grab, all is good handler.started(source.clone(), icon.clone(), seat.clone()); let start_data = pointer.grab_start_data().unwrap(); pointer.set_grab( handler, dnd_grab::DnDGrab::new_pointer(dh, start_data, source, origin, seat, icon), serial, Focus::Clear, ); return; } } if let Some(touch) = seat.get_touch() { if touch.has_grab(serial) { if let Some(ref icon) = icon { if compositor::give_role(icon, DND_ICON_ROLE).is_err() { resource.post_error( wl_data_device::Error::Role, "Given surface already has an other role", ); return; } } // The StartDrag is in response to a touch implicit grab, all is good handler.started(source.clone(), icon.clone(), seat.clone()); let start_data = touch.grab_start_data().unwrap(); touch.set_grab( handler, dnd_grab::DnDGrab::new_touch(dh, start_data, source, origin, seat, icon), serial, ); return; } } debug!(serial = ?serial, client = ?client, "denying drag from client without implicit grab"); } wl_data_device::Request::SetSelection { source, .. } => { let seat_data = match seat.get_keyboard() { Some(keyboard) if keyboard.client_of_object_has_focus(&resource.id()) => seat .user_data() .get::>>() .unwrap(), _ => { debug!( client = ?client, "denying setting selection by a non-focused client" ); return; } }; let source = source.map(SelectionSourceProvider::DataDevice); handler.new_selection( SelectionTarget::Clipboard, source.clone().map(|provider| SelectionSource { provider }), seat.clone(), ); // The client has kbd focus, it can set the selection seat_data .borrow_mut() .set_clipboard_selection::(dh, source.map(OfferReplySource::Client)); } wl_data_device::Request::Release => seat .user_data() .get::>>() .unwrap() .borrow_mut() .retain_devices(|ndd| match ndd { SelectionDevice::DataDevice(ndd) => ndd != resource, _ => true, }), _ => unreachable!(), } } }