use raw_window_handle::RawDisplayHandle; /// Handles interfacing with the OS clipboard. /// /// If the "clipboard" feature is off, or we cannot connect to the OS clipboard, /// then a fallback clipboard that just works within the same app is used instead. pub struct Clipboard { #[cfg(all(feature = "arboard", not(target_os = "android")))] arboard: Option, #[cfg(all( any( target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd" ), feature = "smithay-clipboard" ))] smithay: Option, /// Fallback manual clipboard. clipboard: String, } impl Clipboard { /// Construct a new instance pub fn new(_raw_display_handle: Option) -> Self { Self { #[cfg(all(feature = "arboard", not(target_os = "android")))] arboard: init_arboard(), #[cfg(all( any( target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd" ), feature = "smithay-clipboard" ))] smithay: init_smithay_clipboard(_raw_display_handle), clipboard: Default::default(), } } pub fn get(&mut self) -> Option { #[cfg(all( any( target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd" ), feature = "smithay-clipboard" ))] if let Some(clipboard) = &mut self.smithay { return match clipboard.load() { Ok(text) => Some(text), Err(err) => { log::error!("smithay paste error: {err}"); None } }; } #[cfg(all(feature = "arboard", not(target_os = "android")))] if let Some(clipboard) = &mut self.arboard { return match clipboard.get_text() { Ok(text) => Some(text), Err(err) => { log::error!("arboard paste error: {err}"); None } }; } Some(self.clipboard.clone()) } pub fn set_text(&mut self, text: String) { #[cfg(all( any( target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd" ), feature = "smithay-clipboard" ))] if let Some(clipboard) = &mut self.smithay { clipboard.store(text); return; } #[cfg(all(feature = "arboard", not(target_os = "android")))] if let Some(clipboard) = &mut self.arboard { if let Err(err) = clipboard.set_text(text) { log::error!("arboard copy/cut error: {err}"); } return; } self.clipboard = text; } pub fn set_image(&mut self, image: &egui::ColorImage) { #[cfg(all(feature = "arboard", not(target_os = "android")))] if let Some(clipboard) = &mut self.arboard { if let Err(err) = clipboard.set_image(arboard::ImageData { width: image.width(), height: image.height(), bytes: std::borrow::Cow::Borrowed(bytemuck::cast_slice(&image.pixels)), }) { log::error!("arboard copy/cut error: {err}"); } log::debug!("Copied image to clipboard"); return; } log::error!( "Copying images is not supported. Enable the 'clipboard' feature of `egui-winit` to enable it." ); _ = image; } } #[cfg(all(feature = "arboard", not(target_os = "android")))] fn init_arboard() -> Option { profiling::function_scope!(); log::trace!("Initializing arboard clipboard…"); match arboard::Clipboard::new() { Ok(clipboard) => Some(clipboard), Err(err) => { log::warn!("Failed to initialize arboard clipboard: {err}"); None } } } #[cfg(all( any( target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd" ), feature = "smithay-clipboard" ))] fn init_smithay_clipboard( raw_display_handle: Option, ) -> Option { #![allow(clippy::undocumented_unsafe_blocks)] profiling::function_scope!(); if let Some(RawDisplayHandle::Wayland(display)) = raw_display_handle { log::trace!("Initializing smithay clipboard…"); #[expect(unsafe_code)] Some(unsafe { smithay_clipboard::Clipboard::new(display.display.as_ptr()) }) } else { #[cfg(feature = "wayland")] log::debug!("Cannot init smithay clipboard without a Wayland display handle"); #[cfg(not(feature = "wayland"))] log::debug!( "Cannot init smithay clipboard: the 'wayland' feature of 'egui-winit' is not enabled" ); None } }