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 match clipboard.load() {
69 Ok(text) => return Some(text),
70 Err(err) => {
71 log::error!("smithay paste error: {err}");
72 }
73 }
74 }
75
76 #[cfg(all(
77 not(any(target_os = "android", target_os = "ios")),
78 feature = "arboard",
79 ))]
80 if let Some(clipboard) = &mut self.arboard {
81 return match clipboard.get_text() {
82 Ok(text) => Some(text),
83 Err(err) => {
84 log::error!("arboard paste error: {err}");
85 None
86 }
87 };
88 }
89
90 Some(self.clipboard.clone())
91 }
92
93 pub fn set_text(&mut self, text: String) {
94 #[cfg(all(
95 any(
96 target_os = "linux",
97 target_os = "dragonfly",
98 target_os = "freebsd",
99 target_os = "netbsd",
100 target_os = "openbsd"
101 ),
102 feature = "smithay-clipboard"
103 ))]
104 if let Some(clipboard) = &mut self.smithay {
105 clipboard.store(text);
106 return;
107 }
108
109 #[cfg(all(
110 not(any(target_os = "android", target_os = "ios")),
111 feature = "arboard",
112 ))]
113 if let Some(clipboard) = &mut self.arboard {
114 if let Err(err) = clipboard.set_text(text) {
115 log::error!("arboard copy/cut error: {err}");
116 }
117 return;
118 }
119
120 self.clipboard = text;
121 }
122
123 pub fn set_image(&mut self, image: &egui::ColorImage) {
124 #[cfg(all(
125 not(any(target_os = "android", target_os = "ios")),
126 feature = "arboard",
127 ))]
128 if let Some(clipboard) = &mut self.arboard {
129 if let Err(err) = clipboard.set_image(arboard::ImageData {
130 width: image.width(),
131 height: image.height(),
132 bytes: std::borrow::Cow::Borrowed(bytemuck::cast_slice(&image.pixels)),
133 }) {
134 log::error!("arboard copy/cut error: {err}");
135 }
136 log::debug!("Copied image to clipboard");
137 return;
138 }
139
140 log::error!(
141 "Copying images is not supported. Enable the 'clipboard' feature of `egui-winit` to enable it."
142 );
143 _ = image;
144 }
145}
146
147#[cfg(all(
148 not(any(target_os = "android", target_os = "ios")),
149 feature = "arboard",
150))]
151fn init_arboard() -> Option<arboard::Clipboard> {
152 profiling::function_scope!();
153
154 log::trace!("Initializing arboard clipboard…");
155 match arboard::Clipboard::new() {
156 Ok(clipboard) => Some(clipboard),
157 Err(err) => {
158 log::warn!("Failed to initialize arboard clipboard: {err}");
159 None
160 }
161 }
162}
163
164#[cfg(all(
165 any(
166 target_os = "linux",
167 target_os = "dragonfly",
168 target_os = "freebsd",
169 target_os = "netbsd",
170 target_os = "openbsd"
171 ),
172 feature = "smithay-clipboard"
173))]
174fn init_smithay_clipboard(
175 raw_display_handle: Option<RawDisplayHandle>,
176) -> Option<smithay_clipboard::Clipboard> {
177 #![expect(clippy::undocumented_unsafe_blocks)]
178
179 profiling::function_scope!();
180
181 if let Some(RawDisplayHandle::Wayland(display)) = raw_display_handle {
182 log::trace!("Initializing smithay clipboard…");
183 #[expect(unsafe_code)]
184 Some(unsafe { smithay_clipboard::Clipboard::new(display.display.as_ptr()) })
185 } else {
186 #[cfg(feature = "wayland")]
187 log::debug!("Cannot init smithay clipboard without a Wayland display handle");
188 #[cfg(not(feature = "wayland"))]
189 log::debug!(
190 "Cannot init smithay clipboard: the 'wayland' feature of 'egui-winit' is not enabled"
191 );
192 None
193 }
194}