diff --git a/crates/gpui/src/platform/linux/wayland/client.rs b/crates/gpui/src/platform/linux/wayland/client.rs index 6b7cfd62d8341dfd7a6066bfb16f07b2ebdeeaa9..973e4a12886bf8aee1fab2a31806938d7198c55f 100644 --- a/crates/gpui/src/platform/linux/wayland/client.rs +++ b/crates/gpui/src/platform/linux/wayland/client.rs @@ -671,7 +671,7 @@ impl LinuxClient for WaylandClient { return; }; if state.mouse_focused_window.is_some() || state.keyboard_focused_window.is_some() { - state.clipboard.set_primary(item.text); + state.clipboard.set_primary(item); let serial = state.serial_tracker.get(SerialKind::KeyPress); let data_source = primary_selection_manager.create_source(&state.globals.qh, ()); data_source.offer(state.clipboard.self_mime()); @@ -689,7 +689,7 @@ impl LinuxClient for WaylandClient { return; }; if state.mouse_focused_window.is_some() || state.keyboard_focused_window.is_some() { - state.clipboard.set(item.text); + state.clipboard.set(item); let serial = state.serial_tracker.get(SerialKind::KeyPress); let data_source = data_device_manager.create_data_source(&state.globals.qh, ()); data_source.offer(state.clipboard.self_mime()); @@ -699,25 +699,11 @@ impl LinuxClient for WaylandClient { } fn read_from_primary(&self) -> Option { - self.0 - .borrow_mut() - .clipboard - .read_primary() - .map(|s| crate::ClipboardItem { - text: s, - metadata: None, - }) + self.0.borrow_mut().clipboard.read_primary() } fn read_from_clipboard(&self) -> Option { - self.0 - .borrow_mut() - .clipboard - .read() - .map(|s| crate::ClipboardItem { - text: s, - metadata: None, - }) + self.0.borrow_mut().clipboard.read() } fn active_window(&self) -> Option { diff --git a/crates/gpui/src/platform/linux/wayland/clipboard.rs b/crates/gpui/src/platform/linux/wayland/clipboard.rs index ec1ced07fe1f557fe8deb36a041a51a6fb629908..3df06123afad8d2da33009faac2a81a123277f94 100644 --- a/crates/gpui/src/platform/linux/wayland/clipboard.rs +++ b/crates/gpui/src/platform/linux/wayland/clipboard.rs @@ -9,7 +9,7 @@ use filedescriptor::Pipe; use wayland_client::{protocol::wl_data_offer::WlDataOffer, Connection}; use wayland_protocols::wp::primary_selection::zv1::client::zwp_primary_selection_offer_v1::ZwpPrimarySelectionOfferV1; -use crate::{platform::linux::platform::read_fd, WaylandClientStatePtr}; +use crate::{platform::linux::platform::read_fd, ClipboardItem, WaylandClientStatePtr}; pub(crate) const TEXT_MIME_TYPE: &str = "text/plain;charset=utf-8"; pub(crate) const FILE_LIST_MIME_TYPE: &str = "text/uri-list"; @@ -23,13 +23,13 @@ pub(crate) struct Clipboard { self_mime: String, // Internal clipboard - contents: Option, - primary_contents: Option, + contents: Option, + primary_contents: Option, // External clipboard - cached_read: Option, + cached_read: Option, current_offer: Option>, - cached_primary_read: Option, + cached_primary_read: Option, current_primary_offer: Option>, } @@ -89,12 +89,12 @@ impl Clipboard { } } - pub fn set(&mut self, text: String) { - self.contents = Some(text); + pub fn set(&mut self, item: ClipboardItem) { + self.contents = Some(item); } - pub fn set_primary(&mut self, text: String) { - self.primary_contents = Some(text); + pub fn set_primary(&mut self, item: ClipboardItem) { + self.primary_contents = Some(item); } pub fn set_offer(&mut self, data_offer: Option>) { @@ -113,17 +113,17 @@ impl Clipboard { pub fn send(&self, _mime_type: String, fd: OwnedFd) { if let Some(contents) = &self.contents { - self.send_internal(fd, contents.as_bytes().to_owned()); + self.send_internal(fd, contents.text.as_bytes().to_owned()); } } pub fn send_primary(&self, _mime_type: String, fd: OwnedFd) { if let Some(primary_contents) = &self.primary_contents { - self.send_internal(fd, primary_contents.as_bytes().to_owned()); + self.send_internal(fd, primary_contents.text.as_bytes().to_owned()); } } - pub fn read(&mut self) -> Option { + pub fn read(&mut self) -> Option { let offer = self.current_offer.clone()?; if let Some(cached) = self.cached_read.clone() { return Some(cached); @@ -145,8 +145,8 @@ impl Clipboard { match unsafe { read_fd(fd) } { Ok(v) => { - self.cached_read = Some(v.clone()); - Some(v) + self.cached_read = Some(ClipboardItem::new(v)); + self.cached_read.clone() } Err(err) => { log::error!("error reading clipboard pipe: {err:?}"); @@ -155,7 +155,7 @@ impl Clipboard { } } - pub fn read_primary(&mut self) -> Option { + pub fn read_primary(&mut self) -> Option { let offer = self.current_primary_offer.clone()?; if let Some(cached) = self.cached_primary_read.clone() { return Some(cached); @@ -177,8 +177,8 @@ impl Clipboard { match unsafe { read_fd(fd) } { Ok(v) => { - self.cached_primary_read = Some(v.clone()); - Some(v) + self.cached_primary_read = Some(ClipboardItem::new(v.clone())); + self.cached_primary_read.clone() } Err(err) => { log::error!("error reading clipboard pipe: {err:?}"); diff --git a/crates/gpui/src/platform/linux/x11/client.rs b/crates/gpui/src/platform/linux/x11/client.rs index b630e5dfff37411fcc2e39477569270e66cb337f..347c597b714d27ad335a94bb9692c119e44b7589 100644 --- a/crates/gpui/src/platform/linux/x11/client.rs +++ b/crates/gpui/src/platform/linux/x11/client.rs @@ -29,9 +29,9 @@ use xkbcommon::xkb as xkbc; use crate::platform::linux::LinuxClient; use crate::platform::{LinuxCommon, PlatformWindow}; use crate::{ - modifiers_from_xinput_info, point, px, AnyWindowHandle, Bounds, CursorStyle, DisplayId, - Keystroke, Modifiers, ModifiersChangedEvent, Pixels, PlatformDisplay, PlatformInput, Point, - ScrollDelta, Size, TouchPhase, WindowParams, X11Window, + modifiers_from_xinput_info, point, px, AnyWindowHandle, Bounds, ClipboardItem, CursorStyle, + DisplayId, Keystroke, Modifiers, ModifiersChangedEvent, Pixels, PlatformDisplay, PlatformInput, + Point, ScrollDelta, Size, TouchPhase, WindowParams, X11Window, }; use super::{ @@ -129,6 +129,7 @@ pub struct X11ClientState { pub(crate) common: LinuxCommon, pub(crate) clipboard: x11_clipboard::Clipboard, + pub(crate) clipboard_item: Option, } #[derive(Clone)] @@ -413,6 +414,7 @@ impl X11Client { scroll_y: None, clipboard, + clipboard_item: None, }))) } @@ -1097,7 +1099,7 @@ impl LinuxClient for X11Client { } fn write_to_clipboard(&self, item: crate::ClipboardItem) { - let state = self.0.borrow_mut(); + let mut state = self.0.borrow_mut(); state .clipboard .store( @@ -1106,6 +1108,7 @@ impl LinuxClient for X11Client { item.text().as_bytes(), ) .ok(); + state.clipboard_item.replace(item); } fn read_from_primary(&self) -> Option { @@ -1127,6 +1130,20 @@ impl LinuxClient for X11Client { fn read_from_clipboard(&self) -> Option { let state = self.0.borrow_mut(); + // if the last copy was from this app, return our cached item + // which has metadata attached. + if state + .clipboard + .setter + .connection + .get_selection_owner(state.clipboard.setter.atoms.clipboard) + .ok() + .and_then(|r| r.reply().ok()) + .map(|reply| reply.owner == state.clipboard.setter.window) + .unwrap_or(false) + { + return state.clipboard_item.clone(); + } state .clipboard .load( diff --git a/crates/gpui/src/platform/linux/x11/window.rs b/crates/gpui/src/platform/linux/x11/window.rs index 4dea09393d523348081fe59bae4d88a5e5c55125..3497415613309ef0499476385c67ee73b8b2bf52 100644 --- a/crates/gpui/src/platform/linux/x11/window.rs +++ b/crates/gpui/src/platform/linux/x11/window.rs @@ -34,7 +34,6 @@ use std::{ }; use super::{X11Display, XINPUT_MASTER_DEVICE}; - x11rb::atom_manager! { pub XcbAtoms: AtomsCookie { UTF8_STRING,