window_clipboard/platform/
linux.rs
1use crate::{
2 dnd::DndProvider,
3 mime::{ClipboardLoadData, ClipboardStoreData},
4 ClipboardProvider,
5};
6
7use dnd::{DndAction, DndDestinationRectangle, DndSurface, Icon};
8use mime::{AllowedMimeTypes, AsMimeTypes};
9use raw_window_handle::{HasDisplayHandle, RawDisplayHandle};
10use std::{borrow::Cow, error::Error, sync::Arc};
11use wayland::DndSender;
12
13pub use clipboard_wayland as wayland;
14pub use clipboard_x11 as x11;
15
16pub enum Clipboard {
17 Wayland(wayland::Clipboard),
18 X11(x11::Clipboard),
19}
20
21impl ClipboardProvider for Clipboard {
22 fn read(&self) -> Result<String, Box<dyn Error>> {
23 match self {
24 Clipboard::Wayland(c) => c.read(),
25 Clipboard::X11(c) => c.read().map_err(Box::from),
26 }
27 }
28
29 fn write(&mut self, contents: String) -> Result<(), Box<dyn Error>> {
30 match self {
31 Clipboard::Wayland(c) => c.write(contents),
32 Clipboard::X11(c) => c.write(contents).map_err(Box::from),
33 }
34 }
35
36 fn read_primary(&self) -> Option<Result<String, Box<dyn Error>>> {
37 match self {
38 Clipboard::Wayland(c) => Some(c.read_primary()),
39 Clipboard::X11(c) => Some(c.read_primary().map_err(Box::from)),
40 }
41 }
42
43 fn write_primary(
44 &mut self,
45 contents: String,
46 ) -> Option<Result<(), Box<dyn Error>>> {
47 match self {
48 Clipboard::Wayland(c) => Some(c.write_primary(contents)),
49 Clipboard::X11(c) => {
50 Some(c.write_primary(contents).map_err(Box::from))
51 }
52 }
53 }
54
55 fn read_data<T: 'static>(&self) -> Option<Result<T, Box<dyn Error>>>
56 where
57 T: mime::AllowedMimeTypes,
58 {
59 match self {
60 Clipboard::Wayland(c) => {
61 let ret = c.read_data::<ClipboardLoadData<T>>();
62 Some(ret.map(|ret| ret.0))
63 }
64 Clipboard::X11(_) => None,
65 }
66 }
67
68 fn write_data<T: Send + Sync + 'static>(
69 &mut self,
70 contents: ClipboardStoreData<T>,
71 ) -> Option<Result<(), Box<dyn Error>>>
72 where
73 T: mime::AsMimeTypes,
74 {
75 match self {
76 Clipboard::Wayland(c) => {
77 Some(c.write_data::<ClipboardStoreData<T>>(contents))
78 }
79 Clipboard::X11(_) => None,
80 }
81 }
82
83 fn read_primary_data<T: 'static>(&self) -> Option<Result<T, Box<dyn Error>>>
84 where
85 T: mime::AllowedMimeTypes,
86 {
87 match self {
88 Clipboard::Wayland(c) => {
89 let ret = c.read_primary_data::<ClipboardLoadData<T>>();
90 Some(ret.map(|ret| ret.0))
91 }
92 Clipboard::X11(_) => None,
93 }
94 }
95
96 fn read_primary_raw(
97 &self,
98 allowed: Vec<String>,
99 ) -> Option<Result<(Vec<u8>, String), Box<dyn Error>>> {
100 match self {
101 Clipboard::Wayland(c) => Some(c.read_primary_raw(allowed)),
102 Clipboard::X11(_) => None,
103 }
104 }
105
106 fn read_raw(
107 &self,
108 allowed: Vec<String>,
109 ) -> Option<Result<(Vec<u8>, String), Box<dyn Error>>> {
110 match self {
111 Clipboard::Wayland(c) => Some(c.read_raw(allowed)),
112 Clipboard::X11(_) => None,
113 }
114 }
115
116 fn write_primary_data<T: Send + Sync + 'static>(
117 &mut self,
118 contents: ClipboardStoreData<T>,
119 ) -> Option<Result<(), Box<dyn Error>>>
120 where
121 T: mime::AsMimeTypes,
122 {
123 match self {
124 Clipboard::Wayland(c) => {
125 Some(c.write_primary_data::<ClipboardStoreData<T>>(contents))
126 }
127 Clipboard::X11(_) => None,
128 }
129 }
130}
131
132impl DndProvider for Clipboard {
133 fn init_dnd(
134 &self,
135 tx: Box<dyn dnd::Sender<DndSurface> + Send + Sync + 'static>,
136 ) {
137 match self {
138 Clipboard::Wayland(c) => c.init_dnd(DndSender(Arc::from(tx))),
139 Clipboard::X11(_) => {}
140 }
141 }
142
143 fn start_dnd<D: AsMimeTypes + Send + 'static>(
144 &self,
145 internal: bool,
146 source_surface: DndSurface,
147 icon_surface: Option<Icon>,
148 content: D,
149 actions: DndAction,
150 ) {
151 match self {
152 Clipboard::Wayland(c) => c.start_dnd(
153 internal,
154 source_surface,
155 icon_surface,
156 content,
157 actions,
158 ),
159 Clipboard::X11(_) => {}
160 }
161 }
162
163 fn end_dnd(&self) {
164 match self {
165 Clipboard::Wayland(c) => c.end_dnd(),
166 Clipboard::X11(_) => {}
167 }
168 }
169
170 fn register_dnd_destination(
171 &self,
172 surface: DndSurface,
173 rectangles: Vec<DndDestinationRectangle>,
174 ) {
175 match self {
176 Clipboard::Wayland(c) => {
177 c.register_dnd_destination(surface, rectangles)
178 }
179 Clipboard::X11(_) => {}
180 }
181 }
182
183 fn set_action(&self, action: DndAction) {
184 match self {
185 Clipboard::Wayland(c) => c.set_action(action),
186 Clipboard::X11(_) => {}
187 }
188 }
189
190 fn peek_offer<D: AllowedMimeTypes + 'static>(
191 &self,
192 mime_type: Option<Cow<'static, str>>,
193 ) -> std::io::Result<D> {
194 match self {
195 Clipboard::Wayland(c) => c.peek_offer::<D>(mime_type),
196 Clipboard::X11(_) => Err(std::io::Error::new(
197 std::io::ErrorKind::Other,
198 "DnD not supported",
199 )),
200 }
201 }
202}
203
204pub unsafe fn connect<W: HasDisplayHandle + ?Sized>(
205 window: &W,
206) -> Result<Clipboard, Box<dyn Error>> {
207 let clipboard = match window.display_handle()?.as_raw() {
208 RawDisplayHandle::Wayland(handle) => Clipboard::Wayland(
209 wayland::Clipboard::connect(handle.display.as_ptr()),
210 ) as _,
211 _ => Clipboard::X11(x11::Clipboard::connect()?) as _,
212 };
213
214 Ok(clipboard)
215}