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}