From 3b9bcfa169a34e9a57280a4c33b8dc8bacb95f24 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 | 15 ++++++++++-- egui-winit/Cargo.toml | 3 ++- egui-winit/src/clipboard.rs | 47 ++++++++++++++++++++++++++++++++----- egui-winit/src/lib.rs | 19 ++++++++++++--- 4 files changed, 72 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 10cc343cfdf6..284b8c3ea06c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1119,6 +1119,7 @@ dependencies = [ "instant", "puffin", "serde", + "smithay-clipboard", "tracing", "tts", "webbrowser", @@ -3118,6 +3119,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" @@ -3506,7 +3517,7 @@ version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ee73e6e4924fe940354b8d4d98cad5231175d615cd855b758adc658c0aac6a0" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", "static_assertions", ] @@ -3856,7 +3867,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc6a3cffdb686fbb24d9fb8f03a213803277ed2300f11026a3afe1f108dc021b" dependencies = [ "jni", - "ndk-glue 0.5.2", + "ndk-glue 0.6.2", "url", "web-sys", "widestring", diff --git a/egui-winit/Cargo.toml b/egui-winit/Cargo.toml index b999ef19ee59..6f88dc453617 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"] @@ -49,6 +49,7 @@ tracing = "0.1" winit = "0.26.1" arboard = { version = "2.1", optional = true, default-features = false } +smithay-clipboard = { version = "0.6.3", optional = true } puffin = { version = "0.13", optional = true } serde = { version = "1.0", optional = true, features = ["derive"] } webbrowser = { version = "0.7", optional = true } diff --git a/egui-winit/src/clipboard.rs b/egui-winit/src/clipboard.rs index 8737bfd9f968..46e92c9d2842 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(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(feature = "smithay-clipboard")] + smithay: init_smithay_clipboard(wayland_display), + clipboard: Default::default(), } } -} -impl Clipboard { pub fn get(&mut self) -> Option { + #[cfg(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(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(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 47129ac2fe48..056fcbf198d4 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,21 @@ 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)) + Self::from_pixels_per_point( + max_texture_side, + native_pixels_per_point(window), + 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 +85,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,