use std::ffi::c_void;
use std::fmt::Debug;
use std::sync::mpsc::SendError;
use sctk::reexports::calloop;
use sctk::reexports::client::protocol::wl_data_device_manager::DndAction;
use sctk::reexports::client::protocol::wl_surface::WlSurface;
use sctk::reexports::client::{Connection, Proxy};
use wayland_backend::client::{InvalidId, ObjectId};
use crate::mime::{AllowedMimeTypes, AsMimeTypes, MimeType};
use crate::Clipboard;
pub mod state;
#[derive(Clone)]
pub struct DndSurface<T> {
pub(crate) surface: WlSurface,
pub s: T,
}
impl<T> Debug for DndSurface<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Surface").field("surface", &self.surface).finish()
}
}
impl<T: RawSurface> DndSurface<T> {
fn new(mut s: T, conn: &Connection) -> Result<Self, InvalidId> {
let ptr = unsafe { s.get_ptr() };
let id = unsafe { ObjectId::from_ptr(WlSurface::interface(), ptr.cast())? };
let surface = WlSurface::from_id(conn, id)?;
Ok(Self { s, surface })
}
}
#[cfg(feature = "rwh-6")]
impl<'a> RawSurface for raw_window_handle::WindowHandle<'a> {
unsafe fn get_ptr(&mut self) -> *mut c_void {
match self.as_raw() {
raw_window_handle::RawWindowHandle::Wayland(handle) => handle.surface.as_ptr().cast(),
_ => panic!("Unsupported window handle type."),
}
}
}
impl RawSurface for WlSurface {
unsafe fn get_ptr(&mut self) -> *mut c_void {
self.id().as_ptr().cast()
}
}
pub trait RawSurface {
unsafe fn get_ptr(&mut self) -> *mut c_void;
}
pub trait Sender<T> {
fn send(&self, t: DndEvent<T>) -> Result<(), SendError<DndEvent<T>>>;
}
#[derive(Debug)]
pub enum SourceEvent {
Finished,
Cancelled,
Action(DndAction),
Mime(Option<MimeType>),
Dropped,
}
#[derive(Debug)]
pub enum OfferEvent<T> {
Enter {
x: f64,
y: f64,
mime_types: Vec<MimeType>,
surface: T,
},
Motion {
x: f64,
y: f64,
},
LeaveDestination,
Leave,
Drop,
SelectedAction(DndAction),
Data {
data: Vec<u8>,
mime_type: MimeType,
},
}
#[derive(Debug, Default, Clone)]
pub struct Rectangle {
pub x: f64,
pub y: f64,
pub width: f64,
pub height: f64,
}
impl Rectangle {
fn contains(&self, x: f64, y: f64) -> bool {
self.x <= x && self.x + self.width >= x && self.y <= y && self.y + self.height >= y
}
}
#[derive(Debug, Clone)]
pub struct DndDestinationRectangle {
pub id: u128,
pub rectangle: Rectangle,
pub mime_types: Vec<MimeType>,
pub actions: DndAction,
pub preferred: DndAction,
}
pub enum DndRequest<T> {
InitDnd(Box<dyn crate::dnd::Sender<T> + Send>),
Surface(DndSurface<T>, Vec<DndDestinationRectangle>),
StartDnd {
internal: bool,
source: DndSurface<T>,
icon: Option<Icon<DndSurface<T>>>,
content: Box<dyn AsMimeTypes + Send>,
actions: DndAction,
},
Peek(MimeType),
SetAction(DndAction),
DndEnd,
}
#[derive(Debug)]
pub enum DndEvent<T> {
Offer(Option<u128>, OfferEvent<T>),
Source(SourceEvent),
}
impl<T> Sender<T> for calloop::channel::Sender<DndEvent<T>> {
fn send(&self, t: DndEvent<T>) -> Result<(), SendError<DndEvent<T>>> {
self.send(t)
}
}
impl<T> Sender<T> for calloop::channel::SyncSender<DndEvent<T>> {
fn send(&self, t: DndEvent<T>) -> Result<(), SendError<DndEvent<T>>> {
self.send(t)
}
}
pub enum Icon<S> {
Surface(S),
Buf {
width: u32,
height: u32,
data: Vec<u8>,
transparent: bool,
},
}
impl<T: RawSurface> Clipboard<T> {
pub fn init_dnd(
&self,
tx: Box<dyn Sender<T> + Send>,
) -> Result<(), SendError<crate::worker::Command<T>>> {
self.request_sender.send(crate::worker::Command::DndRequest(DndRequest::InitDnd(tx)))
}
pub fn start_dnd<D: AsMimeTypes + Send + 'static>(
&self,
internal: bool,
source_surface: T,
icon_surface: Option<Icon<T>>,
content: D,
actions: DndAction,
) {
let source = DndSurface::new(source_surface, &self.connection).unwrap();
let icon = icon_surface.map(|i| match i {
Icon::Surface(s) => Icon::Surface(DndSurface::new(s, &self.connection).unwrap()),
Icon::Buf { width, height, data, transparent } => {
Icon::Buf { width, height, data, transparent }
},
});
_ = self.request_sender.send(crate::worker::Command::DndRequest(DndRequest::StartDnd {
internal,
source,
icon,
content: Box::new(content),
actions,
}));
}
pub fn end_dnd(&self) {
_ = self.request_sender.send(crate::worker::Command::DndRequest(DndRequest::DndEnd));
}
pub fn register_dnd_destination(&self, surface: T, rectangles: Vec<DndDestinationRectangle>) {
let s = DndSurface::new(surface, &self.connection).unwrap();
_ = self
.request_sender
.send(crate::worker::Command::DndRequest(DndRequest::Surface(s, rectangles)));
}
pub fn set_action(&self, action: DndAction) {
_ = self
.request_sender
.send(crate::worker::Command::DndRequest(DndRequest::SetAction(action)));
}
pub fn peek_offer<D: AllowedMimeTypes + 'static>(
&self,
mime_type: Option<MimeType>,
) -> std::io::Result<D> {
let Some(mime_type) = mime_type.or_else(|| D::allowed().first().cloned()) else {
return Err(std::io::Error::new(std::io::ErrorKind::Other, "No mime type provided."));
};
self.request_sender
.send(crate::worker::Command::DndRequest(DndRequest::Peek(mime_type)))
.map_err(|_| {
std::io::Error::new(std::io::ErrorKind::Other, "Failed to send Peek request.")
})?;
self.request_receiver
.recv()
.map_err(|_| {
std::io::Error::new(std::io::ErrorKind::Other, "Failed to receive data request.")
})
.and_then(|ret| {
D::try_from(ret?).map_err(|_| {
std::io::Error::new(
std::io::ErrorKind::Other,
"Failed to convert data to requested type.",
)
})
})
}
}