Fix clipboard on Wayland (#1613)

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.
This commit is contained in:
Antti Keränen 2022-05-16 17:37:41 +03:00 committed by GitHub
parent b008b147e5
commit b8a5924295
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 140 additions and 10 deletions

11
Cargo.lock generated
View file

@ -1180,6 +1180,7 @@ dependencies = [
"instant",
"puffin",
"serde",
"smithay-clipboard",
"tracing",
"tts",
"webbrowser",
@ -3308,6 +3309,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"

View file

@ -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(any(target_os="linux", target_os="dragonfly", target_os="freebsd", target_os="netbsd", target_os="openbsd"))'.dependencies]
smithay-clipboard = { version = "0.6.3", optional = true }

View file

@ -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,64 @@ pub struct Clipboard {
#[cfg(feature = "arboard")]
arboard: Option<arboard::Clipboard>,
#[cfg(all(
any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
),
feature = "smithay-clipboard"
))]
smithay: Option<smithay_clipboard::Clipboard>,
/// 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(
any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
),
feature = "smithay-clipboard"
))]
smithay: init_smithay_clipboard(wayland_display),
clipboard: Default::default(),
}
}
}
impl Clipboard {
pub fn get(&mut self) -> Option<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 {
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 +81,21 @@ impl Clipboard {
}
pub fn set(&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(feature = "arboard")]
if let Some(clipboard) = &mut self.arboard {
if let Err(err) = clipboard.set_text(text) {
@ -60,3 +118,25 @@ fn init_arboard() -> Option<arboard::Clipboard> {
}
}
}
#[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(
wayland_display: Option<*mut c_void>,
) -> Option<smithay_clipboard::Clipboard> {
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
}
}

View file

@ -5,6 +5,8 @@
#![allow(clippy::manual_range_contains)]
use std::os::raw::c_void;
pub use egui;
pub use winit;
@ -14,6 +16,15 @@ mod window_settings;
pub use window_settings::WindowSettings;
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
use winit::platform::unix::WindowExtUnix;
pub fn native_pixels_per_point(window: &winit::window::Window) -> f32 {
window.scale_factor() as f32
}
@ -53,13 +64,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),
get_wayland_display(window),
)
}
/// 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 +91,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,
@ -659,6 +678,23 @@ fn translate_cursor(cursor_icon: egui::CursorIcon) -> Option<winit::window::Curs
}
}
/// Returns a Wayland display handle if the target is running Wayland
fn get_wayland_display(window: &winit::window::Window) -> Option<*mut c_void> {
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
{
return window.wayland_display();
}
#[allow(unreachable_code)]
None
}
// ---------------------------------------------------------------------------
/// Profiling macro for feature "puffin"