<NME> mod.rs
<BEF> use std::sync::{Arc, Mutex};

use wayland_server::{
//! Selection and drag'n'drop are per-seat notions.
//!
//! This module provides the freestanding [`set_data_device_focus`] function:
//!   This function sets the data device focus for a given seat; you'd typically call it
//!   whenever the keyboard focus changes, to follow it (for example in the focus hook of your keyboards).
//!
//! Using these two functions is enough for your clients to be able to interact with each other using
//! the data devices.
//!
//! The module also provides additional mechanisms allowing your compositor to see and interact with
//! the contents of the data device:
//!

pub use self::data_source::{with_source_metadata, SourceMetadata};

enum Selection {
    Empty,
    Client(Resource<wl_data_source::WlDataSource>),
}

struct SeatData {
//!     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
    ///
                    dd.send(wl_data_device::Event::Selection { id: Some(offer) });
                }
            }
        }
    }
}
    manager_global: GlobalId,
}

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,
        D: DataDeviceHandler,
    {
        let log = crate::slog_or_fallback(logger).new(slog::o!("smithay_module" => "data_device_mgr"));

        let manager_global = display.create_global::<D, WlDataDeviceManager, _>(3, ());

        Self { log, manager_global }
/// 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, action_choice.clone(), log.clone());
    });

    global
        DndAction::Copy
    } else if available.contains(DndAction::Move) {
        DndAction::Move
    } else {
        DndAction::empty()
    }
}

/// Set the data device focus to a certain client for a given seat
pub fn set_data_device_focus<D>(dh: &DisplayHandle, seat: &Seat<D>, client: Option<Client>)
where
    D: SeatHandler + DataDeviceHandler + 'static,
{
    seat.user_data()
        .insert_if_missing(|| RefCell::new(SeatData::new()));
    seat_data.lock().unwrap().set_focus(client);
}

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>
where
    F: FnMut(DndAction, DndAction) -> DndAction + Send + 'static,
{
    use self::wl_data_device_manager::Request;
    new_ddm.implement(
) 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() {
                    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(), action_choice.clone(), log.clone());
                    seat_data.lock().unwrap().known_devices.push(data_device);
                }
                None => {
    use wayland_server::{
        protocol::{
            wl_data_device::WlDataDevice,
            wl_data_device_manager::{self, WlDataDeviceManager},
            wl_data_source::WlDataSource,
        },
    )
}

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>
where
    F: FnMut(DndAction, DndAction) -> DndAction + Send + 'static,
{
    use self::wl_data_device::Request;
    new_dd.implement(
        move |req, dd| match req {
            Request::StartDrag {
            _handle: &DisplayHandle,
            _client: &wayland_server::Client,
            resource: wayland_server::New<WlDataDeviceManager>,
            _global_data: &(),
            data_init: &mut wayland_server::DataInit<'_, D>,
        ) {
                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(), action_choice.clone()),
                            serial,
        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,
                        .unwrap_or(false)
                    {
                        let seat_data = seat.user_data().get::<Mutex<SeatData>>().unwrap();
                        // The client has kbd focus, it can set the selection
                        seat_data
                            .lock()
                }
                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.");
                        }
                    }
            }
        },
        None::<fn(_)>,
        (),
    )
}

#[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 interact with the selection

<DFF> @@ -1,3 +1,4 @@
+use std::os::unix::io::RawFd;
 use std::sync::{Arc, Mutex};
 
 use wayland_server::{
@@ -16,9 +17,25 @@ mod dnd_grab;
 
 pub use self::data_source::{with_source_metadata, SourceMetadata};
 
+/// Events that are generated by interactions of the clients with the data device
+pub enum DataDeviceEvent {
+    /// A client has set the selection
+    NewSelection(Option<Resource<wl_data_source::WlDataSource>>),
+    /// A client started a drag'n'drop as response to a user pointer action
+    DnDStarted(Option<Resource<wl_data_source::WlDataSource>>),
+    /// A client requested to read the server-set selection
+    SendSelection {
+        // the requested mime type
+        mime_type: String,
+        // the fd to write into
+        fd: RawFd,
+    },
+}
+
 enum Selection {
     Empty,
     Client(Resource<wl_data_source::WlDataSource>),
+    Compositor(SourceMetadata),
 }
 
 struct SeatData {
@@ -109,6 +126,50 @@ impl SeatData {
                     dd.send(wl_data_device::Event::Selection { id: Some(offer) });
                 }
             }
+            Selection::Compositor(ref meta) => {
+                for dd in &self.known_devices {
+                    // skip data devices not belonging to our client
+                    if dd.client().map(|c| !c.equals(client)).unwrap_or(true) {
+                        continue;
+                    }
+                    let log = self.log.clone();
+                    let offer_meta = meta.clone();
+                    let callback = dd
+                        .user_data::<Arc<Mutex<FnMut(DataDeviceEvent) + Send + 'static>>>()
+                        .unwrap()
+                        .clone();
+                    // create a corresponding data offer
+                    let offer = client
+                        .create_resource::<wl_data_offer::WlDataOffer>(dd.version())
+                        .unwrap()
+                        .implement(
+                            move |req, _offer| match req {
+                                wl_data_offer::Request::Receive { fd, mime_type } => {
+                                    // check if the associated mime type is valid
+                                    if !offer_meta.mime_types.contains(&mime_type) {
+                                        // deny the receive
+                                        debug!(log, "Denying a wl_data_offer.receive with invalid source.");
+                                        let _ = ::nix::unistd::close(fd);
+                                    } else {
+                                        (&mut *callback.lock().unwrap())(DataDeviceEvent::SendSelection {
+                                            mime_type,
+                                            fd,
+                                        });
+                                    }
+                                }
+                                _ => { /* seleciton data offers only care about the `receive` event */ }
+                            },
+                            None::<fn(_)>,
+                            (),
+                        );
+                    // advertize the offer to the client
+                    dd.send(wl_data_device::Event::DataOffer { id: offer.clone() });
+                    for mime_type in meta.mime_types.iter().cloned() {
+                        offer.send(wl_data_offer::Event::Offer { mime_type })
+                    }
+                    dd.send(wl_data_device::Event::Selection { id: Some(offer) });
+                }
+            }
         }
     }
 }
