1use std::ffi::c_void;
2use std::fmt::Debug;
3use std::sync::mpsc::SendError;
4
5use sctk::reexports::calloop;
6use sctk::reexports::client::protocol::wl_data_device_manager::DndAction;
7use sctk::reexports::client::protocol::wl_surface::WlSurface;
8use sctk::reexports::client::{Connection, Proxy};
9use wayland_backend::client::{InvalidId, ObjectId};
10
11use crate::mime::{AllowedMimeTypes, AsMimeTypes, MimeType};
12use crate::Clipboard;
13
14pub mod state;
15
16#[derive(Clone)]
17pub struct DndSurface<T> {
18 pub(crate) surface: WlSurface,
19 pub s: T,
20}
21
22impl<T> Debug for DndSurface<T> {
23 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24 f.debug_struct("Surface").field("surface", &self.surface).finish()
25 }
26}
27
28impl<T: RawSurface> DndSurface<T> {
29 fn new(mut s: T, conn: &Connection) -> Result<Self, InvalidId> {
30 let ptr = unsafe { s.get_ptr() };
31 let id = unsafe { ObjectId::from_ptr(WlSurface::interface(), ptr.cast())? };
32 let surface = WlSurface::from_id(conn, id)?;
33 Ok(Self { s, surface })
34 }
35}
36
37#[cfg(feature = "rwh-6")]
38impl<'a> RawSurface for raw_window_handle::WindowHandle<'a> {
39 unsafe fn get_ptr(&mut self) -> *mut c_void {
40 match self.as_raw() {
41 raw_window_handle::RawWindowHandle::Wayland(handle) => handle.surface.as_ptr().cast(),
42 _ => panic!("Unsupported window handle type."),
43 }
44 }
45}
46
47impl RawSurface for WlSurface {
48 unsafe fn get_ptr(&mut self) -> *mut c_void {
49 self.id().as_ptr().cast()
50 }
51}
52
53pub trait RawSurface {
54 unsafe fn get_ptr(&mut self) -> *mut c_void;
59}
60
61pub trait Sender<T> {
62 fn send(&self, t: DndEvent<T>) -> Result<(), SendError<DndEvent<T>>>;
64}
65
66#[derive(Debug)]
67pub enum SourceEvent {
68 Finished,
70 Cancelled,
72 Action(DndAction),
74 Mime(Option<MimeType>),
77 Dropped,
80}
81
82#[derive(Debug)]
83pub enum OfferEvent<T> {
84 Enter {
85 x: f64,
86 y: f64,
87 mime_types: Vec<MimeType>,
88 surface: T,
89 },
90 Motion {
91 x: f64,
92 y: f64,
93 },
94 LeaveDestination,
96 Leave,
98 Drop,
100 SelectedAction(DndAction),
104 Data {
105 data: Vec<u8>,
106 mime_type: MimeType,
107 },
108}
109
110#[derive(Debug, Default, Clone)]
112pub struct Rectangle {
113 pub x: f64,
114 pub y: f64,
115 pub width: f64,
116 pub height: f64,
117}
118
119impl Rectangle {
120 fn contains(&self, x: f64, y: f64) -> bool {
121 self.x <= x && self.x + self.width >= x && self.y <= y && self.y + self.height >= y
122 }
123}
124
125#[derive(Debug, Clone)]
126pub struct DndDestinationRectangle {
127 pub id: u128,
129 pub rectangle: Rectangle,
131 pub mime_types: Vec<MimeType>,
133 pub actions: DndAction,
135 pub preferred: DndAction,
137}
138
139pub enum DndRequest<T> {
140 InitDnd(Box<dyn crate::dnd::Sender<T> + Send>),
142 Surface(DndSurface<T>, Vec<DndDestinationRectangle>),
144 StartDnd {
146 internal: bool,
147 source: DndSurface<T>,
148 icon: Option<Icon<DndSurface<T>>>,
149 content: Box<dyn AsMimeTypes + Send>,
150 actions: DndAction,
151 },
152 Peek(MimeType),
154 SetAction(DndAction),
156 DndEnd,
158}
159
160#[derive(Debug)]
161pub enum DndEvent<T> {
162 Offer(Option<u128>, OfferEvent<T>),
164 Source(SourceEvent),
166}
167
168impl<T> Sender<T> for calloop::channel::Sender<DndEvent<T>> {
169 fn send(&self, t: DndEvent<T>) -> Result<(), SendError<DndEvent<T>>> {
170 self.send(t)
171 }
172}
173
174impl<T> Sender<T> for calloop::channel::SyncSender<DndEvent<T>> {
175 fn send(&self, t: DndEvent<T>) -> Result<(), SendError<DndEvent<T>>> {
176 self.send(t)
177 }
178}
179
180pub enum Icon<S> {
181 Surface(S),
182 Buf {
184 width: u32,
185 height: u32,
186 data: Vec<u8>,
187 transparent: bool,
188 },
189}
190
191impl<T: RawSurface> Clipboard<T> {
192 pub fn init_dnd(
194 &self,
195 tx: Box<dyn Sender<T> + Send>,
196 ) -> Result<(), SendError<crate::worker::Command<T>>> {
197 self.request_sender.send(crate::worker::Command::DndRequest(DndRequest::InitDnd(tx)))
198 }
199
200 pub fn start_dnd<D: AsMimeTypes + Send + 'static>(
202 &self,
203 internal: bool,
204 source_surface: T,
205 icon_surface: Option<Icon<T>>,
206 content: D,
207 actions: DndAction,
208 ) {
209 let source = DndSurface::new(source_surface, &self.connection).unwrap();
210 let icon = icon_surface.map(|i| match i {
211 Icon::Surface(s) => Icon::Surface(DndSurface::new(s, &self.connection).unwrap()),
212 Icon::Buf { width, height, data, transparent } => {
213 Icon::Buf { width, height, data, transparent }
214 },
215 });
216 _ = self.request_sender.send(crate::worker::Command::DndRequest(DndRequest::StartDnd {
217 internal,
218 source,
219 icon,
220 content: Box::new(content),
221 actions,
222 }));
223 }
224
225 pub fn end_dnd(&self) {
227 _ = self.request_sender.send(crate::worker::Command::DndRequest(DndRequest::DndEnd));
228 }
229
230 pub fn register_dnd_destination(&self, surface: T, rectangles: Vec<DndDestinationRectangle>) {
235 let s = DndSurface::new(surface, &self.connection).unwrap();
236
237 _ = self
238 .request_sender
239 .send(crate::worker::Command::DndRequest(DndRequest::Surface(s, rectangles)));
240 }
241
242 pub fn set_action(&self, action: DndAction) {
244 _ = self
245 .request_sender
246 .send(crate::worker::Command::DndRequest(DndRequest::SetAction(action)));
247 }
248
249 pub fn peek_offer<D: AllowedMimeTypes + 'static>(
251 &self,
252 mime_type: Option<MimeType>,
253 ) -> std::io::Result<D> {
254 let Some(mime_type) = mime_type.or_else(|| D::allowed().first().cloned()) else {
255 return Err(std::io::Error::new(std::io::ErrorKind::Other, "No mime type provided."));
256 };
257
258 self.request_sender
259 .send(crate::worker::Command::DndRequest(DndRequest::Peek(mime_type)))
260 .map_err(|_| {
261 std::io::Error::new(std::io::ErrorKind::Other, "Failed to send Peek request.")
262 })?;
263
264 self.request_receiver
265 .recv()
266 .map_err(|_| {
267 std::io::Error::new(std::io::ErrorKind::Other, "Failed to receive data request.")
268 })
269 .and_then(|ret| {
270 D::try_from(ret?).map_err(|_| {
271 std::io::Error::new(
272 std::io::ErrorKind::Other,
273 "Failed to convert data to requested type.",
274 )
275 })
276 })
277 }
278}