1use std::{
16 borrow::Cow,
17 error::Error,
18 ffi::c_void,
19 sync::{mpsc::SendError, Arc, Mutex},
20};
21
22use dnd::{
23 DataWrapper, DndAction, DndDestinationRectangle, DndSurface, Sender,
24};
25use mime::ClipboardData;
26use smithay_clipboard::dnd::{Icon, Rectangle};
27pub use smithay_clipboard::mime::{AllowedMimeTypes, AsMimeTypes, MimeType};
28
29#[derive(Clone)]
30pub struct DndSender(pub Arc<dyn Sender<DndSurface> + 'static + Send + Sync>);
31
32impl smithay_clipboard::dnd::Sender<DndSurface> for DndSender {
33 fn send(
34 &self,
35 event: smithay_clipboard::dnd::DndEvent<DndSurface>,
36 ) -> Result<(), SendError<smithay_clipboard::dnd::DndEvent<DndSurface>>>
37 {
38 _ = self.0.send(match event {
39 smithay_clipboard::dnd::DndEvent::Offer(id, e) => dnd::DndEvent::Offer(
40 id,
41 match e {
42 smithay_clipboard::dnd::OfferEvent::Enter {
43 x,
44 y,
45 mime_types,
46 surface,
47 } => dnd::OfferEvent::Enter {
48 x,
49 y,
50 mime_types: mime_types
51 .into_iter()
52 .map(|m| m.to_string())
53 .collect(),
54 surface,
55 },
56 smithay_clipboard::dnd::OfferEvent::Motion { x, y } => {
57 dnd::OfferEvent::Motion { x, y }
58 }
59 smithay_clipboard::dnd::OfferEvent::LeaveDestination => {
60 dnd::OfferEvent::LeaveDestination
61 }
62 smithay_clipboard::dnd::OfferEvent::Leave => {
63 dnd::OfferEvent::Leave
64 }
65 smithay_clipboard::dnd::OfferEvent::Drop => {
66 dnd::OfferEvent::Drop
67 }
68 smithay_clipboard::dnd::OfferEvent::SelectedAction(
69 action,
70 ) => dnd::OfferEvent::SelectedAction(action.into()),
71 smithay_clipboard::dnd::OfferEvent::Data {
72 data,
73 mime_type,
74 } => dnd::OfferEvent::Data {
75 data,
76 mime_type: mime_type.to_string(),
77 },
78 },
79 ),
80 smithay_clipboard::dnd::DndEvent::Source(e) => match e {
81 smithay_clipboard::dnd::SourceEvent::Finished => {
82 dnd::DndEvent::Source(dnd::SourceEvent::Finished)
83 }
84 smithay_clipboard::dnd::SourceEvent::Cancelled => {
85 dnd::DndEvent::Source(dnd::SourceEvent::Cancelled)
86 }
87 smithay_clipboard::dnd::SourceEvent::Action(action) => {
88 dnd::DndEvent::Source(dnd::SourceEvent::Action(
89 action.into(),
90 ))
91 }
92 smithay_clipboard::dnd::SourceEvent::Mime(mime) => {
93 dnd::DndEvent::Source(dnd::SourceEvent::Mime(
94 mime.map(|m| m.to_string()),
95 ))
96 }
97 smithay_clipboard::dnd::SourceEvent::Dropped => {
98 dnd::DndEvent::Source(dnd::SourceEvent::Dropped)
99 }
100 },
101 });
102 Ok(())
103 }
104}
105
106pub struct Clipboard {
107 context: Arc<Mutex<smithay_clipboard::Clipboard<DndSurface>>>,
108}
109
110impl Clipboard {
111 pub unsafe fn connect(display: *mut c_void) -> Clipboard {
112 let context = Arc::new(Mutex::new(smithay_clipboard::Clipboard::new(
113 display as *mut _,
114 )));
115
116 Clipboard { context }
117 }
118
119 pub fn read(&self) -> Result<String, Box<dyn Error>> {
120 Ok(self.context.lock().unwrap().load_text()?)
121 }
122
123 pub fn read_primary(&self) -> Result<String, Box<dyn Error>> {
124 Ok(self.context.lock().unwrap().load_primary_text()?)
125 }
126
127 pub fn write(&mut self, data: String) -> Result<(), Box<dyn Error>> {
128 self.context.lock().unwrap().store_text(data);
129
130 Ok(())
131 }
132
133 pub fn write_primary(
134 &mut self,
135 data: String,
136 ) -> Result<(), Box<dyn Error>> {
137 self.context.lock().unwrap().store_primary_text(data);
138
139 Ok(())
140 }
141
142 pub fn write_data<T: AsMimeTypes + Send + Sync + 'static>(
143 &mut self,
144 data: T,
145 ) -> Result<(), Box<dyn Error>> {
146 self.context.lock().unwrap().store(data);
147
148 Ok(())
149 }
150
151 pub fn write_primary_data<T: AsMimeTypes + Send + Sync + 'static>(
152 &mut self,
153 data: T,
154 ) -> Result<(), Box<dyn Error>> {
155 self.context.lock().unwrap().store_primary(data);
156
157 Ok(())
158 }
159
160 pub fn read_data<T: AllowedMimeTypes + 'static>(
161 &self,
162 ) -> Result<T, Box<dyn Error>> {
163 Ok(self.context.lock().unwrap().load()?)
164 }
165
166 pub fn read_primary_data<T: AllowedMimeTypes + 'static>(
167 &self,
168 ) -> Result<T, Box<dyn Error>> {
169 Ok(self.context.lock().unwrap().load_primary()?)
170 }
171
172 pub fn read_primary_raw(
173 &self,
174 allowed: Vec<String>,
175 ) -> Result<(Vec<u8>, String), Box<dyn Error>> {
176 Ok(self
177 .context
178 .lock()
179 .unwrap()
180 .load_primary_mime::<DataWrapper<ClipboardData>>(
181 allowed
182 .into_iter()
183 .map(|s| MimeType::from(Cow::Owned(s)))
184 .collect::<Vec<_>>(),
185 )
186 .map(|d| (d.0 .0, d.0 .1.to_string()))?)
187 }
188
189 pub fn read_raw(
190 &self,
191 allowed: Vec<String>,
192 ) -> Result<(Vec<u8>, String), Box<dyn Error>> {
193 Ok(self
194 .context
195 .lock()
196 .unwrap()
197 .load_mime::<DataWrapper<ClipboardData>>(
198 allowed
199 .into_iter()
200 .map(|s| MimeType::from(Cow::Owned(s)))
201 .collect::<Vec<_>>(),
202 )
203 .map(|d| (d.0 .0, d.0 .1))?)
204 }
205
206 pub fn init_dnd(&self, tx: DndSender) {
207 _ = self.context.lock().unwrap().init_dnd(Box::new(tx));
208 }
209
210 pub fn start_dnd<D: mime::AsMimeTypes + Send + 'static>(
212 &self,
213 internal: bool,
214 source_surface: DndSurface,
215 icon_surface: Option<dnd::Icon>,
216 content: D,
217 actions: DndAction,
218 ) {
219 _ = self.context.lock().unwrap().start_dnd(
220 internal,
221 source_surface,
222 icon_surface.map(|i| Icon::<DndSurface>::from(i)),
223 DataWrapper(content),
224 actions.into(),
225 );
226 }
227
228 pub fn end_dnd(&self) {
230 _ = self.context.lock().unwrap().end_dnd();
231 }
232
233 pub fn register_dnd_destination(
238 &self,
239 surface: DndSurface,
240 rectangles: Vec<DndDestinationRectangle>,
241 ) {
242 _ = self.context.lock().unwrap().register_dnd_destination(
243 surface,
244 rectangles
245 .into_iter()
246 .map(|r| RectangleWrapper(r).into())
247 .collect(),
248 );
249 }
250
251 pub fn set_action(&self, action: DndAction) {
253 self.context.lock().unwrap().set_action(action.into());
254 }
255
256 pub fn peek_offer<D: mime::AllowedMimeTypes + 'static>(
258 &self,
259 mime_type: Option<Cow<'static, str>>,
260 ) -> std::io::Result<D> {
261 let d = self
262 .context
263 .lock()
264 .unwrap()
265 .peek_offer::<DataWrapper<D>>(mime_type.map(MimeType::from));
266 d.map(|d| d.0)
267 }
268}
269
270pub struct RectangleWrapper(pub DndDestinationRectangle);
271
272impl From<RectangleWrapper>
273 for smithay_clipboard::dnd::DndDestinationRectangle
274{
275 fn from(RectangleWrapper(d): RectangleWrapper) -> Self {
276 smithay_clipboard::dnd::DndDestinationRectangle {
277 id: d.id,
278 rectangle: Rectangle {
279 x: d.rectangle.x,
280 y: d.rectangle.y,
281 width: d.rectangle.width,
282 height: d.rectangle.height,
283 },
284 mime_types: d.mime_types.into_iter().map(MimeType::from).collect(),
285 actions: d.actions.into(),
286 preferred: d.preferred.into(),
287 }
288 }
289}