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};
pub struct ServerDnDGrab<D: SeatHandler> {
dh: DisplayHandle,
pointer_start_data: Option<PointerGrabStartData<D>>,
touch_start_data: Option<TouchGrabStartData<D>>,
metadata: super::SourceMetadata,
current_focus: Option<WlSurface>,
pending_offers: Vec<wl_data_offer::WlDataOffer>,
offer_data: Option<Arc<Mutex<ServerDndOfferData>>>,
seat: Seat<D>,
}
impl<D: SeatHandler + 'static> fmt::Debug for ServerDnDGrab<D> {
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<D: SeatHandler> ServerDnDGrab<D> {
pub(crate) fn new_pointer(
dh: &DisplayHandle,
start_data: PointerGrabStartData<D>,
metadata: super::SourceMetadata,
seat: Seat<D>,
) -> 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<D>,
metadata: super::SourceMetadata,
seat: Seat<D>,
) -> 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<D> ServerDnDGrab<D>
where
D: DataDeviceHandler,
D: SeatHandler,
D: 'static,
{
fn update_focus<F: WaylandFocus>(
&mut self,
focus: Option<(F, Point<f64, Logical>)>,
location: Point<f64, Logical>,
serial: Serial,
time: u32,
) {
let seat_data = self
.seat
.user_data()
.get::<RefCell<SeatData<D::SelectionUserData>>>()
.unwrap()
.borrow_mut();
if focus.as_ref().and_then(|(s, _)| s.wl_surface()).as_deref() != self.current_focus.as_ref() {
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();
}
}
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)))
{
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() {
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::<DataDeviceUserData>() {
Some(data) => data.wl_seat.clone(),
None => continue,
};
let offer = handle
.create_object::<D>(
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();
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 {
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) {
let seat_data = self
.seat
.user_data()
.get::<RefCell<SeatData<D::SelectionUserData>>>()
.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());
}
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<D> PointerGrab<D> for ServerDnDGrab<D>
where
D: DataDeviceHandler,
D: SeatHandler,
<D as SeatHandler>::PointerFocus: WaylandFocus,
D: 'static,
{
fn motion(
&mut self,
data: &mut D,
handle: &mut PointerInnerHandle<'_, D>,
focus: Option<(<D as SeatHandler>::PointerFocus, Point<f64, Logical>)>,
event: &MotionEvent,
) {
let location = event.location;
let serial = event.serial;
let time = event.time;
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<(<D as SeatHandler>::PointerFocus, Point<f64, Logical>)>,
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() {
handle.unset_grab(self, data, serial, time, true);
}
}
fn axis(&mut self, data: &mut D, handle: &mut PointerInnerHandle<'_, D>, details: AxisFrame) {
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<D> {
self.pointer_start_data.as_ref().unwrap()
}
fn unset(&mut self, data: &mut D) {
self.drop(data);
}
}
impl<D> TouchGrab<D> for ServerDnDGrab<D>
where
D: DataDeviceHandler,
D: SeatHandler,
<D as SeatHandler>::TouchFocus: WaylandFocus,
D: 'static,
{
fn down(
&mut self,
_data: &mut D,
_handle: &mut crate::input::touch::TouchInnerHandle<'_, D>,
_focus: Option<(<D as SeatHandler>::TouchFocus, Point<f64, Logical>)>,
_event: &crate::input::touch::DownEvent,
_seq: Serial,
) {
}
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;
}
handle.unset_grab(self, data);
}
fn motion(
&mut self,
_data: &mut D,
_handle: &mut crate::input::touch::TouchInnerHandle<'_, D>,
focus: Option<(<D as SeatHandler>::TouchFocus, Point<f64, Logical>)>,
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,
) {
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<D> {
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<Mutex<ServerDndOfferData>>,
wl_seat: WlSeat,
}
impl<D> ObjectData<D> for ServerDndData
where
D: DataDeviceHandler + SeatHandler + 'static,
{
fn request(
self: Arc<Self>,
dh: &Handle,
handler: &mut D,
_client_id: ClientId,
msg: Message<ObjectId, OwnedFd>,
) -> Option<Arc<dyn ObjectData<D>>> {
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<Self>,
_handle: &Handle,
_data: &mut D,
_client_id: ClientId,
_object_id: ObjectId,
) {
}
}
fn handle_server_dnd<D>(
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::<D>::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 } => {
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);
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);
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!(),
}
}