<NME> mod.rs
<BEF> use std::sync::Mutex;

use wayland_server::{
    protocol::{wl_data_device, wl_data_device_manager, wl_data_offer, wl_data_source},
    Client, Display, Global, NewResource, Resource,
};

//! the data devices.
//!
//! The module also provides additional mechanisms allowing your compositor to see and interact with
//! the contents of the data device:
//!
//! - the freestanding function [`set_data_device_selection`]
//!   allows you to set the contents of the selection for your clients
//! - the freestanding function [`start_dnd`] allows you to initiate a drag'n'drop event from the compositor
//!   itself and receive interactions of clients with it via an other dedicated callback.
//!
//! The module defines the role `"dnd_icon"` that is assigned to surfaces used as drag'n'drop icons.
//!
//! ## Initialization
//!
//! To initialize this implementation, create the [`DataDeviceState`], store it inside your `State` struct
//! and implement the [`DataDeviceHandler`], as shown in this example:
//!
//! ```
//! # extern crate wayland_server;
//! # #[macro_use] extern crate smithay;
//! use smithay::delegate_data_device;
//! use smithay::wayland::data_device::{ClientDndGrabHandler, DataDeviceState, DataDeviceHandler, ServerDndGrabHandler};
//! # use smithay::input::{Seat, SeatState, SeatHandler, pointer::CursorImageStatus};
//! # use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
//!
//! # struct State { data_device_state: DataDeviceState }
//! # let mut display = wayland_server::Display::<State>::new().unwrap();
//! // Create the data_device state
//! let data_device_state = DataDeviceState::new::<State, _>(
//!     &display.handle(),
//!     None // We don't add a logger in this example
//! );
//!
//! // insert the DataDeviceState into your state
//! // ..
//!
//! // implement the necessary traits
//! # impl SeatHandler for State {
//! #     type KeyboardFocus = WlSurface;
//! #     type PointerFocus = WlSurface;
//! #     fn seat_state(&mut self) -> &mut SeatState<Self> { unimplemented!() }
//! #     fn focus_changed(&mut self, seat: &Seat<Self>, focused: Option<&WlSurface>) { unimplemented!() }
//! #     fn cursor_image(&mut self, seat: &Seat<Self>, image: CursorImageStatus) { unimplemented!() }
//! # }
//! impl ClientDndGrabHandler for State {}
//! impl ServerDndGrabHandler for State {}
//! impl DataDeviceHandler for State {
//!     fn data_device_state(&self) -> &DataDeviceState { &self.data_device_state }
//!     // ... override default implementations here to customize handling ...
//! }
//! delegate_data_device!(State);
//!
//! // You're now ready to go!
//! ```

use std::cell::RefCell;

use io_lifetimes::OwnedFd;
use wayland_server::{
    backend::GlobalId,
    protocol::{
        wl_data_device_manager::{DndAction, WlDataDeviceManager},
        wl_data_source::WlDataSource,
        wl_surface::WlSurface,
    },
    Client, DisplayHandle, GlobalDispatch,
};

use crate::{
    input::{
        pointer::{Focus, GrabStartData as PointerGrabStartData},
        Seat, SeatHandler,
    },
    utils::Serial,
    wayland::seat::WaylandFocus,
};

mod device;
mod dnd_grab;
mod seat_data;
mod server_dnd_grab;
mod source;

pub use device::{DataDeviceUserData, DND_ICON_ROLE};
pub use source::{with_source_metadata, DataSourceUserData, SourceMetadata};

use seat_data::{SeatData, Selection};

/// Events that are generated by interactions of the clients with the data device
#[allow(unused_variables)]
pub trait DataDeviceHandler: Sized + ClientDndGrabHandler + ServerDndGrabHandler {
    /// [DataDeviceState] getter
    fn data_device_state(&self) -> &DataDeviceState;

    /// Action chooser for DnD negociation
    fn action_choice(&mut self, available: DndAction, preferred: DndAction) -> DndAction {
        default_action_chooser(available, preferred)
    }

    /// A client has set the selection
    fn new_selection(&mut self, source: Option<WlDataSource>) {}

    /// A client requested to read the server-set selection
    ///
    /// * `mime_type` - the requested mime type
    /// * `fd` - the fd to write into
    fn send_selection(&mut self, mime_type: String, fd: OwnedFd) {}
}

