1use raw_window_handle::RawDisplayHandle;
2
3pub struct Clipboard {
8 #[cfg(all(
9 not(any(target_os = "android", target_os = "ios")),
10 feature = "arboard",
11 ))]
12 arboard: Option<arboard::Clipboard>,
13
14 #[cfg(all(
15 any(
16 target_os = "linux",
17 target_os = "dragonfly",
18 target_os = "freebsd",
19 target_os = "netbsd",
20 target_os = "openbsd"
21 ),
22 feature = "smithay-clipboard"
23 ))]
24 smithay: Option<smithay_clipboard::Clipboard>,
25
26 clipboard: String,
28}
29
30impl Clipboard {
31 pub fn new(_raw_display_handle: Option<RawDisplayHandle>) -> Self {
33 Self {
34 #[cfg(all(
35 not(any(target_os = "android", target_os = "ios")),
36 feature = "arboard",
37 ))]
38 arboard: init_arboard(),
39
40 #[cfg(all(
41 any(
42 target_os = "linux",
43 target_os = "dragonfly",
44 target_os = "freebsd",
45 target_os = "netbsd",
46 target_os = "openbsd"
47 ),
48 feature = "smithay-clipboard"
49 ))]
50 smithay: init_smithay_clipboard(_raw_display_handle),
51
52 clipboard: Default::default(),
53 }
54 }
55
56 pub fn get(&mut self) -> Option<String> {
57 #[cfg(all(
58 any(
59 target_os = "linux",
60 target_os = "dragonfly",
61 target_os = "freebsd",
62 target_os = "netbsd",
63 target_os = "openbsd"
64 ),
65 feature = "smithay-clipboard"
66 ))]
67 if let Some(clipboard) = &mut self.smithay {
68 return match clipboard.load() {
69 Ok(text) => Some(text),
70 Err(err) => {
71 log::error!("smithay paste error: {err}");
72 None
73 }
74 };
75 }
76
77 #[cfg(all(
78 not(any(target_os = "android", target_os = "ios")),
79 feature = "arboard",
80 ))]
81 if let Some(clipboard) = &mut self.arboard {
82 return match clipboard.get_text() {
83 Ok(text) => Some(text),
84 Err(err) => {
85 log::error!("arboard paste error: {err}");
86 None
87 }
88 };
89 }
90
91 Some(self.clipboard.clone())
92 }
93
94 pub fn set_text(&mut self, text: String) {
95 #[cfg(all(
96 any(
97 target_os = "linux",
98 target_os = "dragonfly",
99 target_os = "freebsd",
100 target_os = "netbsd",
101 target_os = "openbsd"
102 ),
103 feature = "smithay-clipboard"
104 ))]
105 if let Some(clipboard) = &mut self.smithay {
106 clipboard.store(text);
107 return;
108 }
109
110 #[cfg(all(
111 not(any(target_os = "android", target_os = "ios")),
112 feature = "arboard",
113 ))]
114 if let Some(clipboard) = &mut self.arboard {
115 if let Err(err) = clipboard.set_text(text) {
116 log::error!("arboard copy/cut error: {err}");
117 }
118 return;
119 }
120
121 self.clipboard = text;
122 }
123
124 pub fn set_image(&mut self, image: &egui::ColorImage) {
125 #[cfg(all(
126 not(any(target_os = "android", target_os = "ios")),
127 feature = "arboard",
128 ))]
129 if let Some(clipboard) = &mut self.arboard {
130 if let Err(err) = clipboard.set_image(arboard::ImageData {
131 width: image.width(),
132 height: image.height(),
133 bytes: std::borrow::Cow::Borrowed(bytemuck::cast_slice(&image.pixels)),
134 }) {
135 log::error!("arboard copy/cut error: {err}");
136 }
137 log::debug!("Copied image to clipboard");
138 return;
139 }
140
141 log::error!(
142 "Copying images is not supported. Enable the 'clipboard' feature of `egui-winit` to enable it."
143 );
144 _ = image;
145 }
146}
147
148#[cfg(all(
149 not(any(target_os = "android", target_os = "ios")),
150 feature = "arboard",
151))]
152fn init_arboard() -> Option<arboard::Clipboard> {
153 profiling::function_scope!();
154
155 log::trace!("Initializing arboard clipboard…");
156 match arboard::Clipboard::new() {
157 Ok(clipboard) => Some(clipboard),
158 Err(err) => {
159 log::warn!("Failed to initialize arboard clipboard: {err}");
160 None
161 }
162 }
163}
164
165#[cfg(all(
166 any(
167 target_os = "linux",
168 target_os = "dragonfly",
169 target_os = "freebsd",
170 target_os = "netbsd",
171 target_os = "openbsd"
172 ),
173 feature = "smithay-clipboard"
174))]
175fn init_smithay_clipboard(
176 raw_display_handle: Option<RawDisplayHandle>,
177) -> Option<smithay_clipboard::Clipboard> {
178 #![allow(clippy::undocumented_unsafe_blocks)]
179
180 profiling::function_scope!();
181
182 if let Some(RawDisplayHandle::Wayland(display)) = raw_display_handle {
183 log::trace!("Initializing smithay clipboard…");
184 #[expect(unsafe_code)]
185 Some(unsafe { smithay_clipboard::Clipboard::new(display.display.as_ptr()) })
186 } else {
187 #[cfg(feature = "wayland")]
188 log::debug!("Cannot init smithay clipboard without a Wayland display handle");
189 #[cfg(not(feature = "wayland"))]
190 log::debug!(
191 "Cannot init smithay clipboard: the 'wayland' feature of 'egui-winit' is not enabled"
192 );
193 None
194 }
195}