@@ -131,19 +192,22 @@ impl SeatData {
 /// 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>(
+pub fn init_data_device<F, C, L>(
     display: &mut Display,
+    callback: C,
     action_choice: F,
     logger: L,
 ) -> Global<wl_data_device_manager::WlDataDeviceManager>
 where
     F: FnMut(DndAction, DndAction) -> DndAction + Send + 'static,
+    C: FnMut(DataDeviceEvent) + 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 callback = Arc::new(Mutex::new(callback));
     let global = display.create_global(3, move |new_ddm, _version| {
-        implement_ddm(new_ddm, action_choice.clone(), log.clone());
+        implement_ddm(new_ddm, callback.clone(), action_choice.clone(), log.clone());
     });
 
     global
@@ -165,13 +229,38 @@ pub fn set_data_device_focus(seat: &Seat, client: Option<Client>) {
     seat_data.lock().unwrap().set_focus(client);
 }
 
-fn implement_ddm<F>(
+/// 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 `DataDeviceEvent::SendSelection` event.
+pub fn set_data_device_selection(seat: &Seat, mime_types: Vec<String>) {
+    // TODO: same question as in set_data_device_focus
+    seat.user_data().insert_if_missing(|| {
+        Mutex::new(SeatData::new(
+            seat.arc.log.new(o!("smithay_module" => "data_device_mgr")),
+        ))
+    });
+    let seat_data = seat.user_data().get::<Mutex<SeatData>>().unwrap();
+    seat_data
+        .lock()
+        .unwrap()
+        .set_selection(Selection::Compositor(SourceMetadata {
+            mime_types,
+            dnd_action: DndAction::empty(),
+        }));
+}
+
+fn implement_ddm<F, C>(
     new_ddm: NewResource<wl_data_device_manager::WlDataDeviceManager>,
+    callback: Arc<Mutex<C>>,
     action_choice: Arc<Mutex<F>>,
     log: ::slog::Logger,
 ) -> Resource<wl_data_device_manager::WlDataDeviceManager>
 where
     F: FnMut(DndAction, DndAction) -> DndAction + Send + 'static,
+    C: FnMut(DataDeviceEvent) + Send + 'static,
 {
     use self::wl_data_device_manager::Request;
     new_ddm.implement(
@@ -185,8 +274,13 @@ where
                     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(), action_choice.clone(), log.clone());
+                    let data_device = implement_data_device(
+                        id,
+                        seat.clone(),
+                        callback.clone(),
+                        action_choice.clone(),
+                        log.clone(),
+                    );
                     seat_data.lock().unwrap().known_devices.push(data_device);
                 }
                 None => {
@@ -199,16 +293,19 @@ where
     )
 }
 
-fn implement_data_device<F>(
+fn implement_data_device<F, C>(
     new_dd: NewResource<wl_data_device::WlDataDevice>,
     seat: Seat,
+    callback: Arc<Mutex<C>>,
     action_choice: Arc<Mutex<F>>,
     log: ::slog::Logger,
 ) -> Resource<wl_data_device::WlDataDevice>
 where
     F: FnMut(DndAction, DndAction) -> DndAction + Send + 'static,
+    C: FnMut(DataDeviceEvent) + Send + 'static,
 {
     use self::wl_data_device::Request;
+    let callback2 = callback.clone();
     new_dd.implement(
         move |req, dd| match req {
             Request::StartDrag {
@@ -221,6 +318,7 @@ where
                 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
+                        (&mut *callback.lock().unwrap())(DataDeviceEvent::DnDStarted(source.clone()));
                         pointer.set_grab(
                             dnd_grab::DnDGrab::new(source, origin, seat.clone(), action_choice.clone()),
                             serial,
@@ -239,6 +337,7 @@ where
                         .unwrap_or(false)
                     {
                         let seat_data = seat.user_data().get::<Mutex<SeatData>>().unwrap();
+                        (&mut *callback.lock().unwrap())(DataDeviceEvent::NewSelection(source.clone()));
                         // The client has kbd focus, it can set the selection
                         seat_data
                             .lock()
@@ -261,6 +360,6 @@ where
             }
         },
         None::<fn(_)>,
-        (),
+        callback2 as Arc<Mutex<FnMut(DataDeviceEvent) + Send + 'static>>,
     )
 }

data_device: let the compositor interact with the selection