Browse Source

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.
pull/1639/head
Antti Keränen 3 years ago
committed by GitHub
parent
commit
b8a5924295
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 11
      Cargo.lock
  2. 5
      egui-winit/Cargo.toml
  3. 92
      egui-winit/src/clipboard.rs
  4. 42
      egui-winit/src/lib.rs

11
Cargo.lock

@ -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"

5
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(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 }

92
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,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
}
}

42
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,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"

Loading…
Cancel
Save