linux: clipboard (#8822)

Linux clipboard implementation with `copypasta`.


Release Notes:

- Added linux clipboard support
This commit is contained in:
Rom Grk 2024-03-04 11:00:24 -05:00 committed by GitHub
parent 33ef5b7731
commit 996f1036fc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 216 additions and 9 deletions

158
Cargo.lock generated
View file

@ -2141,6 +2141,16 @@ dependencies = [
"util",
]
[[package]]
name = "clipboard-win"
version = "3.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fdf5e01086b6be750428ba4a40619f847eb2e95756eee84b18e06e5f0b50342"
dependencies = [
"lazy-bytes-cast",
"winapi 0.3.9",
]
[[package]]
name = "clock"
version = "0.1.0"
@ -2490,6 +2500,20 @@ dependencies = [
"zed_actions",
]
[[package]]
name = "copypasta"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "deb85422867ca93da58b7f95fb5c0c10f6183ed6e1ef8841568968a896d3a858"
dependencies = [
"clipboard-win",
"objc",
"objc-foundation",
"objc_id",
"smithay-clipboard",
"x11-clipboard",
]
[[package]]
name = "core-foundation"
version = "0.9.4"
@ -4147,6 +4171,16 @@ dependencies = [
"version_check",
]
[[package]]
name = "gethostname"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818"
dependencies = [
"libc",
"windows-targets 0.48.5",
]
[[package]]
name = "getrandom"
version = "0.1.16"
@ -4325,6 +4359,7 @@ dependencies = [
"cbindgen",
"cocoa",
"collections",
"copypasta",
"core-foundation",
"core-graphics",
"core-text",
@ -5333,6 +5368,12 @@ dependencies = [
"util",
]
[[package]]
name = "lazy-bytes-cast"
version = "5.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10257499f089cd156ad82d0a9cd57d9501fa2c989068992a97eb3c27836f206b"
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -5733,6 +5774,15 @@ dependencies = [
"libc",
]
[[package]]
name = "memmap2"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322"
dependencies = [
"libc",
]
[[package]]
name = "memoffset"
version = "0.7.1"
@ -6376,6 +6426,17 @@ dependencies = [
"objc_exception",
]
[[package]]
name = "objc-foundation"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9"
dependencies = [
"block",
"objc",
"objc_id",
]
[[package]]
name = "objc_exception"
version = "0.1.2"
@ -6385,6 +6446,15 @@ dependencies = [
"cc",
]
[[package]]
name = "objc_id"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b"
dependencies = [
"objc",
]
[[package]]
name = "object"
version = "0.32.1"
@ -8915,6 +8985,42 @@ version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a"
[[package]]
name = "smithay-client-toolkit"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "922fd3eeab3bd820d76537ce8f582b1cf951eceb5475c28500c7457d9d17f53a"
dependencies = [
"bitflags 2.4.2",
"calloop",
"calloop-wayland-source",
"cursor-icon",
"libc",
"log",
"memmap2 0.9.4",
"rustix 0.38.30",
"thiserror",
"wayland-backend",
"wayland-client",
"wayland-csd-frame",
"wayland-cursor",
"wayland-protocols",
"wayland-protocols-wlr",
"wayland-scanner",
"xkeysym",
]
[[package]]
name = "smithay-clipboard"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c091e7354ea8059d6ad99eace06dd13ddeedbb0ac72d40a9a6e7ff790525882d"
dependencies = [
"libc",
"smithay-client-toolkit",
"wayland-backend",
]
[[package]]
name = "smol"
version = "1.3.0"
@ -11730,6 +11836,17 @@ dependencies = [
"wayland-scanner",
]
[[package]]
name = "wayland-csd-frame"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e"
dependencies = [
"bitflags 2.4.2",
"cursor-icon",
"wayland-backend",
]
[[package]]
name = "wayland-cursor"
version = "0.31.1"
@ -11753,6 +11870,19 @@ dependencies = [
"wayland-scanner",
]
[[package]]
name = "wayland-protocols-wlr"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6"
dependencies = [
"bitflags 2.4.2",
"wayland-backend",
"wayland-client",
"wayland-protocols",
"wayland-scanner",
]
[[package]]
name = "wayland-scanner"
version = "0.31.1"
@ -11772,6 +11902,7 @@ checksum = "15a0c8eaff5216d07f226cb7a549159267f3467b289d9a2e52fd3ef5aae2b7af"
dependencies = [
"dlib",
"log",
"once_cell",
"pkg-config",
]
@ -12417,6 +12548,33 @@ dependencies = [
"tap",
]
[[package]]
name = "x11-clipboard"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b98785a09322d7446e28a13203d2cae1059a0dd3dfb32cb06d0a225f023d8286"
dependencies = [
"libc",
"x11rb",
]
[[package]]
name = "x11rb"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8f25ead8c7e4cba123243a6367da5d3990e0d3affa708ea19dce96356bd9f1a"
dependencies = [
"gethostname",
"rustix 0.38.30",
"x11rb-protocol",
]
[[package]]
name = "x11rb-protocol"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e63e71c4b8bd9ffec2c963173a4dc4cbde9ee96961d4fcb4429db9929b606c34"
[[package]]
name = "xattr"
version = "0.2.3"

View file

@ -113,6 +113,7 @@ xkbcommon = { version = "0.7", features = ["wayland", "x11"] }
as-raw-xcb-connection = "1"
calloop = "0.12.4"
calloop-wayland-source = "0.2.0"
copypasta = "0.10.1"
oo7 = "0.3.0"
[target.'cfg(windows)'.dependencies]

View file

@ -1,5 +1,8 @@
use std::cell::RefCell;
use std::rc::Rc;
use copypasta::ClipboardProvider;
use crate::platform::PlatformWindow;
use crate::{AnyWindowHandle, CursorStyle, DisplayId, PlatformDisplay, WindowOptions};
@ -12,4 +15,6 @@ pub trait Client {
options: WindowOptions,
) -> Box<dyn PlatformWindow>;
fn set_cursor_style(&self, style: CursorStyle);
fn get_clipboard(&self) -> Rc<RefCell<dyn ClipboardProvider>>;
fn get_primary(&self) -> Rc<RefCell<dyn ClipboardProvider>>;
}

View file

@ -354,12 +354,21 @@ impl Platform for LinuxPlatform {
false
}
// todo(linux)
fn write_to_clipboard(&self, item: ClipboardItem) {}
fn write_to_clipboard(&self, item: ClipboardItem) {
let clipboard = self.client.get_clipboard();
clipboard.borrow_mut().set_contents(item.text);
}
// todo(linux)
fn read_from_clipboard(&self) -> Option<ClipboardItem> {
None
let clipboard = self.client.get_clipboard();
let contents = clipboard.borrow_mut().get_contents();
match contents {
Ok(text) => Some(ClipboardItem {
metadata: None,
text,
}),
_ => None,
}
}
//todo!(linux)
@ -382,6 +391,8 @@ impl Platform for LinuxPlatform {
})
}
//todo!(linux): add trait methods for accessing the primary selection
//todo!(linux)
fn read_credentials(&self, url: &str) -> Task<Result<Option<(String, Vec<u8>)>>> {
let url = url.to_string();

View file

@ -6,6 +6,8 @@ use std::time::Duration;
use calloop::timer::{TimeoutAction, Timer};
use calloop::LoopHandle;
use calloop_wayland_source::WaylandSource;
use copypasta::wayland_clipboard::{create_clipboards_from_external, Clipboard, Primary};
use copypasta::ClipboardProvider;
use wayland_backend::client::ObjectId;
use wayland_backend::protocol::WEnum;
use wayland_client::globals::{registry_queue_init, GlobalListContents};
@ -74,6 +76,8 @@ pub(crate) struct CursorState {
pub(crate) struct WaylandClientState {
client_state_inner: Rc<RefCell<WaylandClientStateInner>>,
cursor_state: Rc<RefCell<CursorState>>,
clipboard: Rc<RefCell<Clipboard>>,
primary: Rc<RefCell<Primary>>,
}
pub(crate) struct KeyRepeat {
@ -111,6 +115,9 @@ impl WaylandClient {
}
});
let display = conn.backend().display_ptr() as *mut std::ffi::c_void;
let (primary, clipboard) = unsafe { create_clipboards_from_external(display) };
let mut state_inner = Rc::new(RefCell::new(WaylandClientStateInner {
compositor: globals.bind(&qh, 1..=1, ()).unwrap(),
wm_base: globals.bind(&qh, 1..=1, ()).unwrap(),
@ -152,20 +159,20 @@ impl WaylandClient {
let mut state = WaylandClientState {
client_state_inner: Rc::clone(&state_inner),
cursor_state: Rc::clone(&cursor_state),
clipboard: Rc::new(RefCell::new(clipboard)),
primary: Rc::new(RefCell::new(primary)),
};
let mut state_loop = state.clone();
linux_platform_inner
.loop_handle
.insert_source(source, move |_, queue, _| {
queue.dispatch_pending(&mut state)
queue.dispatch_pending(&mut state_loop)
})
.unwrap();
Self {
platform_inner: linux_platform_inner,
state: WaylandClientState {
client_state_inner: state_inner,
cursor_state,
},
state,
qh: Arc::new(qh),
}
}
@ -265,6 +272,14 @@ impl Client for WaylandClient {
let mut cursor_state = self.state.cursor_state.borrow_mut();
cursor_state.cursor_icon_name = cursor_icon_name;
}
fn get_clipboard(&self) -> Rc<RefCell<dyn ClipboardProvider>> {
self.state.clipboard.clone()
}
fn get_primary(&self) -> Rc<RefCell<dyn ClipboardProvider>> {
self.state.primary.clone()
}
}
impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for WaylandClientState {

View file

@ -6,6 +6,8 @@ use xcb::{x, Xid as _};
use xkbcommon::xkb;
use collections::HashMap;
use copypasta::x11_clipboard::{Clipboard, Primary, X11ClipboardContext};
use copypasta::ClipboardProvider;
use crate::platform::linux::client::Client;
use crate::platform::{LinuxPlatformInner, PlatformWindow};
@ -28,6 +30,8 @@ struct WindowRef {
struct X11ClientState {
windows: HashMap<x::Window, WindowRef>,
xkb: xkbcommon::xkb::State,
clipboard: Rc<RefCell<X11ClipboardContext<Clipboard>>>,
primary: Rc<RefCell<X11ClipboardContext<Primary>>>,
}
pub(crate) struct X11Client {
@ -70,6 +74,9 @@ impl X11Client {
xkb::x11::state_new_from_device(&xkb_keymap, &xcb_connection, xkb_device_id)
};
let clipboard = X11ClipboardContext::<Clipboard>::new().unwrap();
let primary = X11ClipboardContext::<Primary>::new().unwrap();
let client: Rc<X11Client> = Rc::new(Self {
platform_inner: inner.clone(),
xcb_connection: Rc::clone(&xcb_connection),
@ -78,6 +85,8 @@ impl X11Client {
state: RefCell::new(X11ClientState {
windows: HashMap::default(),
xkb: xkb_state,
clipboard: Rc::new(RefCell::new(clipboard)),
primary: Rc::new(RefCell::new(primary)),
}),
});
@ -354,6 +363,14 @@ impl Client for X11Client {
//todo!(linux)
fn set_cursor_style(&self, _style: CursorStyle) {}
fn get_clipboard(&self) -> Rc<RefCell<dyn ClipboardProvider>> {
self.state.borrow().clipboard.clone()
}
fn get_primary(&self) -> Rc<RefCell<dyn ClipboardProvider>> {
self.state.borrow().primary.clone()
}
}
// Adatpted from: