use std::{
ops::{Deref, DerefMut},
os::unix::prelude::{AsFd, OwnedFd},
sync::{Arc, Mutex},
};
use log::warn;
use crate::reexports::client::{
protocol::{
wl_data_device_manager::DndAction,
wl_data_offer::{self, WlDataOffer},
wl_surface::WlSurface,
},
Connection, Dispatch, Proxy, QueueHandle,
};
use super::{DataDeviceManagerState, ReadPipe};
pub trait DataOfferHandler: Sized {
fn source_actions(
&mut self,
conn: &Connection,
qh: &QueueHandle<Self>,
offer: &mut DragOffer,
actions: DndAction,
);
fn selected_action(
&mut self,
conn: &Connection,
qh: &QueueHandle<Self>,
offer: &mut DragOffer,
actions: DndAction,
);
}
#[derive(Debug, thiserror::Error)]
pub enum DataOfferError {
#[error("offer is not valid to receive from yet")]
InvalidReceive,
#[error("IO error")]
Io(std::io::Error),
}
#[derive(Debug, Clone)]
pub struct DragOffer {
pub(crate) data_offer: WlDataOffer,
pub serial: u32,
pub surface: WlSurface,
pub x: f64,
pub y: f64,
pub time: Option<u32>,
pub source_actions: DndAction,
pub selected_action: DndAction,
pub dropped: bool,
pub left: bool,
}
impl DragOffer {
pub fn finish(&self) {
if self.data_offer.version() >= 3 {
self.data_offer.finish();
}
}
pub fn with_mime_types<T, F: Fn(&[String]) -> T>(&self, callback: F) -> T {
let mime_types =
&self.data_offer.data::<DataOfferData>().unwrap().inner.lock().unwrap().mime_types;
callback(mime_types)
}
pub fn set_actions(&self, actions: DndAction, preferred_action: DndAction) {
if self.data_offer.version() >= 3 && !self.left {
self.data_offer.set_actions(actions, preferred_action);
}
}
pub fn receive(&self, mime_type: String) -> std::io::Result<ReadPipe> {
if !self.left || self.dropped {
receive(&self.data_offer, mime_type)
} else {
Err(std::io::Error::new(std::io::ErrorKind::Other, "offer has left"))
}
}
pub fn accept_mime_type(&self, serial: u32, mime_type: Option<String>) {
if !self.left {
self.data_offer.accept(serial, mime_type);
}
}
pub fn destroy(&self) {
self.data_offer.destroy();
}
pub fn inner(&self) -> &WlDataOffer {
&self.data_offer
}
}
impl PartialEq for DragOffer {
fn eq(&self, other: &Self) -> bool {
self.data_offer == other.data_offer
}
}
#[derive(Debug, Clone)]
pub struct SelectionOffer {
pub(crate) data_offer: WlDataOffer,
}
impl SelectionOffer {
pub fn with_mime_types<T, F: Fn(&[String]) -> T>(&self, callback: F) -> T {
let mime_types =
&self.data_offer.data::<DataOfferData>().unwrap().inner.lock().unwrap().mime_types;
callback(mime_types)
}
pub fn receive(&self, mime_type: String) -> Result<ReadPipe, DataOfferError> {
receive(&self.data_offer, mime_type).map_err(DataOfferError::Io)
}
pub fn destroy(&self) {
self.data_offer.destroy();
}
pub fn inner(&self) -> &WlDataOffer {
&self.data_offer
}
}
impl PartialEq for SelectionOffer {
fn eq(&self, other: &Self) -> bool {
self.data_offer == other.data_offer
}
}
#[derive(Debug, Default)]
pub struct DataOfferData {
pub(crate) inner: Arc<Mutex<DataDeviceOfferInner>>,
}
impl DataOfferData {
pub fn with_mime_types<T, F: Fn(&[String]) -> T>(&self, callback: F) -> T {
let mime_types = &self.inner.lock().unwrap().mime_types;
callback(mime_types)
}
pub(crate) fn push_mime_type(&self, mime_type: String) {
self.inner.lock().unwrap().mime_types.push(mime_type);
}
pub(crate) fn set_source_action(&self, action: DndAction) {
let mut inner = self.inner.lock().unwrap();
match &mut inner.deref_mut().offer {
DataDeviceOffer::Drag(ref mut o) => o.source_actions = action,
DataDeviceOffer::Selection(_) => {}
DataDeviceOffer::Undetermined(ref mut o) => o.actions = action,
};
}
pub(crate) fn set_selected_action(&self, action: DndAction) {
let mut inner = self.inner.lock().unwrap();
match &mut inner.deref_mut().offer {
DataDeviceOffer::Drag(ref mut o) => o.selected_action = action,
DataDeviceOffer::Selection(_) => {} DataDeviceOffer::Undetermined(_) => {} };
}
pub(crate) fn to_selection_offer(&self) {
let mut inner = self.inner.lock().unwrap();
match &mut inner.deref_mut().offer {
DataDeviceOffer::Drag(o) => {
inner.offer =
DataDeviceOffer::Selection(SelectionOffer { data_offer: o.data_offer.clone() });
}
DataDeviceOffer::Selection(_) => {}
DataDeviceOffer::Undetermined(o) => {
inner.offer = DataDeviceOffer::Selection(SelectionOffer {
data_offer: o.data_offer.clone().unwrap(),
});
}
}
}
pub(crate) fn init_undetermined_offer(&self, offer: &WlDataOffer) {
let mut inner = self.inner.lock().unwrap();
match &mut inner.deref_mut().offer {
DataDeviceOffer::Drag(o) => {
inner.offer = DataDeviceOffer::Undetermined(UndeterminedOffer {
data_offer: Some(offer.clone()),
actions: o.source_actions,
});
}
DataDeviceOffer::Selection(_) => {
inner.offer = DataDeviceOffer::Undetermined(UndeterminedOffer {
data_offer: Some(offer.clone()),
actions: DndAction::empty(),
});
}
DataDeviceOffer::Undetermined(o) => {
o.data_offer = Some(offer.clone());
}
}
}
pub(crate) fn to_dnd_offer(
&self,
serial: u32,
surface: WlSurface,
x: f64,
y: f64,
time: Option<u32>,
) {
let mut inner = self.inner.lock().unwrap();
match &mut inner.deref_mut().offer {
DataDeviceOffer::Drag(_) => {}
DataDeviceOffer::Selection(o) => {
inner.offer = DataDeviceOffer::Drag(DragOffer {
data_offer: o.data_offer.clone(),
source_actions: DndAction::empty(),
selected_action: DndAction::empty(),
serial,
surface,
x,
y,
time,
dropped: false,
left: false,
});
}
DataDeviceOffer::Undetermined(o) => {
inner.offer = DataDeviceOffer::Drag(DragOffer {
data_offer: o.data_offer.clone().unwrap(),
source_actions: o.actions,
selected_action: DndAction::empty(),
serial,
surface,
x,
y,
time,
dropped: false,
left: false,
});
}
}
}
pub(crate) fn motion(&self, x: f64, y: f64, time: u32) {
let mut inner = self.inner.lock().unwrap();
match &mut inner.deref_mut().offer {
DataDeviceOffer::Drag(o) => {
o.x = x;
o.y = y;
o.time = Some(time);
}
DataDeviceOffer::Selection(_) => {}
DataDeviceOffer::Undetermined(_) => {}
}
}
pub(crate) fn as_drag_offer(&self) -> Option<DragOffer> {
match &self.inner.lock().unwrap().deref().offer {
DataDeviceOffer::Drag(o) => Some(o.clone()),
_ => None,
}
}
pub(crate) fn leave(&self) -> bool {
let mut inner = self.inner.lock().unwrap();
match &mut inner.deref_mut().offer {
DataDeviceOffer::Drag(o) => {
o.left = true;
if !o.dropped {
o.data_offer.destroy();
}
!o.dropped
}
_ => {
warn!("DataDeviceOffer::leave called on non-drag offer");
false
}
}
}
pub(crate) fn as_selection_offer(&self) -> Option<SelectionOffer> {
match &self.inner.lock().unwrap().deref().offer {
DataDeviceOffer::Selection(o) => Some(o.clone()),
_ => None,
}
}
}
#[derive(Debug, Default)]
pub struct DataDeviceOfferInner {
pub(crate) offer: DataDeviceOffer,
pub(crate) mime_types: Vec<String>,
}
#[derive(Debug, Clone, PartialEq)]
pub(crate) enum DataDeviceOffer {
Drag(DragOffer),
Selection(SelectionOffer),
Undetermined(UndeterminedOffer),
}
impl Default for DataDeviceOffer {
fn default() -> Self {
DataDeviceOffer::Undetermined(UndeterminedOffer {
data_offer: None,
actions: DndAction::empty(),
})
}
}
impl<D> Dispatch<wl_data_offer::WlDataOffer, DataOfferData, D> for DataDeviceManagerState
where
D: Dispatch<wl_data_offer::WlDataOffer, DataOfferData> + DataOfferHandler,
{
fn event(
state: &mut D,
_offer: &wl_data_offer::WlDataOffer,
event: <wl_data_offer::WlDataOffer as wayland_client::Proxy>::Event,
data: &DataOfferData,
conn: &wayland_client::Connection,
qh: &wayland_client::QueueHandle<D>,
) {
match event {
wl_data_offer::Event::Offer { mime_type } => {
data.push_mime_type(mime_type);
}
wl_data_offer::Event::SourceActions { source_actions } => {
match source_actions {
wayland_client::WEnum::Value(a) => {
data.set_source_action(a);
match &mut data.inner.lock().unwrap().offer {
DataDeviceOffer::Drag(o) => {
state.source_actions(conn, qh, o, a);
}
DataDeviceOffer::Selection(_) => {}
DataDeviceOffer::Undetermined(_) => {}
}
}
wayland_client::WEnum::Unknown(_) => {} }
}
wl_data_offer::Event::Action { dnd_action } => {
match dnd_action {
wayland_client::WEnum::Value(a) => {
data.set_selected_action(a);
match &mut data.inner.lock().unwrap().offer {
DataDeviceOffer::Drag(o) => {
state.selected_action(conn, qh, o, a);
}
DataDeviceOffer::Selection(_) => {}
DataDeviceOffer::Undetermined(_) => {}
}
}
wayland_client::WEnum::Unknown(_) => {} }
}
_ => unimplemented!(),
};
}
}
#[derive(Debug, Clone)]
pub(crate) struct UndeterminedOffer {
pub(crate) data_offer: Option<WlDataOffer>,
pub actions: DndAction,
}
impl PartialEq for UndeterminedOffer {
fn eq(&self, other: &Self) -> bool {
self.data_offer == other.data_offer
}
}
pub fn receive(offer: &WlDataOffer, mime_type: String) -> std::io::Result<ReadPipe> {
use rustix::pipe::{pipe_with, PipeFlags};
let (readfd, writefd) = pipe_with(PipeFlags::CLOEXEC)?;
receive_to_fd(offer, mime_type, writefd);
Ok(ReadPipe::from(readfd))
}
pub fn receive_to_fd(offer: &WlDataOffer, mime_type: String, writefd: OwnedFd) {
offer.receive(mime_type, writefd.as_fd());
}