/// Events that are generated during client initiated drag'n'drop
#[allow(unused_variables)]
pub trait ClientDndGrabHandler: SeatHandler + Sized {
    /// A client started a drag'n'drop as response to a user pointer action
}

/// Initialize the data device global
pub fn init_data_device<L>(
    display: &mut Display,
    logger: L,
) -> Global<wl_data_device_manager::WlDataDeviceManager>
where
    L: Into<Option<::slog::Logger>>,
{
    let log = ::slog_or_stdlog(logger).new(o!("smithay_module" => "data_device_mgr"));

    let global = display.create_global(3, move |new_ddm, _version| {
        implement_ddm(new_ddm, log.clone());
    });

    global
    /// The DnD resource was dropped by the user
    ///
    /// After that, the client can still interact with your resource
    fn dropped(&mut self) {}

    /// The Dnd was cancelled
    ///
    /// The client can no longer interact
    fn cancelled(&mut self) {}

    /// The client requested for data to be sent
    ///
    /// * `mime_type` - The requested mime type
    /// * `fd` - The FD to write into
    fn send(&mut self, mime_type: String, fd: OwnedFd) {}
    seat_data.lock().unwrap().set_focus(client);
}

fn implement_ddm(
    new_ddm: NewResource<wl_data_device_manager::WlDataDeviceManager>,
    log: ::slog::Logger,
) -> Resource<wl_data_device_manager::WlDataDeviceManager> {
    use self::wl_data_device_manager::Request;
    new_ddm.implement(
        move |req, _ddm| match req {
impl DataDeviceState {
    /// Regiseter new [WlDataDeviceManager] global
    pub fn new<D, L>(display: &DisplayHandle, logger: L) -> Self
    where
        L: Into<Option<::slog::Logger>>,
        D: GlobalDispatch<WlDataDeviceManager, ()> + 'static,
                    seat.user_data()
                        .insert_if_missing(|| Mutex::new(SeatData::new(log.clone())));
                    let seat_data = seat.user_data().get::<Mutex<SeatData>>().unwrap();
                    let data_device = implement_data_device(id, seat.clone(), log.clone());
                    seat_data.lock().unwrap().known_devices.push(data_device);
                }
                None => {

    /// [WlDataDeviceManager] GlobalId getter
    pub fn global(&self) -> GlobalId {
        self.manager_global.clone()
    }
}
    )
}

fn implement_data_device(
    new_dd: NewResource<wl_data_device::WlDataDevice>,
    seat: Seat,
    log: ::slog::Logger,
) -> Resource<wl_data_device::WlDataDevice> {
    use self::wl_data_device::Request;
    new_dd.implement(
        move |req, dd| match req {
        DndAction::Copy
    } else if available.contains(DndAction::Move) {
        DndAction::Move
    } else {
        DndAction::empty()
    }
}
                if let Some(pointer) = seat.get_pointer() {
                    if pointer.has_grab(serial) {
                        // The StartDrag is in response to a pointer implicit grab, all is good
                        pointer.set_grab(dnd_grab::DnDGrab::new(source, origin, seat.clone()), serial);
                        return;
                    }
                }
}

/// Set a compositor-provided selection for this seat
///
/// You need to provide the available mime types for this selection.
///
/// Whenever a client requests to read the selection, your callback will
/// receive a [`DataDeviceHandler::send_selection`] event.
pub fn set_data_device_selection<D>(dh: &DisplayHandle, seat: &Seat<D>, mime_types: Vec<String>)
where
    D: SeatHandler + DataDeviceHandler + 'static,
{
    seat.user_data()
        .insert_if_missing(|| RefCell::new(SeatData::new()));
    let seat_data = seat.user_data().get::<RefCell<SeatData>>().unwrap();
    seat_data.borrow_mut().set_selection::<D>(
        dh,
        Selection::Compositor(SourceMetadata {
            mime_types,
            dnd_action: DndAction::empty(),
        }),
    );
}

/// Start a drag'n'drop from a resource controlled by the compositor
///
/// You'll receive events generated by the interaction of clients with your
/// drag'n'drop in the provided callback. See [`ServerDndGrabHandler`] for details about
/// which events can be generated and what response is expected from you to them.
pub fn start_dnd<D, C>(
    dh: &DisplayHandle,
    seat: &Seat<D>,
    data: &mut D,
    serial: Serial,
    start_data: PointerGrabStartData<D>,
    metadata: SourceMetadata,
) where
    D: SeatHandler + DataDeviceHandler + 'static,
    <D as SeatHandler>::PointerFocus: WaylandFocus,
{
    seat.user_data()
        .insert_if_missing(|| RefCell::new(SeatData::new()));
    if let Some(pointer) = seat.get_pointer() {
        pointer.set_grab(
            data,
            server_dnd_grab::ServerDnDGrab::new(dh, start_data, metadata, seat.clone()),
            serial,
            Focus::Keep,
        );
    }
}

mod handlers {
    use std::cell::RefCell;

    use slog::error;
    use wayland_server::{
        protocol::{
            wl_data_device::WlDataDevice,
            wl_data_device_manager::{self, WlDataDeviceManager},
            wl_data_source::WlDataSource,
        },
        Dispatch, DisplayHandle, GlobalDispatch,
    };

    use crate::input::Seat;

    use super::{device::DataDeviceUserData, seat_data::SeatData, source::DataSourceUserData};
    use super::{DataDeviceHandler, DataDeviceState};

    impl<D> GlobalDispatch<WlDataDeviceManager, (), D> for DataDeviceState
    where
        D: GlobalDispatch<WlDataDeviceManager, ()>,
        D: Dispatch<WlDataDeviceManager, ()>,
        D: Dispatch<WlDataSource, DataSourceUserData>,
        D: Dispatch<WlDataDevice, DataDeviceUserData>,
        D: DataDeviceHandler,
        D: 'static,
    {
        fn bind(
            _state: &mut D,
            _handle: &DisplayHandle,
            _client: &wayland_server::Client,
            resource: wayland_server::New<WlDataDeviceManager>,
            _global_data: &(),
            data_init: &mut wayland_server::DataInit<'_, D>,
        ) {
            data_init.init(resource, ());
        }
    }

    impl<D> Dispatch<WlDataDeviceManager, (), D> for DataDeviceState
    where
        D: Dispatch<WlDataDeviceManager, ()>,
        D: Dispatch<WlDataSource, DataSourceUserData>,
        D: Dispatch<WlDataDevice, DataDeviceUserData>,
        D: DataDeviceHandler,
        D: 'static,
    {
        fn request(
            state: &mut D,
            _client: &wayland_server::Client,
            _resource: &WlDataDeviceManager,
            request: wl_data_device_manager::Request,
            _data: &(),
            _dhandle: &DisplayHandle,
            data_init: &mut wayland_server::DataInit<'_, D>,
        ) {
            let data_device_state = state.data_device_state();

            match request {
                wl_data_device_manager::Request::CreateDataSource { id } => {
                    data_init.init(id, DataSourceUserData::new());
                }
                wl_data_device_manager::Request::GetDataDevice { id, seat: wl_seat } => {
                    match Seat::<D>::from_resource(&wl_seat) {
                        Some(seat) => {
                            seat.user_data()
                                .insert_if_missing(|| RefCell::new(SeatData::new()));

                            let data_device = data_init.init(id, DataDeviceUserData { wl_seat });

                            let seat_data = seat.user_data().get::<RefCell<SeatData>>().unwrap();
                            seat_data.borrow_mut().add_device(data_device);
                        }
                        None => {
                            error!(&data_device_state.log, "Unmanaged seat given to a data device.");
                        }
                    }
                }
                _ => unreachable!(),
            }
        }
    }
}

#[allow(missing_docs)] // TODO
#[macro_export]
macro_rules! delegate_data_device {
    ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
        $crate::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
            $crate::reexports::wayland_server::protocol::wl_data_device_manager::WlDataDeviceManager: ()
        ] => $crate::wayland::data_device::DataDeviceState);

        $crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
            $crate::reexports::wayland_server::protocol::wl_data_device_manager::WlDataDeviceManager: ()
        ] => $crate::wayland::data_device::DataDeviceState);
        $crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
            $crate::reexports::wayland_server::protocol::wl_data_device::WlDataDevice: $crate::wayland::data_device::DataDeviceUserData
        ] => $crate::wayland::data_device::DataDeviceState);
        $crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
            $crate::reexports::wayland_server::protocol::wl_data_source::WlDataSource: $crate::wayland::data_device::DataSourceUserData
        ] => $crate::wayland::data_device::DataDeviceState);
    };
}

