egui_winit/
clipboard.rs

1use raw_window_handle::RawDisplayHandle;
2
3/// Handles interfacing with the OS clipboard.
4///
5/// If the "clipboard" feature is off, or we cannot connect to the OS clipboard,
6/// then a fallback clipboard that just works within the same app is used instead.
7pub 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    /// Fallback manual clipboard.
27    clipboard: String,
28}
29
30impl Clipboard {
31    /// Construct a new instance
32    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}