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};
pub const DND_ICON_ROLE: &str = "dnd_icon";
#[doc(hidden)]
#[derive(Debug)]
pub struct DataDeviceUserData {
pub(crate) wl_seat: WlSeat,
}
impl<D> Dispatch<WlDataDevice, DataDeviceUserData, D> for DataDeviceState
where
D: Dispatch<WlDataDevice, DataDeviceUserData>,
D: DataDeviceHandler,
D: SeatHandler,
<D as SeatHandler>::PointerFocus: WaylandFocus,
<D as SeatHandler>::TouchFocus: WaylandFocus,
<D as SeatHandler>::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::<D>::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;
}
}
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;
}
}
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::<RefCell<SeatData<D::SelectionUserData>>>()
.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(),
);
seat_data
.borrow_mut()
.set_clipboard_selection::<D>(dh, source.map(OfferReplySource::Client));
}
wl_data_device::Request::Release => seat
.user_data()
.get::<RefCell<SeatData<D::SelectionUserData>>>()
.unwrap()
.borrow_mut()
.retain_devices(|ndd| match ndd {
SelectionDevice::DataDevice(ndd) => ndd != resource,
_ => true,
}),
_ => unreachable!(),
}
}
}