From 901fe64fb50e5953dd3802830d922c9d5e025cd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antti=20Ker=C3=A4nen?= Date: Mon, 9 May 2022 22:01:17 +0300 Subject: [PATCH] Fix clipboard on Wayland arboard advertises that it works with Wayland, but in reality it only works with Wayland terminal applications. To make the clipboard work with applications that draw Wayland surfaces, arboard isn't going to work. Copypasta does support Wayland's graphical clipboard, but the usage isn't documented. However, for the reasons mentioned in #1474 the move from Copypasta to arboard makes sense. To resolve the issue, this commit brings in an optional dependency smithay-clipboard, that is a crate that correctly handles the Wayland clipboard in graphical applications. It is used by default if a Wayland window handle is found. If for some reason the handle to the Wayland window handle cannot be fetched, arboard is used as a backup. --- Cargo.lock | 11 +++++++++ egui-winit/Cargo.toml | 5 +++- egui-winit/src/clipboard.rs | 47 ++++++++++++++++++++++++++++++++----- egui-winit/src/lib.rs | 24 ++++++++++++++++--- 4 files changed, 77 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c014a07639b8..0f1fa699cb23 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1174,6 +1174,7 @@ dependencies = [ "instant", "puffin", "serde", + "smithay-clipboard", "tracing", "tts", "webbrowser", @@ -3301,6 +3302,16 @@ dependencies = [ "wayland-protocols", ] +[[package]] +name = "smithay-clipboard" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "610b551bd25378bfd2b8e7a0fcbd83d427e8f2f6a40c47ae0f70688e9949dd55" +dependencies = [ + "smithay-client-toolkit", + "wayland-client", +] + [[package]] name = "socket2" version = "0.4.4" diff --git a/egui-winit/Cargo.toml b/egui-winit/Cargo.toml index b999ef19ee59..8c40aa837562 100644 --- a/egui-winit/Cargo.toml +++ b/egui-winit/Cargo.toml @@ -25,7 +25,7 @@ bytemuck = ["egui/bytemuck"] # enable cut/copy/paste to OS clipboard. # if disabled a clipboard will be simulated so you can still copy/paste within the egui app. -clipboard = ["arboard"] +clipboard = ["arboard", "smithay-clipboard"] # enable opening links in a browser when an egui hyperlink is clicked. links = ["webbrowser"] @@ -55,3 +55,6 @@ webbrowser = { version = "0.7", optional = true } # feature screen_reader tts = { version = "0.20", optional = true } # stuck on old version due to compilation problems on linux + +[target.'cfg(target_os = "linux")'.dependencies] +smithay-clipboard = { version = "0.6.3", optional = true } diff --git a/egui-winit/src/clipboard.rs b/egui-winit/src/clipboard.rs index 8737bfd9f968..a87ae2fda9ac 100644 --- a/egui-winit/src/clipboard.rs +++ b/egui-winit/src/clipboard.rs @@ -1,3 +1,5 @@ +use std::os::raw::c_void; + /// Handles interfacing with the OS clipboard. /// /// If the "clipboard" feature is off, or we cannot connect to the OS clipboard, @@ -6,23 +8,37 @@ pub struct Clipboard { #[cfg(feature = "arboard")] arboard: Option, + #[cfg(all(target_os = "linux", feature = "smithay-clipboard"))] + smithay: Option, + /// Fallback manual clipboard. clipboard: String, } -impl Default for Clipboard { - fn default() -> Self { +impl Clipboard { + #[allow(unused_variables)] + pub fn new(#[allow(unused_variables)] wayland_display: Option<*mut c_void>) -> Self { Self { #[cfg(feature = "arboard")] arboard: init_arboard(), - - clipboard: String::default(), + #[cfg(all(target_os = "linux", feature = "smithay-clipboard"))] + smithay: init_smithay_clipboard(wayland_display), + clipboard: Default::default(), } } -} -impl Clipboard { pub fn get(&mut self) -> Option { + #[cfg(all(target_os = "linux", feature = "smithay-clipboard"))] + if let Some(clipboard) = &mut self.smithay { + return match clipboard.load() { + Ok(text) => Some(text), + Err(err) => { + tracing::error!("Paste error: {}", err); + None + } + }; + } + #[cfg(feature = "arboard")] if let Some(clipboard) = &mut self.arboard { return match clipboard.get_text() { @@ -38,6 +54,12 @@ impl Clipboard { } pub fn set(&mut self, text: String) { + #[cfg(all(target_os = "linux", feature = "smithay-clipboard"))] + if let Some(clipboard) = &mut self.smithay { + clipboard.store(text); + return; + } + #[cfg(feature = "arboard")] if let Some(clipboard) = &mut self.arboard { if let Err(err) = clipboard.set_text(text) { @@ -60,3 +82,16 @@ fn init_arboard() -> Option { } } } + +#[cfg(all(target_os = "linux", feature = "smithay-clipboard"))] +fn init_smithay_clipboard( + wayland_display: Option<*mut c_void>, +) -> Option { + if let Some(display) = wayland_display { + #[allow(unsafe_code)] + Some(unsafe { smithay_clipboard::Clipboard::new(display) }) + } else { + tracing::error!("Cannot initialize smithay clipboard without a display handle!"); + None + } +} diff --git a/egui-winit/src/lib.rs b/egui-winit/src/lib.rs index 4286af84c2e6..976aa85b1505 100644 --- a/egui-winit/src/lib.rs +++ b/egui-winit/src/lib.rs @@ -5,6 +5,8 @@ #![allow(clippy::manual_range_contains)] +use std::os::raw::c_void; + pub use egui; pub use winit; @@ -14,6 +16,9 @@ mod window_settings; pub use window_settings::WindowSettings; +#[cfg(unix)] +use winit::platform::unix::WindowExtUnix; + pub fn native_pixels_per_point(window: &winit::window::Window) -> f32 { window.scale_factor() as f32 } @@ -53,13 +58,26 @@ impl State { /// * `max_texture_side`: e.g. `GL_MAX_TEXTURE_SIZE` /// * the native `pixels_per_point` (dpi scaling). pub fn new(max_texture_side: usize, window: &winit::window::Window) -> Self { - Self::from_pixels_per_point(max_texture_side, native_pixels_per_point(window)) + #[cfg(unix)] + let wayland_display = window.wayland_display(); + #[cfg(not(unix))] + let wayland_display = None; + + Self::from_pixels_per_point( + max_texture_side, + native_pixels_per_point(window), + wayland_display, + ) } /// Initialize with: /// * `max_texture_side`: e.g. `GL_MAX_TEXTURE_SIZE` /// * the given `pixels_per_point` (dpi scaling). - pub fn from_pixels_per_point(max_texture_side: usize, pixels_per_point: f32) -> Self { + pub fn from_pixels_per_point( + max_texture_side: usize, + pixels_per_point: f32, + wayland_display: Option<*mut c_void>, + ) -> Self { Self { start_time: instant::Instant::now(), egui_input: egui::RawInput { @@ -72,7 +90,7 @@ impl State { current_cursor_icon: egui::CursorIcon::Default, current_pixels_per_point: pixels_per_point, - clipboard: Default::default(), + clipboard: clipboard::Clipboard::new(wayland_display), screen_reader: screen_reader::ScreenReader::default(), simulate_touch_screen: false,