use wayland_client::{
protocol::{wl_data_device, wl_data_device_manager, wl_data_offer, wl_seat, wl_surface},
DispatchData, Main,
};
use std::sync::{Arc, Mutex};
use super::{DataOffer, DataSource, DndAction};
#[derive(Debug)]
struct Inner {
selection: Option<DataOffer>,
current_dnd: Option<DataOffer>,
known_offers: Vec<DataOffer>,
}
impl Inner {
fn new_offer(&mut self, offer: Main<wl_data_offer::WlDataOffer>) {
self.known_offers.push(DataOffer::new(offer));
}
fn set_selection(&mut self, offer: Option<wl_data_offer::WlDataOffer>) {
if let Some(offer) = offer {
if let Some(id) = self.known_offers.iter().position(|o| o.offer == offer) {
self.selection = Some(self.known_offers.swap_remove(id));
} else {
panic!("Compositor set an unknown data_offer for selection.");
}
} else {
self.selection = None;
}
}
fn set_dnd(&mut self, offer: Option<wl_data_offer::WlDataOffer>) {
if let Some(offer) = offer {
if let Some(id) = self.known_offers.iter().position(|o| o.offer == offer) {
self.current_dnd = Some(self.known_offers.swap_remove(id));
} else {
panic!("Compositor set an unknown data_offer for selection.");
}
} else {
self.current_dnd = None;
}
}
}
#[derive(Debug)]
pub struct DataDevice {
device: wl_data_device::WlDataDevice,
inner: Arc<Mutex<Inner>>,
}
#[derive(Debug)]
pub enum DndEvent<'a> {
Enter {
offer: Option<&'a DataOffer>,
serial: u32,
surface: wl_surface::WlSurface,
x: f64,
y: f64,
},
Motion {
offer: Option<&'a DataOffer>,
time: u32,
x: f64,
y: f64,
},
Leave,
Drop {
offer: Option<&'a DataOffer>,
},
}
fn data_device_implem<F>(
event: wl_data_device::Event,
inner: &mut Inner,
implem: &mut F,
ddata: DispatchData,
) where
for<'a> F: FnMut(DndEvent<'a>, DispatchData),
{
use self::wl_data_device::Event;
match event {
Event::DataOffer { id } => inner.new_offer(id),
Event::Enter { serial, surface, x, y, id } => {
inner.set_dnd(id);
implem(
DndEvent::Enter { serial, surface, x, y, offer: inner.current_dnd.as_ref() },
ddata,
);
}
Event::Motion { time, x, y } => {
implem(DndEvent::Motion { x, y, time, offer: inner.current_dnd.as_ref() }, ddata);
}
Event::Leave => implem(DndEvent::Leave, ddata),
Event::Drop => {
implem(DndEvent::Drop { offer: inner.current_dnd.as_ref() }, ddata);
}
Event::Selection { id } => inner.set_selection(id),
_ => unreachable!(),
}
}
impl DataDevice {
pub fn init_for_seat<F>(
manager: &wl_data_device_manager::WlDataDeviceManager,
seat: &wl_seat::WlSeat,
mut callback: F,
) -> DataDevice
where
for<'a> F: FnMut(DndEvent<'a>, DispatchData) + 'static,
{
let inner = Arc::new(Mutex::new(Inner {
selection: None,
current_dnd: None,
known_offers: Vec::new(),
}));
let inner2 = inner.clone();
let device = manager.get_data_device(seat);
device.quick_assign(move |_, evt, ddata| {
let mut inner = inner2.lock().unwrap();
data_device_implem(evt, &mut *inner, &mut callback, ddata);
});
DataDevice { device: device.detach(), inner }
}
pub fn start_drag(
&self,
origin: &wl_surface::WlSurface,
source: Option<DataSource>,
actions: DndAction,
icon: Option<&wl_surface::WlSurface>,
serial: u32,
) {
if let Some(source) = source {
source.source.set_actions(actions);
self.device.start_drag(Some(&source.source), origin, icon, serial);
} else {
self.device.start_drag(None, origin, icon, serial);
}
}
pub fn set_selection(&self, source: &Option<DataSource>, serial: u32) {
self.device.set_selection(source.as_ref().map(|s| &s.source), serial);
}
pub fn with_selection<F, T>(&self, f: F) -> T
where
F: FnOnce(Option<&DataOffer>) -> T,
{
let inner = self.inner.lock().unwrap();
f(inner.selection.as_ref())
}
}
impl Drop for DataDevice {
fn drop(&mut self) {
self.device.release();
}
}