Skip to content

Commit

Permalink
Fix clipboard on Wayland
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Detegr committed May 16, 2022
1 parent b008b14 commit d9cd34a
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 10 deletions.
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion egui-winit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down Expand Up @@ -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 changes: 86 additions & 6 deletions egui-winit/src/clipboard.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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() {
Expand All @@ -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) {
Expand All @@ -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 changes: 39 additions & 3 deletions egui-winit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

#![allow(clippy::manual_range_contains)]

use std::os::raw::c_void;

pub use egui;
pub use winit;

Expand All @@ -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
}
Expand Down Expand Up @@ -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 {
Expand All @@ -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,
Expand Down Expand Up @@ -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"
Expand Down

0 comments on commit d9cd34a

Please sign in to comment.