<MSG> data_device: let the compositor decide the action choice strategy

<DFF> @@ -1,7 +1,11 @@
-use std::sync::Mutex;
+use std::sync::{Arc, Mutex};
 
 use wayland_server::{
-    protocol::{wl_data_device, wl_data_device_manager, wl_data_offer, wl_data_source},
+    protocol::{
+        wl_data_device,
+        wl_data_device_manager::{self, DndAction},
+        wl_data_offer, wl_data_source,
+    },
     Client, Display, Global, NewResource, Resource,
 };
 
@@ -121,17 +125,25 @@ impl SeatData {
 }
 
 /// Initialize the data device global
-pub fn init_data_device<L>(
+///
+/// You need to provide a `(DndAction, DndAction) -> DndAction` closure that will arbitrate
+/// the choice of action resulting from a drag'n'drop session. Its first argument is the set of
+/// available actions (which is the intersection of the actions supported by the source and targets)
+/// and the second argument is the preferred action reported by the target. If no action should be
+/// chosen (and thus the drag'n'drop should abort on drop), return `DndAction::empty()`.
+pub fn init_data_device<F, L>(
     display: &mut Display,
+    action_choice: F,
     logger: L,
 ) -> Global<wl_data_device_manager::WlDataDeviceManager>
 where
+    F: FnMut(DndAction, DndAction) -> DndAction + Send + 'static,
     L: Into<Option<::slog::Logger>>,
 {
     let log = ::slog_or_stdlog(logger).new(o!("smithay_module" => "data_device_mgr"));
-
+    let action_choice = Arc::new(Mutex::new(action_choice));
     let global = display.create_global(3, move |new_ddm, _version| {
-        implement_ddm(new_ddm, log.clone());
+        implement_ddm(new_ddm, action_choice.clone(), log.clone());
     });
 
     global
@@ -153,10 +165,14 @@ pub fn set_data_device_focus(seat: &Seat, client: Option<Client>) {
     seat_data.lock().unwrap().set_focus(client);
 }
 
-fn implement_ddm(
+fn implement_ddm<F>(
     new_ddm: NewResource<wl_data_device_manager::WlDataDeviceManager>,
+    action_choice: Arc<Mutex<F>>,
     log: ::slog::Logger,
-) -> Resource<wl_data_device_manager::WlDataDeviceManager> {
+) -> Resource<wl_data_device_manager::WlDataDeviceManager>
+where
+    F: FnMut(DndAction, DndAction) -> DndAction + Send + 'static,
+{
     use self::wl_data_device_manager::Request;
     new_ddm.implement(
         move |req, _ddm| match req {
@@ -169,7 +185,8 @@ fn implement_ddm(
                     seat.user_data()
                         .insert_if_missing(|| Mutex::new(SeatData::new(log.clone())));
                     let seat_data = seat.user_data().get::<Mutex<SeatData>>().unwrap();
-                    let data_device = implement_data_device(id, seat.clone(), log.clone());
+                    let data_device =
+                        implement_data_device(id, seat.clone(), action_choice.clone(), log.clone());
                     seat_data.lock().unwrap().known_devices.push(data_device);
                 }
                 None => {
@@ -182,11 +199,15 @@ fn implement_ddm(
     )
 }
 
-fn implement_data_device(
+fn implement_data_device<F>(
     new_dd: NewResource<wl_data_device::WlDataDevice>,
     seat: Seat,
+    action_choice: Arc<Mutex<F>>,
     log: ::slog::Logger,
-) -> Resource<wl_data_device::WlDataDevice> {
+) -> Resource<wl_data_device::WlDataDevice>
+where
+    F: FnMut(DndAction, DndAction) -> DndAction + Send + 'static,
+{
     use self::wl_data_device::Request;
     new_dd.implement(
         move |req, dd| match req {
@@ -200,7 +221,10 @@ fn implement_data_device(
                 if let Some(pointer) = seat.get_pointer() {
                     if pointer.has_grab(serial) {
                         // The StartDrag is in response to a pointer implicit grab, all is good
-                        pointer.set_grab(dnd_grab::DnDGrab::new(source, origin, seat.clone()), serial);
+                        pointer.set_grab(
+                            dnd_grab::DnDGrab::new(source, origin, seat.clone(), action_choice.clone()),
+                            serial,
+                        );
                         return;
                     }
                 }

data_device: let the compositor decide the action choice strategy