From 8e7827ebbe8cfd3d0badd1c4a62e28b51c7fd7a1 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Thu, 29 Feb 2024 14:30:53 -0500 Subject: [PATCH 01/27] refactor: allow clipboard methods to take generic data --- src/lib.rs | 40 +++++++++++-------- src/platform/android.rs | 4 +- src/platform/dummy.rs | 8 ++-- src/platform/ios.rs | 4 +- src/platform/linux.rs | 86 +++++++++++++++++++++-------------------- src/platform/macos.rs | 7 ++-- src/platform/windows.rs | 4 +- wayland/Cargo.toml | 2 +- wayland/src/lib.rs | 27 +++++++------ 9 files changed, 100 insertions(+), 82 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a9857e3..d3f0c61 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +pub mod mime; + #[cfg(all( unix, not(any( @@ -49,49 +51,55 @@ mod platform; use raw_window_handle::HasDisplayHandle; use std::error::Error; -pub struct Clipboard { - raw: Box, +pub struct Clipboard { + raw: C, } -impl Clipboard { +impl Clipboard { /// Safety: the display handle must be valid for the lifetime of `Clipboard` pub unsafe fn connect( window: &W, ) -> Result> { - let raw = platform::connect(window)?; - - Ok(Clipboard { raw }) + Ok(Clipboard { + raw: platform::connect(window)?, + }) } pub fn read(&self) -> Result> { - self.raw.read() + self.raw.read_text() } pub fn write(&mut self, contents: String) -> Result<(), Box> { - self.raw.write(contents) + self.raw.write_text(contents) } } -impl Clipboard { +impl Clipboard { pub fn read_primary(&self) -> Option>> { - self.raw.read_primary() + self.raw.read_primary_text() } - pub fn write_primary(&mut self, contents: String) -> Option>> { - self.raw.write_primary(contents) + pub fn write_primary( + &mut self, + contents: String, + ) -> Option>> { + self.raw.write_primary_text(contents) } } pub trait ClipboardProvider { - fn read(&self) -> Result>; + fn read_text(&self) -> Result>; - fn write(&mut self, contents: String) -> Result<(), Box>; + fn write_text(&mut self, contents: String) -> Result<(), Box>; - fn read_primary(&self) -> Option>> { + fn read_primary_text(&self) -> Option>> { None } - fn write_primary(&mut self, _contents: String) -> Option>> { + fn write_primary_text( + &mut self, + _contents: String, + ) -> Option>> { None } } diff --git a/src/platform/android.rs b/src/platform/android.rs index b0ac8b6..c2d460a 100644 --- a/src/platform/android.rs +++ b/src/platform/android.rs @@ -5,8 +5,8 @@ use std::error::Error; pub fn connect( _window: &W, -) -> Result, Box> { - Ok(Box::new(Clipboard::new()?)) +) -> Result> { + Ok(Clipboard::new()) } pub struct Clipboard; diff --git a/src/platform/dummy.rs b/src/platform/dummy.rs index 8cf7418..096f68c 100644 --- a/src/platform/dummy.rs +++ b/src/platform/dummy.rs @@ -2,15 +2,15 @@ use crate::ClipboardProvider; use raw_window_handle::HasDisplayHandle; -struct Dummy; +struct Clipboard; pub fn connect( _window: &W, -) -> Result, Box> { - Ok(Box::new(Dummy)) +) -> Result> { + Ok(Clipboard) } -impl ClipboardProvider for Dummy { +impl ClipboardProvider for Clipboard { fn read(&self) -> Result> { Err(Box::new(Error::Unimplemented)) } diff --git a/src/platform/ios.rs b/src/platform/ios.rs index 84ae06a..1ab6e84 100644 --- a/src/platform/ios.rs +++ b/src/platform/ios.rs @@ -5,8 +5,8 @@ use std::error::Error; pub fn connect( _window: &W, -) -> Result, Box> { - Ok(Box::new(Clipboard::new()?)) +) -> Result> { + Clipboard::new() } pub struct Clipboard; diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 12d8c67..8fef7e9 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -6,51 +6,55 @@ use std::error::Error; pub use clipboard_wayland as wayland; pub use clipboard_x11 as x11; +pub enum Clipboard { + Wayland(wayland::Clipboard), + X11(x11::Clipboard), +} + +impl ClipboardProvider for Clipboard { + fn read_text(&self) -> Result> { + match self { + Clipboard::Wayland(c) => c.read_text(), + Clipboard::X11(c) => c.read().map_err(Box::from), + } + } + + fn write_text(&mut self, contents: String) -> Result<(), Box> { + match self { + Clipboard::Wayland(c) => c.write_text(contents), + Clipboard::X11(c) => c.write(contents).map_err(Box::from), + } + } + + fn read_primary_text(&self) -> Option>> { + match self { + Clipboard::Wayland(c) => Some(c.read_primary_text()), + Clipboard::X11(c) => Some(c.read_primary().map_err(Box::from)), + } + } + + fn write_primary_text( + &mut self, + contents: String, + ) -> Option>> { + match self { + Clipboard::Wayland(c) => Some(c.write_primary_text(contents)), + Clipboard::X11(c) => { + Some(c.write_primary(contents).map_err(Box::from)) + } + } + } +} + pub unsafe fn connect( window: &W, -) -> Result, Box> { +) -> Result> { let clipboard = match window.display_handle()?.as_raw() { - RawDisplayHandle::Wayland(handle) => { - Box::new(wayland::Clipboard::connect(handle.display.as_ptr())) as _ - } - _ => Box::new(x11::Clipboard::connect()?) as _, + RawDisplayHandle::Wayland(handle) => Clipboard::Wayland( + wayland::Clipboard::connect(handle.display.as_ptr()), + ) as _, + _ => Clipboard::X11(x11::Clipboard::connect()?) as _, }; Ok(clipboard) } - -impl ClipboardProvider for wayland::Clipboard { - fn read(&self) -> Result> { - self.read() - } - - fn read_primary(&self) -> Option>> { - Some(self.read_primary()) - } - - fn write(&mut self, contents: String) -> Result<(), Box> { - self.write(contents) - } - - fn write_primary(&mut self, contents: String) -> Option>> { - Some(self.write_primary(contents)) - } -} - -impl ClipboardProvider for x11::Clipboard { - fn read(&self) -> Result> { - self.read().map_err(Box::from) - } - - fn read_primary(&self) -> Option>> { - Some(self.read_primary().map_err(Box::from)) - } - - fn write(&mut self, contents: String) -> Result<(), Box> { - self.write(contents).map_err(Box::from) - } - - fn write_primary(&mut self, contents: String) -> Option>> { - Some(self.write_primary(contents).map_err(Box::from)) - } -} diff --git a/src/platform/macos.rs b/src/platform/macos.rs index 5b399e0..084ace8 100644 --- a/src/platform/macos.rs +++ b/src/platform/macos.rs @@ -1,15 +1,16 @@ use crate::ClipboardProvider; +pub(crate) use clipboard_macos::Clipboard; use raw_window_handle::HasDisplayHandle; use std::error::Error; pub fn connect( _window: &W, -) -> Result, Box> { - Ok(Box::new(clipboard_macos::Clipboard::new()?)) +) -> Result> { + Clipboard::new() } -impl ClipboardProvider for clipboard_macos::Clipboard { +impl ClipboardProvider for Clipboard { fn read(&self) -> Result> { self.read() } diff --git a/src/platform/windows.rs b/src/platform/windows.rs index a0c774b..f317b10 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -7,8 +7,8 @@ use std::error::Error; pub fn connect( _window: &W, -) -> Result, Box> { - Ok(Box::new(Clipboard)) +) -> Result> { + Ok(Clipboard) } pub struct Clipboard; diff --git a/wayland/Cargo.toml b/wayland/Cargo.toml index 6853c9d..d5919d9 100644 --- a/wayland/Cargo.toml +++ b/wayland/Cargo.toml @@ -10,4 +10,4 @@ documentation = "https://docs.rs/clipboard_wayland" keywords = ["clipboard", "wayland"] [dependencies] -smithay-clipboard = "0.7" +smithay-clipboard = { git = "https://github.com/wash2/smithay-clipboard", branch = "mime-types" } diff --git a/wayland/src/lib.rs b/wayland/src/lib.rs index fdf62c0..2558177 100644 --- a/wayland/src/lib.rs +++ b/wayland/src/lib.rs @@ -12,9 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::error::Error; -use std::ffi::c_void; -use std::sync::{Arc, Mutex}; +use std::{ + error::Error, + ffi::c_void, + sync::{Arc, Mutex}, +}; pub struct Clipboard { context: Arc>, @@ -29,22 +31,25 @@ impl Clipboard { Clipboard { context } } - pub fn read(&self) -> Result> { - Ok(self.context.lock().unwrap().load()?) + pub fn read_text(&self) -> Result> { + Ok(self.context.lock().unwrap().load_text()?) } - pub fn read_primary(&self) -> Result> { - Ok(self.context.lock().unwrap().load_primary()?) + pub fn read_primary_text(&self) -> Result> { + Ok(self.context.lock().unwrap().load_primary_text()?) } - pub fn write(&mut self, data: String) -> Result<(), Box> { - self.context.lock().unwrap().store(data); + pub fn write_text(&mut self, data: String) -> Result<(), Box> { + self.context.lock().unwrap().store_text(data); Ok(()) } - pub fn write_primary(&mut self, data: String) -> Result<(), Box> { - self.context.lock().unwrap().store_primary(data); + pub fn write_primary_text( + &mut self, + data: String, + ) -> Result<(), Box> { + self.context.lock().unwrap().store_primary_text(data); Ok(()) } From 6c41143f5c55bf34e0da2b7f1cb3e5bf76e3875f Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Thu, 29 Feb 2024 17:15:30 -0500 Subject: [PATCH 02/27] feat: add wayland impl for custom mime types --- src/lib.rs | 35 +++++++++++++ src/mime.rs | 45 +++++++++++++++++ src/platform/linux.rs | 113 +++++++++++++++++++++++++++++++++++++++++- wayland/Cargo.toml | 1 + wayland/src/lib.rs | 32 ++++++++++++ 5 files changed, 225 insertions(+), 1 deletion(-) create mode 100644 src/mime.rs diff --git a/src/lib.rs b/src/lib.rs index d3f0c61..1385a8d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,6 +48,7 @@ mod platform; #[path = "platform/dummy.rs"] mod platform; +use mime::{ClipboardLoadData, ClipboardStoreData}; use raw_window_handle::HasDisplayHandle; use std::error::Error; @@ -102,4 +103,38 @@ pub trait ClipboardProvider { ) -> Option>> { None } + + fn read(&self) -> Option>> + where + ClipboardLoadData: platform::InnerAllowedMimeTypes, + { + None + } + + fn write( + &mut self, + _contents: ClipboardStoreData, + ) -> Option>> + where + ClipboardStoreData: platform::InnerAsMimeTypes, + { + None + } + + fn read_primary(&self) -> Option>> + where + ClipboardLoadData: platform::InnerAllowedMimeTypes, + { + None + } + + fn write_primary( + &mut self, + _contents: ClipboardStoreData, + ) -> Option>> + where + ClipboardStoreData: platform::InnerAsMimeTypes, + { + None + } } diff --git a/src/mime.rs b/src/mime.rs new file mode 100644 index 0000000..97c55aa --- /dev/null +++ b/src/mime.rs @@ -0,0 +1,45 @@ +// need a type that can implement traits for storing custom data + +use std::{borrow::Cow, error, fmt}; + +/// Data that can be loaded from the clipboard. +pub struct ClipboardLoadData(pub T); + +/// Describes the mime types which are accepted. +pub trait AllowedMimeTypes: + TryFrom<(Vec, String)> + Send + Sync + 'static +{ + /// List allowed mime types for the type to convert from a byte slice. + /// + /// Allowed mime types should be listed in order of decreasing preference, + /// most preferred first. + fn allowed() -> Cow<'static, [String]>; +} + +/// Can be converted to data with the available mime types. +pub trait AsMimeTypes { + /// List available mime types for this data to convert to a byte slice. + fn available(&self) -> Cow<'static, [String]>; + + /// Converts a type to a byte slice for the given mime type if possible. + fn as_bytes(&self, mime_type: &str) -> Option>; +} + +/// Data that can be stored to the clipboard. +pub struct ClipboardStoreData { + /// Clipboard data. + pub data: T, + /// Available mime types for the clipboard data. + pub available_mime_types: Vec>, +} + +#[derive(Debug, Clone, Copy)] +pub struct Error; + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Unsupported mime type") + } +} + +impl error::Error for Error {} diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 8fef7e9..75d65d9 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -1,10 +1,17 @@ -use crate::ClipboardProvider; +use crate::{ + mime::{ClipboardLoadData, ClipboardStoreData}, + ClipboardProvider, +}; use raw_window_handle::{HasDisplayHandle, RawDisplayHandle}; use std::error::Error; +use wayland::MimeType; pub use clipboard_wayland as wayland; pub use clipboard_x11 as x11; +pub use wayland::{ + AllowedMimeTypes as InnerAllowedMimeTypes, AsMimeTypes as InnerAsMimeTypes, +}; pub enum Clipboard { Wayland(wayland::Clipboard), @@ -44,6 +51,62 @@ impl ClipboardProvider for Clipboard { } } } + + fn read(&self) -> Option>> + where + ClipboardLoadData: InnerAllowedMimeTypes, + { + match self { + Clipboard::Wayland(c) => { + let ret = c.read::>(); + Some(ret.map(|ret| ret.0)) + } + Clipboard::X11(_) => None, + } + } + + fn write( + &mut self, + contents: ClipboardStoreData, + ) -> Option>> + where + ClipboardStoreData: InnerAsMimeTypes, + { + match self { + Clipboard::Wayland(c) => { + Some(c.write::>(contents)) + } + Clipboard::X11(_) => None, + } + } + + fn read_primary(&self) -> Option>> + where + ClipboardLoadData: InnerAllowedMimeTypes, + { + match self { + Clipboard::Wayland(c) => { + let ret = c.read_primary::>(); + Some(ret.map(|ret| ret.0)) + } + Clipboard::X11(_) => None, + } + } + + fn write_primary( + &mut self, + contents: ClipboardStoreData, + ) -> Option>> + where + ClipboardStoreData: InnerAsMimeTypes, + { + match self { + Clipboard::Wayland(c) => { + Some(c.write_primary::>(contents)) + } + Clipboard::X11(_) => None, + } + } } pub unsafe fn connect( @@ -58,3 +121,51 @@ pub unsafe fn connect( Ok(clipboard) } + +impl InnerAsMimeTypes for ClipboardLoadData { + fn available(&self) -> std::borrow::Cow<'static, [MimeType]> { + self.0 + .available() + .into_iter() + .map(|m| MimeType::Other(m.clone().into())) + .collect() + } + + fn as_bytes( + &self, + mime_type: &MimeType, + ) -> Option> { + self.0.as_bytes(mime_type.as_ref()) + } +} + +impl InnerAllowedMimeTypes + for ClipboardLoadData +where + ClipboardLoadData: TryFrom<(Vec, MimeType)>, +{ + // TODO select text variants if string matches... + fn allowed() -> std::borrow::Cow<'static, [wayland::MimeType]> { + T::allowed() + .into_iter() + .map(|s| MimeType::Other(s.clone().into())) + .collect() + } +} + +impl TryFrom<(Vec, MimeType)> for ClipboardLoadData +where + T: for<'b> TryFrom<(Vec, String)>, + T: 'static, +{ + type Error = crate::mime::Error; + + fn try_from( + (value, mime): (Vec, MimeType), + ) -> Result { + let mime = mime.to_string(); + Ok(ClipboardLoadData( + T::try_from((value, mime)).map_err(|_| crate::mime::Error)?, + )) + } +} diff --git a/wayland/Cargo.toml b/wayland/Cargo.toml index d5919d9..9d3ada8 100644 --- a/wayland/Cargo.toml +++ b/wayland/Cargo.toml @@ -11,3 +11,4 @@ keywords = ["clipboard", "wayland"] [dependencies] smithay-clipboard = { git = "https://github.com/wash2/smithay-clipboard", branch = "mime-types" } +# smithay-clipboard = { path = "../../smithay-clipboard" } diff --git a/wayland/src/lib.rs b/wayland/src/lib.rs index 2558177..e4a8b22 100644 --- a/wayland/src/lib.rs +++ b/wayland/src/lib.rs @@ -18,6 +18,8 @@ use std::{ sync::{Arc, Mutex}, }; +pub use smithay_clipboard::mime::{AllowedMimeTypes, AsMimeTypes, MimeType}; + pub struct Clipboard { context: Arc>, } @@ -53,4 +55,34 @@ impl Clipboard { Ok(()) } + + pub fn write( + &mut self, + data: T, + ) -> Result<(), Box> { + self.context.lock().unwrap().store(data); + + Ok(()) + } + + pub fn write_primary( + &mut self, + data: T, + ) -> Result<(), Box> { + self.context.lock().unwrap().store_primary(data); + + Ok(()) + } + + pub fn read( + &self, + ) -> Result> { + Ok(self.context.lock().unwrap().load()?) + } + + pub fn read_primary( + &self, + ) -> Result> { + Ok(self.context.lock().unwrap().load_primary()?) + } } From 886f43041477e98035e065719c62bdfcae0a7d21 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Thu, 14 Mar 2024 13:16:13 -0400 Subject: [PATCH 03/27] refactor: avoid breaking changes and update smithay-clipboard --- src/lib.rs | 24 ++++++++++++------------ src/platform/linux.rs | 32 ++++++++++++++++---------------- wayland/Cargo.toml | 2 +- wayland/src/lib.rs | 16 ++++++++-------- 4 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1385a8d..af880a0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -67,51 +67,51 @@ impl Clipboard { } pub fn read(&self) -> Result> { - self.raw.read_text() + self.raw.read() } pub fn write(&mut self, contents: String) -> Result<(), Box> { - self.raw.write_text(contents) + self.raw.write(contents) } } impl Clipboard { pub fn read_primary(&self) -> Option>> { - self.raw.read_primary_text() + self.raw.read_primary() } pub fn write_primary( &mut self, contents: String, ) -> Option>> { - self.raw.write_primary_text(contents) + self.raw.write_primary(contents) } } pub trait ClipboardProvider { - fn read_text(&self) -> Result>; + fn read(&self) -> Result>; - fn write_text(&mut self, contents: String) -> Result<(), Box>; + fn write(&mut self, contents: String) -> Result<(), Box>; - fn read_primary_text(&self) -> Option>> { + fn read_primary(&self) -> Option>> { None } - fn write_primary_text( + fn write_primary( &mut self, _contents: String, ) -> Option>> { None } - fn read(&self) -> Option>> + fn read_data(&self) -> Option>> where ClipboardLoadData: platform::InnerAllowedMimeTypes, { None } - fn write( + fn write_data( &mut self, _contents: ClipboardStoreData, ) -> Option>> @@ -121,14 +121,14 @@ pub trait ClipboardProvider { None } - fn read_primary(&self) -> Option>> + fn read_primary_data(&self) -> Option>> where ClipboardLoadData: platform::InnerAllowedMimeTypes, { None } - fn write_primary( + fn write_primary_data( &mut self, _contents: ClipboardStoreData, ) -> Option>> diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 75d65d9..5001852 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -19,53 +19,53 @@ pub enum Clipboard { } impl ClipboardProvider for Clipboard { - fn read_text(&self) -> Result> { + fn read(&self) -> Result> { match self { - Clipboard::Wayland(c) => c.read_text(), + Clipboard::Wayland(c) => c.read(), Clipboard::X11(c) => c.read().map_err(Box::from), } } - fn write_text(&mut self, contents: String) -> Result<(), Box> { + fn write(&mut self, contents: String) -> Result<(), Box> { match self { - Clipboard::Wayland(c) => c.write_text(contents), + Clipboard::Wayland(c) => c.write(contents), Clipboard::X11(c) => c.write(contents).map_err(Box::from), } } - fn read_primary_text(&self) -> Option>> { + fn read_primary(&self) -> Option>> { match self { - Clipboard::Wayland(c) => Some(c.read_primary_text()), + Clipboard::Wayland(c) => Some(c.read_primary()), Clipboard::X11(c) => Some(c.read_primary().map_err(Box::from)), } } - fn write_primary_text( + fn write_primary( &mut self, contents: String, ) -> Option>> { match self { - Clipboard::Wayland(c) => Some(c.write_primary_text(contents)), + Clipboard::Wayland(c) => Some(c.write_primary(contents)), Clipboard::X11(c) => { Some(c.write_primary(contents).map_err(Box::from)) } } } - fn read(&self) -> Option>> + fn read_data(&self) -> Option>> where ClipboardLoadData: InnerAllowedMimeTypes, { match self { Clipboard::Wayland(c) => { - let ret = c.read::>(); + let ret = c.read_data::>(); Some(ret.map(|ret| ret.0)) } Clipboard::X11(_) => None, } } - fn write( + fn write_data( &mut self, contents: ClipboardStoreData, ) -> Option>> @@ -74,26 +74,26 @@ impl ClipboardProvider for Clipboard { { match self { Clipboard::Wayland(c) => { - Some(c.write::>(contents)) + Some(c.write_data::>(contents)) } Clipboard::X11(_) => None, } } - fn read_primary(&self) -> Option>> + fn read_primary_data(&self) -> Option>> where ClipboardLoadData: InnerAllowedMimeTypes, { match self { Clipboard::Wayland(c) => { - let ret = c.read_primary::>(); + let ret = c.read_primary_data::>(); Some(ret.map(|ret| ret.0)) } Clipboard::X11(_) => None, } } - fn write_primary( + fn write_primary_data( &mut self, contents: ClipboardStoreData, ) -> Option>> @@ -102,7 +102,7 @@ impl ClipboardProvider for Clipboard { { match self { Clipboard::Wayland(c) => { - Some(c.write_primary::>(contents)) + Some(c.write_primary_data::>(contents)) } Clipboard::X11(_) => None, } diff --git a/wayland/Cargo.toml b/wayland/Cargo.toml index 9d3ada8..2751f2e 100644 --- a/wayland/Cargo.toml +++ b/wayland/Cargo.toml @@ -10,5 +10,5 @@ documentation = "https://docs.rs/clipboard_wayland" keywords = ["clipboard", "wayland"] [dependencies] -smithay-clipboard = { git = "https://github.com/wash2/smithay-clipboard", branch = "mime-types" } +smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "pop-mime-types" } # smithay-clipboard = { path = "../../smithay-clipboard" } diff --git a/wayland/src/lib.rs b/wayland/src/lib.rs index e4a8b22..fb5bb2e 100644 --- a/wayland/src/lib.rs +++ b/wayland/src/lib.rs @@ -33,21 +33,21 @@ impl Clipboard { Clipboard { context } } - pub fn read_text(&self) -> Result> { + pub fn read(&self) -> Result> { Ok(self.context.lock().unwrap().load_text()?) } - pub fn read_primary_text(&self) -> Result> { + pub fn read_primary(&self) -> Result> { Ok(self.context.lock().unwrap().load_primary_text()?) } - pub fn write_text(&mut self, data: String) -> Result<(), Box> { + pub fn write(&mut self, data: String) -> Result<(), Box> { self.context.lock().unwrap().store_text(data); Ok(()) } - pub fn write_primary_text( + pub fn write_primary( &mut self, data: String, ) -> Result<(), Box> { @@ -56,7 +56,7 @@ impl Clipboard { Ok(()) } - pub fn write( + pub fn write_data( &mut self, data: T, ) -> Result<(), Box> { @@ -65,7 +65,7 @@ impl Clipboard { Ok(()) } - pub fn write_primary( + pub fn write_primary_data( &mut self, data: T, ) -> Result<(), Box> { @@ -74,13 +74,13 @@ impl Clipboard { Ok(()) } - pub fn read( + pub fn read_data( &self, ) -> Result> { Ok(self.context.lock().unwrap().load()?) } - pub fn read_primary( + pub fn read_primary_data( &self, ) -> Result> { Ok(self.context.lock().unwrap().load_primary()?) From 5dd795d463c0ca6c02f9cc7d60cc9d3adb0b1324 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Thu, 14 Mar 2024 13:30:50 -0400 Subject: [PATCH 04/27] refactor: add type that hides the platform clipboard --- examples/big_file.rs | 6 +++--- examples/read.rs | 6 +++--- examples/write.rs | 6 +++--- src/lib.rs | 10 ++++++---- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/examples/big_file.rs b/examples/big_file.rs index f666812..8be0fa2 100644 --- a/examples/big_file.rs +++ b/examples/big_file.rs @@ -1,5 +1,5 @@ use rand::distributions::{Alphanumeric, Distribution}; -use window_clipboard::Clipboard; +use window_clipboard::PlatformClipboard; use winit::{ error::EventLoopError, event::{ElementState, Event, KeyEvent, WindowEvent}, @@ -24,8 +24,8 @@ fn main() -> Result<(), EventLoopError> { .build(&event_loop) .unwrap(); - let mut clipboard = - unsafe { Clipboard::connect(&window) }.expect("Connect to clipboard"); + let mut clipboard = unsafe { PlatformClipboard::connect(&window) } + .expect("Connect to clipboard"); clipboard.write(data.clone()).unwrap(); diff --git a/examples/read.rs b/examples/read.rs index ddbaf8b..8407133 100644 --- a/examples/read.rs +++ b/examples/read.rs @@ -1,4 +1,4 @@ -use window_clipboard::Clipboard; +use window_clipboard::PlatformClipboard; use winit::{ error::EventLoopError, event::{Event, WindowEvent}, @@ -14,8 +14,8 @@ fn main() -> Result<(), EventLoopError> { .build(&event_loop) .unwrap(); - let clipboard = - unsafe { Clipboard::connect(&window) }.expect("Connect to clipboard"); + let clipboard = unsafe { PlatformClipboard::connect(&window) } + .expect("Connect to clipboard"); event_loop.run(move |event, elwt| match event { Event::AboutToWait => { diff --git a/examples/write.rs b/examples/write.rs index 40e6263..49dc505 100644 --- a/examples/write.rs +++ b/examples/write.rs @@ -1,4 +1,4 @@ -use window_clipboard::Clipboard; +use window_clipboard::PlatformClipboard; use winit::{ error::EventLoopError, event::{Event, WindowEvent}, @@ -14,8 +14,8 @@ fn main() -> Result<(), EventLoopError> { .build(&event_loop) .unwrap(); - let mut clipboard = - unsafe { Clipboard::connect(&window) }.expect("Connect to clipboard"); + let mut clipboard = unsafe { PlatformClipboard::connect(&window) } + .expect("Connect to clipboard"); clipboard .write(String::from("Hello, world!")) diff --git a/src/lib.rs b/src/lib.rs index af880a0..f574198 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,16 +52,18 @@ use mime::{ClipboardLoadData, ClipboardStoreData}; use raw_window_handle::HasDisplayHandle; use std::error::Error; -pub struct Clipboard { +pub type Clipboard = PlatformClipboard; + +pub struct PlatformClipboard { raw: C, } -impl Clipboard { +impl PlatformClipboard { /// Safety: the display handle must be valid for the lifetime of `Clipboard` pub unsafe fn connect( window: &W, ) -> Result> { - Ok(Clipboard { + Ok(PlatformClipboard { raw: platform::connect(window)?, }) } @@ -75,7 +77,7 @@ impl Clipboard { } } -impl Clipboard { +impl PlatformClipboard { pub fn read_primary(&self) -> Option>> { self.raw.read_primary() } From 71df657777da460b6e25677c82514558222a92b2 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Thu, 14 Mar 2024 16:42:24 -0400 Subject: [PATCH 05/27] feat: add methods for loading raw data --- Cargo.toml | 2 + mime/Cargo.toml | 11 +++++ src/mime.rs => mime/src/lib.rs | 2 + mime/src/platform/linux.rs | 47 ++++++++++++++++++++ mime/src/platform/mod.rs | 11 +++++ src/lib.rs | 26 ++++++++--- src/platform/linux.rs | 80 ++++++++++------------------------ wayland/Cargo.toml | 2 +- wayland/src/lib.rs | 35 +++++++++++++++ 9 files changed, 153 insertions(+), 63 deletions(-) create mode 100644 mime/Cargo.toml rename src/mime.rs => mime/src/lib.rs (98%) create mode 100644 mime/src/platform/linux.rs create mode 100644 mime/src/platform/mod.rs diff --git a/Cargo.toml b/Cargo.toml index d0fb561..fcd381b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ categories = ["gui"] [dependencies] raw-window-handle = { version = "0.6", features = ["std"] } thiserror = "1.0" +mime = { path = "./mime" } [target.'cfg(windows)'.dependencies] clipboard-win = { version = "5.0", features = ["std"] } @@ -32,6 +33,7 @@ winit = "0.29" [workspace] members = [ "macos", + "mime", "wayland", "x11", ] diff --git a/mime/Cargo.toml b/mime/Cargo.toml new file mode 100644 index 0000000..74bb6ad --- /dev/null +++ b/mime/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "mime" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[target.'cfg(all(unix, not(any(target_os="macos", target_os="android", target_os="emscripten", target_os="ios", target_os="redox"))))'.dependencies] +smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "pop-mime-types" } + + diff --git a/src/mime.rs b/mime/src/lib.rs similarity index 98% rename from src/mime.rs rename to mime/src/lib.rs index 97c55aa..78a2303 100644 --- a/src/mime.rs +++ b/mime/src/lib.rs @@ -1,3 +1,5 @@ +pub mod platform; + // need a type that can implement traits for storing custom data use std::{borrow::Cow, error, fmt}; diff --git a/mime/src/platform/linux.rs b/mime/src/platform/linux.rs new file mode 100644 index 0000000..d24dc28 --- /dev/null +++ b/mime/src/platform/linux.rs @@ -0,0 +1,47 @@ +use smithay_clipboard::mime::{AllowedMimeTypes, AsMimeTypes, MimeType}; + +use crate::{ClipboardLoadData, ClipboardStoreData}; + +impl AsMimeTypes for ClipboardStoreData { + fn available(&self) -> std::borrow::Cow<'static, [MimeType]> { + self.data + .available() + .into_iter() + .map(|m| MimeType::Other(m.clone().into())) + .collect() + } + + fn as_bytes( + &self, + mime_type: &MimeType, + ) -> Option> { + self.data.as_bytes(mime_type.as_ref()) + } +} + +impl AllowedMimeTypes for ClipboardLoadData { + // TODO select text variants if string matches... + fn allowed() -> std::borrow::Cow<'static, [MimeType]> { + T::allowed() + .into_iter() + .map(|s| MimeType::Other(s.clone().into())) + .collect() + } +} + +impl TryFrom<(Vec, MimeType)> for ClipboardLoadData +where + T: for<'b> TryFrom<(Vec, String)>, + T: 'static, +{ + type Error = crate::Error; + + fn try_from( + (value, mime): (Vec, MimeType), + ) -> Result { + let mime = mime.to_string(); + Ok(ClipboardLoadData( + T::try_from((value, mime)).map_err(|_| crate::Error)?, + )) + } +} diff --git a/mime/src/platform/mod.rs b/mime/src/platform/mod.rs new file mode 100644 index 0000000..10c3ec9 --- /dev/null +++ b/mime/src/platform/mod.rs @@ -0,0 +1,11 @@ +#[cfg(all( + unix, + not(any( + target_os = "macos", + target_os = "ios", + target_os = "android", + target_os = "emscripten", + target_os = "redox" + )) +))] +pub mod linux; diff --git a/src/lib.rs b/src/lib.rs index f574198..3da6d3e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -pub mod mime; +pub use mime; #[cfg(all( unix, @@ -48,7 +48,7 @@ mod platform; #[path = "platform/dummy.rs"] mod platform; -use mime::{ClipboardLoadData, ClipboardStoreData}; +use mime::ClipboardStoreData; use raw_window_handle::HasDisplayHandle; use std::error::Error; @@ -108,7 +108,7 @@ pub trait ClipboardProvider { fn read_data(&self) -> Option>> where - ClipboardLoadData: platform::InnerAllowedMimeTypes, + T: mime::AllowedMimeTypes, { None } @@ -118,24 +118,38 @@ pub trait ClipboardProvider { _contents: ClipboardStoreData, ) -> Option>> where - ClipboardStoreData: platform::InnerAsMimeTypes, + T: mime::AsMimeTypes, { None } fn read_primary_data(&self) -> Option>> where - ClipboardLoadData: platform::InnerAllowedMimeTypes, + T: mime::AllowedMimeTypes, { None } + fn read_primary_raw( + &self, + _allowed: Vec, + ) -> Option, String), Box>> { + None + } + + fn read_raw( + &self, + _allowed: Vec, + ) -> Option, String), Box>> { + None + } + fn write_primary_data( &mut self, _contents: ClipboardStoreData, ) -> Option>> where - ClipboardStoreData: platform::InnerAsMimeTypes, + T: mime::AsMimeTypes, { None } diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 5001852..bebf78e 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -5,13 +5,9 @@ use crate::{ use raw_window_handle::{HasDisplayHandle, RawDisplayHandle}; use std::error::Error; -use wayland::MimeType; pub use clipboard_wayland as wayland; pub use clipboard_x11 as x11; -pub use wayland::{ - AllowedMimeTypes as InnerAllowedMimeTypes, AsMimeTypes as InnerAsMimeTypes, -}; pub enum Clipboard { Wayland(wayland::Clipboard), @@ -54,7 +50,7 @@ impl ClipboardProvider for Clipboard { fn read_data(&self) -> Option>> where - ClipboardLoadData: InnerAllowedMimeTypes, + T: mime::AllowedMimeTypes, { match self { Clipboard::Wayland(c) => { @@ -70,7 +66,7 @@ impl ClipboardProvider for Clipboard { contents: ClipboardStoreData, ) -> Option>> where - ClipboardStoreData: InnerAsMimeTypes, + T: mime::AsMimeTypes, { match self { Clipboard::Wayland(c) => { @@ -82,7 +78,7 @@ impl ClipboardProvider for Clipboard { fn read_primary_data(&self) -> Option>> where - ClipboardLoadData: InnerAllowedMimeTypes, + T: mime::AllowedMimeTypes, { match self { Clipboard::Wayland(c) => { @@ -93,12 +89,32 @@ impl ClipboardProvider for Clipboard { } } + fn read_primary_raw( + &self, + allowed: Vec, + ) -> Option, String), Box>> { + match self { + Clipboard::Wayland(c) => Some(c.read_primary_raw(allowed)), + Clipboard::X11(_) => None, + } + } + + fn read_raw( + &self, + allowed: Vec, + ) -> Option, String), Box>> { + match self { + Clipboard::Wayland(c) => Some(c.read_raw(allowed)), + Clipboard::X11(_) => None, + } + } + fn write_primary_data( &mut self, contents: ClipboardStoreData, ) -> Option>> where - ClipboardStoreData: InnerAsMimeTypes, + T: mime::AsMimeTypes, { match self { Clipboard::Wayland(c) => { @@ -121,51 +137,3 @@ pub unsafe fn connect( Ok(clipboard) } - -impl InnerAsMimeTypes for ClipboardLoadData { - fn available(&self) -> std::borrow::Cow<'static, [MimeType]> { - self.0 - .available() - .into_iter() - .map(|m| MimeType::Other(m.clone().into())) - .collect() - } - - fn as_bytes( - &self, - mime_type: &MimeType, - ) -> Option> { - self.0.as_bytes(mime_type.as_ref()) - } -} - -impl InnerAllowedMimeTypes - for ClipboardLoadData -where - ClipboardLoadData: TryFrom<(Vec, MimeType)>, -{ - // TODO select text variants if string matches... - fn allowed() -> std::borrow::Cow<'static, [wayland::MimeType]> { - T::allowed() - .into_iter() - .map(|s| MimeType::Other(s.clone().into())) - .collect() - } -} - -impl TryFrom<(Vec, MimeType)> for ClipboardLoadData -where - T: for<'b> TryFrom<(Vec, String)>, - T: 'static, -{ - type Error = crate::mime::Error; - - fn try_from( - (value, mime): (Vec, MimeType), - ) -> Result { - let mime = mime.to_string(); - Ok(ClipboardLoadData( - T::try_from((value, mime)).map_err(|_| crate::mime::Error)?, - )) - } -} diff --git a/wayland/Cargo.toml b/wayland/Cargo.toml index 2751f2e..3e28518 100644 --- a/wayland/Cargo.toml +++ b/wayland/Cargo.toml @@ -11,4 +11,4 @@ keywords = ["clipboard", "wayland"] [dependencies] smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "pop-mime-types" } -# smithay-clipboard = { path = "../../smithay-clipboard" } +mime = { path = "../mime" } \ No newline at end of file diff --git a/wayland/src/lib.rs b/wayland/src/lib.rs index fb5bb2e..b14586e 100644 --- a/wayland/src/lib.rs +++ b/wayland/src/lib.rs @@ -13,6 +13,7 @@ // limitations under the License. use std::{ + borrow::Cow, error::Error, ffi::c_void, sync::{Arc, Mutex}, @@ -85,4 +86,38 @@ impl Clipboard { ) -> Result> { Ok(self.context.lock().unwrap().load_primary()?) } + + pub fn read_primary_raw( + &self, + allowed: Vec, + ) -> Result<(Vec, String), Box> { + Ok(self + .context + .lock() + .unwrap() + .load_primary_raw( + allowed + .into_iter() + .map(|s| MimeType::from(Cow::Owned(s))) + .collect::>(), + ) + .map(|(d, m)| (d, m.to_string()))?) + } + + pub fn read_raw( + &self, + allowed: Vec, + ) -> Result<(Vec, String), Box> { + Ok(self + .context + .lock() + .unwrap() + .load_raw( + allowed + .into_iter() + .map(|s| MimeType::from(Cow::Owned(s))) + .collect::>(), + ) + .map(|(d, m)| (d, m.to_string()))?) + } } From 20e7cbedf547490ea005e311e3dcad48fe9a59c0 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Thu, 14 Mar 2024 17:19:32 -0400 Subject: [PATCH 06/27] chore: add more impls of clipboard methods --- mime/src/lib.rs | 10 ++++++++++ src/lib.rs | 50 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/mime/src/lib.rs b/mime/src/lib.rs index 78a2303..4517102 100644 --- a/mime/src/lib.rs +++ b/mime/src/lib.rs @@ -27,6 +27,16 @@ pub trait AsMimeTypes { fn as_bytes(&self, mime_type: &str) -> Option>; } +impl AsMimeTypes for Box { + fn available(&self) -> Cow<'static, [String]> { + self.as_ref().available() + } + + fn as_bytes(&self, mime_type: &str) -> Option> { + self.as_ref().as_bytes(mime_type) + } +} + /// Data that can be stored to the clipboard. pub struct ClipboardStoreData { /// Clipboard data. diff --git a/src/lib.rs b/src/lib.rs index 3da6d3e..421b3a1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -88,6 +88,56 @@ impl PlatformClipboard { ) -> Option>> { self.raw.write_primary(contents) } + + pub fn read_data(&self) -> Option>> + where + T: mime::AllowedMimeTypes, + { + self.raw.read_data() + } + + pub fn write_data( + &mut self, + contents: ClipboardStoreData, + ) -> Option>> + where + T: mime::AsMimeTypes, + { + self.raw.write_data(contents) + } + + pub fn read_primary_data( + &self, + ) -> Option>> + where + T: mime::AllowedMimeTypes, + { + self.raw.read_primary_data() + } + + pub fn read_primary_raw( + &self, + allowed: Vec, + ) -> Option, String), Box>> { + self.raw.read_primary_raw(allowed) + } + + pub fn read_raw( + &self, + allowed: Vec, + ) -> Option, String), Box>> { + self.raw.read_raw(allowed) + } + + pub fn write_primary_data( + &mut self, + contents: ClipboardStoreData, + ) -> Option>> + where + T: mime::AsMimeTypes, + { + self.raw.write_primary_data(contents) + } } pub trait ClipboardProvider { From c3e9e794b94a2e79419517145424dc2035a572da Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Thu, 14 Mar 2024 18:52:26 -0400 Subject: [PATCH 07/27] refactor: remove unneeded mime types from ClipboardStoredData --- mime/src/lib.rs | 7 +------ mime/src/platform/linux.rs | 4 ++-- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/mime/src/lib.rs b/mime/src/lib.rs index 4517102..98f8d29 100644 --- a/mime/src/lib.rs +++ b/mime/src/lib.rs @@ -38,12 +38,7 @@ impl AsMimeTypes for Box { } /// Data that can be stored to the clipboard. -pub struct ClipboardStoreData { - /// Clipboard data. - pub data: T, - /// Available mime types for the clipboard data. - pub available_mime_types: Vec>, -} +pub struct ClipboardStoreData(pub T); #[derive(Debug, Clone, Copy)] pub struct Error; diff --git a/mime/src/platform/linux.rs b/mime/src/platform/linux.rs index d24dc28..1da9698 100644 --- a/mime/src/platform/linux.rs +++ b/mime/src/platform/linux.rs @@ -4,7 +4,7 @@ use crate::{ClipboardLoadData, ClipboardStoreData}; impl AsMimeTypes for ClipboardStoreData { fn available(&self) -> std::borrow::Cow<'static, [MimeType]> { - self.data + self.0 .available() .into_iter() .map(|m| MimeType::Other(m.clone().into())) @@ -15,7 +15,7 @@ impl AsMimeTypes for ClipboardStoreData { &self, mime_type: &MimeType, ) -> Option> { - self.data.as_bytes(mime_type.as_ref()) + self.0.as_bytes(mime_type.as_ref()) } } From 228288dfdfc5a6e135050b36d587415b1b089165 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Tue, 26 Mar 2024 16:03:05 -0400 Subject: [PATCH 08/27] feat: dnd integration --- Cargo.toml | 8 +- dnd/Cargo.toml | 16 ++++ dnd/src/lib.rs | 126 ++++++++++++++++++++++++++++ dnd/src/platform/linux.rs | 88 ++++++++++++++++++++ dnd/src/platform/mod.rs | 0 mime/Cargo.toml | 4 +- src/dnd/mod.rs | 102 +++++++++++++++++++++++ src/lib.rs | 2 + src/platform/linux.rs | 78 ++++++++++++++++- wayland/Cargo.toml | 8 +- wayland/src/lib.rs | 171 +++++++++++++++++++++++++++++++++++++- 11 files changed, 589 insertions(+), 14 deletions(-) create mode 100644 dnd/Cargo.toml create mode 100644 dnd/src/lib.rs create mode 100644 dnd/src/platform/linux.rs create mode 100644 dnd/src/platform/mod.rs create mode 100644 src/dnd/mod.rs diff --git a/Cargo.toml b/Cargo.toml index fcd381b..2aacaa0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ categories = ["gui"] raw-window-handle = { version = "0.6", features = ["std"] } thiserror = "1.0" mime = { path = "./mime" } +dnd = { path = "./dnd" } [target.'cfg(windows)'.dependencies] clipboard-win = { version = "5.0", features = ["std"] } @@ -31,9 +32,4 @@ rand = "0.8" winit = "0.29" [workspace] -members = [ - "macos", - "mime", - "wayland", - "x11", -] +members = ["dnd", "macos", "mime", "dnd", "wayland", "x11"] diff --git a/dnd/Cargo.toml b/dnd/Cargo.toml new file mode 100644 index 0000000..202a186 --- /dev/null +++ b/dnd/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "dnd" +version = "0.1.0" +edition = "2021" + +[dependencies] +mime = { path = "../mime" } +bitflags = "2.5.0" + +[target.'cfg(all(unix, not(any(target_os="macos", target_os="android", target_os="emscripten", target_os="ios", target_os="redox"))))'.dependencies] +smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", branch = "dnd", features = [ + "dnd", +] } +sctk = { package = "smithay-client-toolkit", git = "https://github.com/Smithay/client-toolkit", default-features = false, features = [ + "calloop", +] } diff --git a/dnd/src/lib.rs b/dnd/src/lib.rs new file mode 100644 index 0000000..192acae --- /dev/null +++ b/dnd/src/lib.rs @@ -0,0 +1,126 @@ +use std::{ + borrow::Cow, + ffi::c_void, + sync::{mpsc::SendError, Arc}, +}; + +use bitflags::bitflags; + +#[cfg(all( + unix, + not(any( + target_os = "macos", + target_os = "ios", + target_os = "android", + target_os = "emscripten", + target_os = "redox" + )) +))] +#[path = "platform/linux.rs"] +pub mod platform; + +bitflags! { + // Attributes can be applied to flags types + #[repr(transparent)] + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + pub struct DndAction: u32 { + const Copy = 0b00000001; + const Move = 0b00000010; + const Ask = 0b00000100; + } +} + +#[derive(Debug)] +pub enum DndEvent { + /// Dnd Offer event with the corresponding destination rectangle ID. + Offer(Option, OfferEvent), + /// Dnd Source event. + Source(SourceEvent), +} + +#[derive(Debug)] +pub enum SourceEvent { + /// DnD operation ended. + Finished, + /// DnD Cancelled. + Cancelled, + /// DnD action chosen by the compositor. + Action(DndAction), + /// Mime accepted by destination. + /// If [`None`], no mime types are accepted. + Mime(Option), + /// DnD Dropped. The operation is still ongoing until receiving a + /// [`SourceEvent::Finished`] event. + Dropped, +} + +#[derive(Debug)] +pub enum OfferEvent { + Enter { + x: f64, + y: f64, + mime_types: Vec, + surface: T, + }, + Motion { + x: f64, + y: f64, + }, + /// The offer is no longer on a DnD destination. + LeaveDestination, + /// The offer has left the surface. + Leave, + /// An offer was dropped + Drop, + /// If the selected action is ASK, the user must be presented with a + /// choice. [`Clipboard::set_action`] should then be called before data + /// can be requested and th DnD operation can be finished. + SelectedAction(DndAction), + Data { + data: Vec, + mime_type: String, + }, +} + +/// A rectangle with a logical location and size relative to a [`DndSurface`] +#[derive(Debug, Default, Clone)] +pub struct Rectangle { + pub x: f64, + pub y: f64, + pub width: f64, + pub height: f64, +} + +pub trait Sender { + /// Send an event in the channel + fn send(&self, t: DndEvent) -> Result<(), SendError>>; +} + +pub trait RawSurface { + /// # Safety + /// + /// returned pointer must be a valid pointer to the underlying surface, and it must + /// remain valid for as long as `RawSurface` object is alive. + unsafe fn get_ptr(&mut self) -> *mut c_void; +} + +/// A rectangle with a logical location and size relative to a [`DndSurface`] +#[derive(Debug, Clone)] +pub struct DndDestinationRectangle { + /// A unique ID + pub id: u128, + /// The rectangle representing this destination. + pub rectangle: Rectangle, + /// Accepted mime types in this rectangle + pub mime_types: Vec>, + /// Accepted actions in this rectangle + pub actions: DndAction, + /// Prefered action in this rectangle + pub preferred: DndAction, +} + +#[derive(Clone)] +pub struct DndSurface(pub Arc>); + +#[derive(Clone)] +pub struct DataWrapper(pub T); diff --git a/dnd/src/platform/linux.rs b/dnd/src/platform/linux.rs new file mode 100644 index 0000000..bcb7db0 --- /dev/null +++ b/dnd/src/platform/linux.rs @@ -0,0 +1,88 @@ +use std::{borrow::Cow, ffi::c_void, sync::Arc}; + +use crate::{DataWrapper, DndAction, DndSurface}; +use smithay_clipboard::mime::{AllowedMimeTypes, AsMimeTypes, MimeType}; + +impl< + T: mime::AllowedMimeTypes + + std::convert::TryFrom<(std::vec::Vec, String)>, + > AllowedMimeTypes for DataWrapper +{ + fn allowed() -> Cow<'static, [MimeType]> { + T::allowed() + .into_iter() + .map(|s| MimeType::from(Cow::Owned(s.to_string()))) + .collect() + } +} + +impl, String)>> TryFrom<(Vec, MimeType)> + for DataWrapper +{ + type Error = T::Error; + + fn try_from( + (data, mime): (Vec, MimeType), + ) -> Result { + T::try_from((data, mime.to_string())).map(|d| DataWrapper(d)) + } +} + +impl AsMimeTypes for DataWrapper { + fn available(&self) -> Cow<'static, [MimeType]> { + self.0 + .available() + .into_iter() + .map(|m| MimeType::from(Cow::Owned(m.to_string()))) + .collect() + } + + fn as_bytes(&self, mime_type: &MimeType) -> Option> { + self.0.as_bytes(mime_type.as_ref()) + } +} + +impl smithay_clipboard::dnd::RawSurface for DndSurface { + unsafe fn get_ptr(&mut self) -> *mut c_void { + // XXX won't panic because this is only called once before it could be cloned + Arc::get_mut(&mut self.0).unwrap().get_ptr() + } +} + +impl From + for DndAction +{ + fn from( + action: sctk::reexports::client::protocol::wl_data_device_manager::DndAction, + ) -> Self { + let mut a = DndAction::empty(); + if action.contains(sctk::reexports::client::protocol::wl_data_device_manager::DndAction::Copy) { + a |= DndAction::Copy; + } + if action.contains(sctk::reexports::client::protocol::wl_data_device_manager::DndAction::Move) { + a |= DndAction::Move; + } + if action.contains(sctk::reexports::client::protocol::wl_data_device_manager::DndAction::Ask) { + a |= DndAction::Ask; + } + a + } +} + +impl From + for sctk::reexports::client::protocol::wl_data_device_manager::DndAction +{ + fn from(action: DndAction) -> Self { + let mut a = sctk::reexports::client::protocol::wl_data_device_manager::DndAction::empty(); + if action.contains(DndAction::Copy) { + a |= sctk::reexports::client::protocol::wl_data_device_manager::DndAction::Copy; + } + if action.contains(DndAction::Move) { + a |= sctk::reexports::client::protocol::wl_data_device_manager::DndAction::Move; + } + if action.contains(DndAction::Ask) { + a |= sctk::reexports::client::protocol::wl_data_device_manager::DndAction::Ask; + } + a + } +} diff --git a/dnd/src/platform/mod.rs b/dnd/src/platform/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/mime/Cargo.toml b/mime/Cargo.toml index 74bb6ad..247c364 100644 --- a/mime/Cargo.toml +++ b/mime/Cargo.toml @@ -6,6 +6,4 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [target.'cfg(all(unix, not(any(target_os="macos", target_os="android", target_os="emscripten", target_os="ios", target_os="redox"))))'.dependencies] -smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "pop-mime-types" } - - +smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", branch = "dnd" } diff --git a/src/dnd/mod.rs b/src/dnd/mod.rs new file mode 100644 index 0000000..c43a76e --- /dev/null +++ b/src/dnd/mod.rs @@ -0,0 +1,102 @@ +use std::borrow::Cow; + +use ::dnd::{DndAction, DndDestinationRectangle, Sender}; +use dnd::DndSurface; +use mime::{AllowedMimeTypes, AsMimeTypes}; + +pub trait DndProvider { + /// Set up DnD operations for the Clipboard + fn init_dnd( + &self, + _tx: Box + Send + Sync + 'static>, + ) { + } + + /// Start a DnD operation on the given surface with some data + fn start_dnd( + &self, + _internal: bool, + _source_surface: DndSurface, + _icon_surface: Option, + _content: D, + _actions: DndAction, + ) { + } + + /// End the current DnD operation, if there is one + fn end_dnd(&self) {} + + /// Register a surface for receiving DnD offers + /// Rectangles should be provided in order of decreasing priority. + /// This method can be called multiple time for a single surface if the + /// rectangles change. + fn register_dnd_destination( + &self, + _surface: DndSurface, + _rectangles: Vec, + ) { + } + + /// Set the final action after presenting the user with a choice + fn set_action(&self, _action: DndAction) {} + + /// Peek at the contents of a DnD offer + fn peek_offer( + &self, + _mime_type: Cow<'static, str>, + ) -> std::io::Result { + Err(std::io::Error::new( + std::io::ErrorKind::Other, + "DnD not supported", + )) + } +} + +impl DndProvider for crate::PlatformClipboard { + fn init_dnd( + &self, + tx: Box + Send + Sync + 'static>, + ) { + self.raw.init_dnd(tx); + } + + fn start_dnd( + &self, + internal: bool, + source_surface: DndSurface, + icon_surface: Option, + content: D, + actions: DndAction, + ) { + self.raw.start_dnd( + internal, + source_surface, + icon_surface, + content, + actions, + ); + } + + fn end_dnd(&self) { + self.raw.end_dnd(); + } + + fn register_dnd_destination( + &self, + surface: DndSurface, + rectangles: Vec, + ) { + self.raw.register_dnd_destination(surface, rectangles); + } + + fn set_action(&self, action: DndAction) { + self.raw.set_action(action); + } + + fn peek_offer( + &self, + mime_type: Cow<'static, str>, + ) -> std::io::Result { + self.raw.peek_offer::(mime_type) + } +} diff --git a/src/lib.rs b/src/lib.rs index 421b3a1..fd89a71 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,6 +48,8 @@ mod platform; #[path = "platform/dummy.rs"] mod platform; +mod dnd; + use mime::ClipboardStoreData; use raw_window_handle::HasDisplayHandle; use std::error::Error; diff --git a/src/platform/linux.rs b/src/platform/linux.rs index bebf78e..d3cb4f2 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -1,10 +1,14 @@ use crate::{ + dnd::DndProvider, mime::{ClipboardLoadData, ClipboardStoreData}, ClipboardProvider, }; +use dnd::{DndAction, DndDestinationRectangle, DndSurface}; +use mime::{AllowedMimeTypes, AsMimeTypes}; use raw_window_handle::{HasDisplayHandle, RawDisplayHandle}; -use std::error::Error; +use std::{borrow::Cow, error::Error, sync::Arc}; +use wayland::DndSender; pub use clipboard_wayland as wayland; pub use clipboard_x11 as x11; @@ -125,6 +129,78 @@ impl ClipboardProvider for Clipboard { } } +impl DndProvider for Clipboard { + fn init_dnd( + &self, + tx: Box + Send + Sync + 'static>, + ) { + match self { + Clipboard::Wayland(c) => c.init_dnd(DndSender(Arc::new(tx))), + Clipboard::X11(_) => {} + } + } + + fn start_dnd( + &self, + internal: bool, + source_surface: DndSurface, + icon_surface: Option, + content: D, + actions: DndAction, + ) { + match self { + Clipboard::Wayland(c) => c.start_dnd( + internal, + source_surface, + icon_surface, + content, + actions, + ), + Clipboard::X11(_) => {} + } + } + + fn end_dnd(&self) { + match self { + Clipboard::Wayland(c) => c.end_dnd(), + Clipboard::X11(_) => {} + } + } + + fn register_dnd_destination( + &self, + surface: DndSurface, + rectangles: Vec, + ) { + match self { + Clipboard::Wayland(c) => { + c.register_dnd_destination(surface, rectangles) + } + Clipboard::X11(_) => {} + } + } + + fn set_action(&self, action: DndAction) { + match self { + Clipboard::Wayland(c) => c.set_action(action), + Clipboard::X11(_) => {} + } + } + + fn peek_offer( + &self, + mime_type: Cow<'static, str>, + ) -> std::io::Result { + match self { + Clipboard::Wayland(c) => c.peek_offer::(mime_type), + Clipboard::X11(_) => Err(std::io::Error::new( + std::io::ErrorKind::Other, + "DnD not supported", + )), + } + } +} + pub unsafe fn connect( window: &W, ) -> Result> { diff --git a/wayland/Cargo.toml b/wayland/Cargo.toml index 3e28518..6e0c01d 100644 --- a/wayland/Cargo.toml +++ b/wayland/Cargo.toml @@ -10,5 +10,9 @@ documentation = "https://docs.rs/clipboard_wayland" keywords = ["clipboard", "wayland"] [dependencies] -smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "pop-mime-types" } -mime = { path = "../mime" } \ No newline at end of file + +smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", branch = "dnd", features = [ + "dnd", +] } +mime = { path = "../mime" } +dnd = { path = "../dnd" } diff --git a/wayland/src/lib.rs b/wayland/src/lib.rs index b14586e..94ef900 100644 --- a/wayland/src/lib.rs +++ b/wayland/src/lib.rs @@ -16,13 +16,96 @@ use std::{ borrow::Cow, error::Error, ffi::c_void, - sync::{Arc, Mutex}, + sync::{mpsc::SendError, Arc, Mutex}, }; +use dnd::{ + DataWrapper, DndAction, DndDestinationRectangle, DndSurface, Sender, +}; +use smithay_clipboard::dnd::Rectangle; pub use smithay_clipboard::mime::{AllowedMimeTypes, AsMimeTypes, MimeType}; +#[derive(Clone)] +pub struct DndSender( + pub Arc + 'static + Send + Sync>>, +); + +impl smithay_clipboard::dnd::Sender for DndSender { + fn send( + &self, + event: smithay_clipboard::dnd::DndEvent, + ) -> Result<(), SendError>> + { + _ = self.0.send(match event { + smithay_clipboard::dnd::DndEvent::Offer(id, e) => dnd::DndEvent::Offer( + id, + match e { + smithay_clipboard::dnd::OfferEvent::Enter { + x, + y, + mime_types, + surface, + } => dnd::OfferEvent::Enter { + x, + y, + mime_types: mime_types + .into_iter() + .map(|m| m.to_string()) + .collect(), + surface, + }, + smithay_clipboard::dnd::OfferEvent::Motion { x, y } => { + dnd::OfferEvent::Motion { x, y } + } + smithay_clipboard::dnd::OfferEvent::LeaveDestination => { + dnd::OfferEvent::LeaveDestination + } + smithay_clipboard::dnd::OfferEvent::Leave => { + dnd::OfferEvent::Leave + } + smithay_clipboard::dnd::OfferEvent::Drop => { + dnd::OfferEvent::Drop + } + smithay_clipboard::dnd::OfferEvent::SelectedAction( + action, + ) => dnd::OfferEvent::SelectedAction(action.into()), + smithay_clipboard::dnd::OfferEvent::Data { + data, + mime_type, + } => dnd::OfferEvent::Data { + data, + mime_type: mime_type.to_string(), + }, + }, + ), + smithay_clipboard::dnd::DndEvent::Source(e) => match e { + smithay_clipboard::dnd::SourceEvent::Finished => { + dnd::DndEvent::Source(dnd::SourceEvent::Finished) + } + smithay_clipboard::dnd::SourceEvent::Cancelled => { + dnd::DndEvent::Source(dnd::SourceEvent::Cancelled) + } + smithay_clipboard::dnd::SourceEvent::Action(action) => { + dnd::DndEvent::Source(dnd::SourceEvent::Action( + action.into(), + )) + } + smithay_clipboard::dnd::SourceEvent::Mime(mime) => { + dnd::DndEvent::Source(dnd::SourceEvent::Mime( + mime.map(|m| m.to_string()), + )) + } + smithay_clipboard::dnd::SourceEvent::Dropped => { + dnd::DndEvent::Source(dnd::SourceEvent::Dropped) + } + }, + }); + Ok(()) + } +} + pub struct Clipboard { - context: Arc>, + context: Arc>>, } impl Clipboard { @@ -120,4 +203,88 @@ impl Clipboard { ) .map(|(d, m)| (d, m.to_string()))?) } + + pub fn init_dnd(&self, tx: DndSender) { + _ = self.context.lock().unwrap().init_dnd(Box::new(tx)); + } + + /// Start a DnD operation on the given surface with some data + pub fn start_dnd( + &self, + internal: bool, + source_surface: DndSurface, + icon_surface: Option, + content: D, + actions: DndAction, + ) { + _ = self.context.lock().unwrap().start_dnd( + internal, + source_surface, + icon_surface, + DataWrapper(content), + actions.into(), + ); + } + + /// End the current DnD operation, if there is one + pub fn end_dnd(&self) { + _ = self.context.lock().unwrap().end_dnd(); + } + + /// Register a surface for receiving DnD offers + /// Rectangles should be provided in order of decreasing priority. + /// This method can be called multiple time for a single surface if the + /// rectangles change. + pub fn register_dnd_destination( + &self, + surface: DndSurface, + rectangles: Vec, + ) { + _ = self.context.lock().unwrap().register_dnd_destination( + surface, + rectangles + .into_iter() + .map(|r| RectangleWrapper(r).into()) + .collect(), + ); + } + + /// Set the final action after presenting the user with a choice + pub fn set_action(&self, action: DndAction) { + self.context.lock().unwrap().set_action(action.into()); + } + + /// Peek at the contents of a DnD offer + pub fn peek_offer( + &self, + mime_type: Cow<'static, str>, + ) -> std::io::Result { + let d = self + .context + .lock() + .unwrap() + .peek_offer::>(mime_type.into()); + d.map(|d| d.0) + } +} + +pub struct RectangleWrapper(pub DndDestinationRectangle); + +impl From + for smithay_clipboard::dnd::DndDestinationRectangle +{ + fn from(RectangleWrapper(d): RectangleWrapper) -> Self { + smithay_clipboard::dnd::DndDestinationRectangle { + id: d.id, + rectangle: Rectangle { + x: d.rectangle.x, + y: d.rectangle.y, + width: d.rectangle.width, + height: d.rectangle.height, + }, + mime_types: d.mime_types.into_iter().map(MimeType::from).collect(), + actions: d.actions.into(), + preferred: d.preferred.into(), + } + } } From 4e05e3c657fa4f3d101198e943506687501d8abc Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Tue, 26 Mar 2024 16:05:29 -0400 Subject: [PATCH 09/27] make dnd types public --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index fd89a71..e671882 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,7 +48,7 @@ mod platform; #[path = "platform/dummy.rs"] mod platform; -mod dnd; +pub mod dnd; use mime::ClipboardStoreData; use raw_window_handle::HasDisplayHandle; From 5bfbaae180c401968b314366f7fa9e1f0303c875 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Tue, 26 Mar 2024 17:32:09 -0400 Subject: [PATCH 10/27] refactor: update smithay-clipboard --- dnd/src/lib.rs | 4 ++-- dnd/src/platform/linux.rs | 3 ++- macos/src/lib.rs | 8 +++---- mime/src/lib.rs | 17 ++++++++++++++ src/dnd/mod.rs | 4 ++-- src/platform/linux.rs | 2 +- wayland/src/lib.rs | 13 ++++++----- x11/src/error.rs | 6 +++-- x11/src/lib.rs | 48 +++++++++++++++++++++++++-------------- 9 files changed, 70 insertions(+), 35 deletions(-) diff --git a/dnd/src/lib.rs b/dnd/src/lib.rs index 192acae..1a17bd8 100644 --- a/dnd/src/lib.rs +++ b/dnd/src/lib.rs @@ -99,8 +99,8 @@ pub trait Sender { pub trait RawSurface { /// # Safety /// - /// returned pointer must be a valid pointer to the underlying surface, and it must - /// remain valid for as long as `RawSurface` object is alive. + /// returned pointer must be a valid pointer to the underlying surface, and + /// it must remain valid for as long as `RawSurface` object is alive. unsafe fn get_ptr(&mut self) -> *mut c_void; } diff --git a/dnd/src/platform/linux.rs b/dnd/src/platform/linux.rs index bcb7db0..9036dd0 100644 --- a/dnd/src/platform/linux.rs +++ b/dnd/src/platform/linux.rs @@ -44,7 +44,8 @@ impl AsMimeTypes for DataWrapper { impl smithay_clipboard::dnd::RawSurface for DndSurface { unsafe fn get_ptr(&mut self) -> *mut c_void { - // XXX won't panic because this is only called once before it could be cloned + // XXX won't panic because this is only called once before it could be + // cloned Arc::get_mut(&mut self.0).unwrap().get_ptr() } } diff --git a/macos/src/lib.rs b/macos/src/lib.rs index ca9047e..deeb52f 100644 --- a/macos/src/lib.rs +++ b/macos/src/lib.rs @@ -15,11 +15,11 @@ extern crate objc; use objc::runtime::{Class, Object}; -use objc_foundation::{INSArray, INSObject, INSString}; -use objc_foundation::{NSArray, NSDictionary, NSObject, NSString}; +use objc_foundation::{ + INSArray, INSObject, INSString, NSArray, NSDictionary, NSObject, NSString, +}; use objc_id::{Id, Owned}; -use std::error::Error; -use std::mem::transmute; +use std::{error::Error, mem::transmute}; pub struct Clipboard { pasteboard: Id, diff --git a/mime/src/lib.rs b/mime/src/lib.rs index 98f8d29..983827d 100644 --- a/mime/src/lib.rs +++ b/mime/src/lib.rs @@ -4,6 +4,23 @@ pub mod platform; use std::{borrow::Cow, error, fmt}; +/// Raw data from the clipboard +pub struct ClipboardData(pub Vec, pub String); + +impl AllowedMimeTypes for ClipboardData { + fn allowed() -> Cow<'static, [String]> { + Cow::Owned(vec![]) + } +} + +impl TryFrom<(Vec, String)> for ClipboardData { + type Error = Error; + + fn try_from((data, mime): (Vec, String)) -> Result { + Ok(ClipboardData(data, mime)) + } +} + /// Data that can be loaded from the clipboard. pub struct ClipboardLoadData(pub T); diff --git a/src/dnd/mod.rs b/src/dnd/mod.rs index c43a76e..f06f0d1 100644 --- a/src/dnd/mod.rs +++ b/src/dnd/mod.rs @@ -43,7 +43,7 @@ pub trait DndProvider { /// Peek at the contents of a DnD offer fn peek_offer( &self, - _mime_type: Cow<'static, str>, + _mime_type: Option>, ) -> std::io::Result { Err(std::io::Error::new( std::io::ErrorKind::Other, @@ -95,7 +95,7 @@ impl DndProvider for crate::PlatformClipboard { fn peek_offer( &self, - mime_type: Cow<'static, str>, + mime_type: Option>, ) -> std::io::Result { self.raw.peek_offer::(mime_type) } diff --git a/src/platform/linux.rs b/src/platform/linux.rs index d3cb4f2..9ea9b41 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -189,7 +189,7 @@ impl DndProvider for Clipboard { fn peek_offer( &self, - mime_type: Cow<'static, str>, + mime_type: Option>, ) -> std::io::Result { match self { Clipboard::Wayland(c) => c.peek_offer::(mime_type), diff --git a/wayland/src/lib.rs b/wayland/src/lib.rs index 94ef900..ae94714 100644 --- a/wayland/src/lib.rs +++ b/wayland/src/lib.rs @@ -22,6 +22,7 @@ use std::{ use dnd::{ DataWrapper, DndAction, DndDestinationRectangle, DndSurface, Sender, }; +use mime::ClipboardData; use smithay_clipboard::dnd::Rectangle; pub use smithay_clipboard::mime::{AllowedMimeTypes, AsMimeTypes, MimeType}; @@ -178,13 +179,13 @@ impl Clipboard { .context .lock() .unwrap() - .load_primary_raw( + .load_primary_mime::>( allowed .into_iter() .map(|s| MimeType::from(Cow::Owned(s))) .collect::>(), ) - .map(|(d, m)| (d, m.to_string()))?) + .map(|d| (d.0 .0, d.0 .1.to_string()))?) } pub fn read_raw( @@ -195,13 +196,13 @@ impl Clipboard { .context .lock() .unwrap() - .load_raw( + .load_mime::>( allowed .into_iter() .map(|s| MimeType::from(Cow::Owned(s))) .collect::>(), ) - .map(|(d, m)| (d, m.to_string()))?) + .map(|d| (d.0 .0, d.0 .1))?) } pub fn init_dnd(&self, tx: DndSender) { @@ -257,13 +258,13 @@ impl Clipboard { /// Peek at the contents of a DnD offer pub fn peek_offer( &self, - mime_type: Cow<'static, str>, + mime_type: Option>, ) -> std::io::Result { let d = self .context .lock() .unwrap() - .peek_offer::>(mime_type.into()); + .peek_offer::>(mime_type.map(MimeType::from)); d.map(|d| d.0) } } diff --git a/x11/src/error.rs b/x11/src/error.rs index cf04d2f..a08b07a 100644 --- a/x11/src/error.rs +++ b/x11/src/error.rs @@ -1,5 +1,7 @@ -use x11rb::errors::{ConnectError, ConnectionError, ReplyError}; -use x11rb::protocol::xproto::Atom; +use x11rb::{ + errors::{ConnectError, ConnectionError, ReplyError}, + protocol::xproto::Atom, +}; use std::sync::mpsc; diff --git a/x11/src/lib.rs b/x11/src/lib.rs index 0a1e310..f951252 100644 --- a/x11/src/lib.rs +++ b/x11/src/lib.rs @@ -3,17 +3,23 @@ mod error; pub use error::Error; -use x11rb::connection::Connection as _; -use x11rb::errors::ConnectError; -use x11rb::protocol::xproto::{self, Atom, AtomEnum, EventMask, Window}; -use x11rb::protocol::Event; -use x11rb::rust_connection::RustConnection as Connection; -use x11rb::wrapper::ConnectionExt; +use x11rb::{ + connection::Connection as _, + errors::ConnectError, + protocol::{ + xproto::{self, Atom, AtomEnum, EventMask, Window}, + Event, + }, + rust_connection::RustConnection as Connection, + wrapper::ConnectionExt, +}; -use std::collections::HashMap; -use std::sync::{Arc, RwLock}; -use std::thread; -use std::time::{Duration, Instant}; +use std::{ + collections::HashMap, + sync::{Arc, RwLock}, + thread, + time::{Duration, Instant}, +}; const POLL_DURATION: std::time::Duration = Duration::from_micros(50); @@ -60,13 +66,16 @@ impl Clipboard { self.read_selection(self.reader.atoms.clipboard) } - /// Read the current PRIMARY [`Clipboard`] value. pub fn read_primary(&self) -> Result { self.read_selection(self.reader.atoms.primary) } - fn write_selection(&mut self, selection: Atom, contents: String) -> Result<(), Error> { + fn write_selection( + &mut self, + selection: Atom, + contents: String, + ) -> Result<(), Error> { let target = self.writer.atoms.utf8_string; self.selections @@ -124,9 +133,13 @@ impl Clipboard { selection, target, property, - x11rb::CURRENT_TIME, // FIXME ^ - // Clients should not use CurrentTime for the time argument of a ConvertSelection request. - // Instead, they should use the timestamp of the event that caused the request to be made. + x11rb::CURRENT_TIME, /* FIXME ^ + * Clients should not use CurrentTime for + * the time argument of a ConvertSelection + * request. + * Instead, they should use the timestamp + * of the event that caused the request to + * be made. */ )?; let _ = self.reader.connection.flush()?; @@ -186,8 +199,9 @@ impl Clipboard { continue; }; - // Note that setting the property argument to None indicates that the - // conversion requested could not be made. + // Note that setting the property argument to None indicates + // that the conversion requested could + // not be made. if event.property == AtomEnum::NONE.into() { break; } From 42a888736be51bd8b234060d0a75503184ecad9f Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Wed, 27 Mar 2024 18:28:59 -0400 Subject: [PATCH 11/27] refactor: use icon type when starting dnd --- dnd/src/lib.rs | 12 ++++++++++++ dnd/src/platform/linux.rs | 24 +++++++++++++++++++++++- src/dnd/mod.rs | 6 +++--- src/platform/linux.rs | 4 ++-- wayland/src/lib.rs | 6 +++--- 5 files changed, 43 insertions(+), 9 deletions(-) diff --git a/dnd/src/lib.rs b/dnd/src/lib.rs index 1a17bd8..c1b7ac3 100644 --- a/dnd/src/lib.rs +++ b/dnd/src/lib.rs @@ -119,6 +119,18 @@ pub struct DndDestinationRectangle { pub preferred: DndAction, } +#[derive(Clone)] +pub enum Icon { + Surface(DndSurface), + /// Xrgb8888 or Argb8888 image data with premultiplied alpha + Buffer { + data: Arc>, + width: u32, + height: u32, + transparent: bool, + }, +} + #[derive(Clone)] pub struct DndSurface(pub Arc>); diff --git a/dnd/src/platform/linux.rs b/dnd/src/platform/linux.rs index 9036dd0..5a5766e 100644 --- a/dnd/src/platform/linux.rs +++ b/dnd/src/platform/linux.rs @@ -1,6 +1,6 @@ use std::{borrow::Cow, ffi::c_void, sync::Arc}; -use crate::{DataWrapper, DndAction, DndSurface}; +use crate::{DataWrapper, DndAction, DndSurface, Icon}; use smithay_clipboard::mime::{AllowedMimeTypes, AsMimeTypes, MimeType}; impl< @@ -87,3 +87,25 @@ impl From a } } + +impl From for smithay_clipboard::dnd::Icon { + fn from(icon: Icon) -> Self { + match icon { + Icon::Surface(surface) => { + smithay_clipboard::dnd::Icon::Surface(surface) + } + Icon::Buffer { + data, + width, + height, + transparent, + } => smithay_clipboard::dnd::Icon::Buf { + data: Arc::try_unwrap(data) + .unwrap_or_else(|d| d.as_ref().clone()), + width, + height, + transparent, + }, + } + } +} diff --git a/src/dnd/mod.rs b/src/dnd/mod.rs index f06f0d1..b145ca0 100644 --- a/src/dnd/mod.rs +++ b/src/dnd/mod.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use ::dnd::{DndAction, DndDestinationRectangle, Sender}; -use dnd::DndSurface; +use dnd::{DndSurface, Icon}; use mime::{AllowedMimeTypes, AsMimeTypes}; pub trait DndProvider { @@ -17,7 +17,7 @@ pub trait DndProvider { &self, _internal: bool, _source_surface: DndSurface, - _icon_surface: Option, + _icon_surface: Option, _content: D, _actions: DndAction, ) { @@ -64,7 +64,7 @@ impl DndProvider for crate::PlatformClipboard { &self, internal: bool, source_surface: DndSurface, - icon_surface: Option, + icon_surface: Option, content: D, actions: DndAction, ) { diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 9ea9b41..734f8f6 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -4,7 +4,7 @@ use crate::{ ClipboardProvider, }; -use dnd::{DndAction, DndDestinationRectangle, DndSurface}; +use dnd::{DndAction, DndDestinationRectangle, DndSurface, Icon}; use mime::{AllowedMimeTypes, AsMimeTypes}; use raw_window_handle::{HasDisplayHandle, RawDisplayHandle}; use std::{borrow::Cow, error::Error, sync::Arc}; @@ -144,7 +144,7 @@ impl DndProvider for Clipboard { &self, internal: bool, source_surface: DndSurface, - icon_surface: Option, + icon_surface: Option, content: D, actions: DndAction, ) { diff --git a/wayland/src/lib.rs b/wayland/src/lib.rs index ae94714..deb59d9 100644 --- a/wayland/src/lib.rs +++ b/wayland/src/lib.rs @@ -23,7 +23,7 @@ use dnd::{ DataWrapper, DndAction, DndDestinationRectangle, DndSurface, Sender, }; use mime::ClipboardData; -use smithay_clipboard::dnd::Rectangle; +use smithay_clipboard::dnd::{Icon, Rectangle}; pub use smithay_clipboard::mime::{AllowedMimeTypes, AsMimeTypes, MimeType}; #[derive(Clone)] @@ -214,14 +214,14 @@ impl Clipboard { &self, internal: bool, source_surface: DndSurface, - icon_surface: Option, + icon_surface: Option, content: D, actions: DndAction, ) { _ = self.context.lock().unwrap().start_dnd( internal, source_surface, - icon_surface, + icon_surface.map(|i| Icon::::from(i)), DataWrapper(content), actions.into(), ); From c9e7a8e2fae144336dd45835545216b521d1d2ac Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Fri, 29 Mar 2024 15:48:29 -0400 Subject: [PATCH 12/27] refactor: use rwh --- dnd/Cargo.toml | 5 ++- dnd/src/lib.rs | 91 +++++++++++++++++++++++++++++++++------ dnd/src/platform/linux.rs | 7 ++- mime/Cargo.toml | 2 +- wayland/Cargo.toml | 2 +- 5 files changed, 89 insertions(+), 18 deletions(-) diff --git a/dnd/Cargo.toml b/dnd/Cargo.toml index 202a186..a9e3b34 100644 --- a/dnd/Cargo.toml +++ b/dnd/Cargo.toml @@ -6,11 +6,12 @@ edition = "2021" [dependencies] mime = { path = "../mime" } bitflags = "2.5.0" +raw-window-handle = "0.6" [target.'cfg(all(unix, not(any(target_os="macos", target_os="android", target_os="emscripten", target_os="ios", target_os="redox"))))'.dependencies] -smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", branch = "dnd", features = [ +smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "pop-dnd", features = [ "dnd", ] } sctk = { package = "smithay-client-toolkit", git = "https://github.com/Smithay/client-toolkit", default-features = false, features = [ "calloop", -] } +], rev = "3bed072" } diff --git a/dnd/src/lib.rs b/dnd/src/lib.rs index c1b7ac3..16c99f9 100644 --- a/dnd/src/lib.rs +++ b/dnd/src/lib.rs @@ -1,10 +1,11 @@ use std::{ borrow::Cow, - ffi::c_void, + fmt::Debug, sync::{mpsc::SendError, Arc}, }; use bitflags::bitflags; +use raw_window_handle::HasWindowHandle; #[cfg(all( unix, @@ -30,7 +31,7 @@ bitflags! { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum DndEvent { /// Dnd Offer event with the corresponding destination rectangle ID. Offer(Option, OfferEvent), @@ -38,7 +39,19 @@ pub enum DndEvent { Source(SourceEvent), } -#[derive(Debug)] +impl PartialEq for DndEvent { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (DndEvent::Offer(a, b), DndEvent::Offer(a2, b2)) => { + a == a2 && b == b2 + } + (DndEvent::Source(a), DndEvent::Source(b)) => a == b, + _ => false, + } + } +} + +#[derive(Debug, Clone)] pub enum SourceEvent { /// DnD operation ended. Finished, @@ -54,7 +67,20 @@ pub enum SourceEvent { Dropped, } -#[derive(Debug)] +impl PartialEq for SourceEvent { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (SourceEvent::Finished, SourceEvent::Finished) + | (SourceEvent::Cancelled, SourceEvent::Cancelled) + | (SourceEvent::Dropped, SourceEvent::Dropped) => true, + (SourceEvent::Action(a), SourceEvent::Action(b)) => a == b, + (SourceEvent::Mime(a), SourceEvent::Mime(b)) => a == b, + _ => false, + } + } +} + +#[derive(Debug, Clone)] pub enum OfferEvent { Enter { x: f64, @@ -82,6 +108,45 @@ pub enum OfferEvent { }, } +impl PartialEq for OfferEvent { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + ( + OfferEvent::Enter { + x, + y, + mime_types, + surface: _, + }, + OfferEvent::Enter { + x: x2, + y: y2, + mime_types: mime_types2, + surface: _, + }, + ) => x == x2 && y == y2 && mime_types == mime_types2, + ( + OfferEvent::Motion { x, y }, + OfferEvent::Motion { x: x2, y: y2 }, + ) => x == x2 && y == y2, + (OfferEvent::LeaveDestination, OfferEvent::LeaveDestination) + | (OfferEvent::Leave, OfferEvent::Leave) + | (OfferEvent::Drop, OfferEvent::Drop) => true, + (OfferEvent::SelectedAction(a), OfferEvent::SelectedAction(b)) => { + a == b + } + ( + OfferEvent::Data { data, mime_type }, + OfferEvent::Data { + data: data2, + mime_type: mime_type2, + }, + ) => data == data2 && mime_type == mime_type2, + _ => false, + } + } +} + /// A rectangle with a logical location and size relative to a [`DndSurface`] #[derive(Debug, Default, Clone)] pub struct Rectangle { @@ -96,14 +161,6 @@ pub trait Sender { fn send(&self, t: DndEvent) -> Result<(), SendError>>; } -pub trait RawSurface { - /// # Safety - /// - /// returned pointer must be a valid pointer to the underlying surface, and - /// it must remain valid for as long as `RawSurface` object is alive. - unsafe fn get_ptr(&mut self) -> *mut c_void; -} - /// A rectangle with a logical location and size relative to a [`DndSurface`] #[derive(Debug, Clone)] pub struct DndDestinationRectangle { @@ -132,7 +189,15 @@ pub enum Icon { } #[derive(Clone)] -pub struct DndSurface(pub Arc>); +pub struct DndSurface( + pub Arc>, +); + +impl Debug for DndSurface { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("DndSurface").finish() + } +} #[derive(Clone)] pub struct DataWrapper(pub T); diff --git a/dnd/src/platform/linux.rs b/dnd/src/platform/linux.rs index 5a5766e..abafb4b 100644 --- a/dnd/src/platform/linux.rs +++ b/dnd/src/platform/linux.rs @@ -1,6 +1,7 @@ use std::{borrow::Cow, ffi::c_void, sync::Arc}; use crate::{DataWrapper, DndAction, DndSurface, Icon}; +use raw_window_handle::HasWindowHandle; use smithay_clipboard::mime::{AllowedMimeTypes, AsMimeTypes, MimeType}; impl< @@ -46,7 +47,11 @@ impl smithay_clipboard::dnd::RawSurface for DndSurface { unsafe fn get_ptr(&mut self) -> *mut c_void { // XXX won't panic because this is only called once before it could be // cloned - Arc::get_mut(&mut self.0).unwrap().get_ptr() + Arc::get_mut(&mut self.0) + .unwrap() + .window_handle() + .unwrap() + .get_ptr() } } diff --git a/mime/Cargo.toml b/mime/Cargo.toml index 247c364..2d94eef 100644 --- a/mime/Cargo.toml +++ b/mime/Cargo.toml @@ -6,4 +6,4 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [target.'cfg(all(unix, not(any(target_os="macos", target_os="android", target_os="emscripten", target_os="ios", target_os="redox"))))'.dependencies] -smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", branch = "dnd" } +smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "pop-dnd" } diff --git a/wayland/Cargo.toml b/wayland/Cargo.toml index 6e0c01d..3f2b079 100644 --- a/wayland/Cargo.toml +++ b/wayland/Cargo.toml @@ -11,7 +11,7 @@ keywords = ["clipboard", "wayland"] [dependencies] -smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", branch = "dnd", features = [ +smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "pop-dnd", features = [ "dnd", ] } mime = { path = "../mime" } From f290a4fc8674172cd3f221040a73a143138fc8d7 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Thu, 4 Apr 2024 19:40:49 -0400 Subject: [PATCH 13/27] chore: update tag --- dnd/Cargo.toml | 2 +- mime/Cargo.toml | 2 +- wayland/Cargo.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dnd/Cargo.toml b/dnd/Cargo.toml index a9e3b34..b072716 100644 --- a/dnd/Cargo.toml +++ b/dnd/Cargo.toml @@ -9,7 +9,7 @@ bitflags = "2.5.0" raw-window-handle = "0.6" [target.'cfg(all(unix, not(any(target_os="macos", target_os="android", target_os="emscripten", target_os="ios", target_os="redox"))))'.dependencies] -smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "pop-dnd", features = [ +smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "pop-dnd-2", features = [ "dnd", ] } sctk = { package = "smithay-client-toolkit", git = "https://github.com/Smithay/client-toolkit", default-features = false, features = [ diff --git a/mime/Cargo.toml b/mime/Cargo.toml index 2d94eef..63dea4c 100644 --- a/mime/Cargo.toml +++ b/mime/Cargo.toml @@ -6,4 +6,4 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [target.'cfg(all(unix, not(any(target_os="macos", target_os="android", target_os="emscripten", target_os="ios", target_os="redox"))))'.dependencies] -smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "pop-dnd" } +smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "pop-dnd-2" } diff --git a/wayland/Cargo.toml b/wayland/Cargo.toml index 3f2b079..f8cd08b 100644 --- a/wayland/Cargo.toml +++ b/wayland/Cargo.toml @@ -11,7 +11,7 @@ keywords = ["clipboard", "wayland"] [dependencies] -smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "pop-dnd", features = [ +smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "pop-dnd-2", features = [ "dnd", ] } mime = { path = "../mime" } From e717bd58a0ccc7ef8bedda234f9159b5938e1894 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Tue, 9 Apr 2024 14:39:19 -0400 Subject: [PATCH 14/27] chore: update tag --- dnd/Cargo.toml | 2 +- mime/Cargo.toml | 2 +- wayland/Cargo.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dnd/Cargo.toml b/dnd/Cargo.toml index b072716..f977386 100644 --- a/dnd/Cargo.toml +++ b/dnd/Cargo.toml @@ -9,7 +9,7 @@ bitflags = "2.5.0" raw-window-handle = "0.6" [target.'cfg(all(unix, not(any(target_os="macos", target_os="android", target_os="emscripten", target_os="ios", target_os="redox"))))'.dependencies] -smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "pop-dnd-2", features = [ +smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "pop-dnd-3", features = [ "dnd", ] } sctk = { package = "smithay-client-toolkit", git = "https://github.com/Smithay/client-toolkit", default-features = false, features = [ diff --git a/mime/Cargo.toml b/mime/Cargo.toml index 63dea4c..61643a0 100644 --- a/mime/Cargo.toml +++ b/mime/Cargo.toml @@ -6,4 +6,4 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [target.'cfg(all(unix, not(any(target_os="macos", target_os="android", target_os="emscripten", target_os="ios", target_os="redox"))))'.dependencies] -smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "pop-dnd-2" } +smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "pop-dnd-3" } diff --git a/wayland/Cargo.toml b/wayland/Cargo.toml index f8cd08b..eafd122 100644 --- a/wayland/Cargo.toml +++ b/wayland/Cargo.toml @@ -11,7 +11,7 @@ keywords = ["clipboard", "wayland"] [dependencies] -smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "pop-dnd-2", features = [ +smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "pop-dnd-3", features = [ "dnd", ] } mime = { path = "../mime" } From ff869add344a591898832db4d95da7ecc2ad318a Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Sat, 13 Apr 2024 08:42:31 -0600 Subject: [PATCH 15/27] Make dummy Clipboard pub --- src/platform/dummy.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/dummy.rs b/src/platform/dummy.rs index 096f68c..016235c 100644 --- a/src/platform/dummy.rs +++ b/src/platform/dummy.rs @@ -2,7 +2,7 @@ use crate::ClipboardProvider; use raw_window_handle::HasDisplayHandle; -struct Clipboard; +pub struct Clipboard; pub fn connect( _window: &W, From 6ca3cc3d4c221d34a4c385957bd3fd8be9ad48e5 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Sat, 13 Apr 2024 08:59:37 -0600 Subject: [PATCH 16/27] Stub DndProvider for dummy platform --- src/platform/dummy.rs | 44 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/src/platform/dummy.rs b/src/platform/dummy.rs index 016235c..3fc3779 100644 --- a/src/platform/dummy.rs +++ b/src/platform/dummy.rs @@ -1,6 +1,9 @@ -use crate::ClipboardProvider; +use crate::{dnd::DndProvider, ClipboardProvider}; +use dnd::{DndAction, DndDestinationRectangle, DndSurface, Icon}; +use mime::{AllowedMimeTypes, AsMimeTypes}; use raw_window_handle::HasDisplayHandle; +use std::borrow::Cow; pub struct Clipboard; @@ -23,6 +26,45 @@ impl ClipboardProvider for Clipboard { } } +impl DndProvider for Clipboard { + fn init_dnd( + &self, + _tx: Box + Send + Sync + 'static>, + ) { + } + + fn start_dnd( + &self, + _internal: bool, + _source_surface: DndSurface, + _icon_surface: Option, + _content: D, + _actions: DndAction, + ) { + } + + fn end_dnd(&self) {} + + fn register_dnd_destination( + &self, + _surface: DndSurface, + _rectangles: Vec, + ) { + } + + fn set_action(&self, _action: DndAction) {} + + fn peek_offer( + &self, + _mime_type: Option>, + ) -> std::io::Result { + Err(std::io::Error::new( + std::io::ErrorKind::Other, + "DnD not supported", + )) + } +} + #[derive(Debug, Clone, Copy, thiserror::Error)] enum Error { #[error("unimplemented")] From 8a816d8f218e290041bb5ef6d3b695c38e0a53b7 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Wed, 1 May 2024 11:55:55 -0400 Subject: [PATCH 17/27] chore: update smithay-clipboard --- dnd/Cargo.toml | 2 +- mime/Cargo.toml | 2 +- wayland/Cargo.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dnd/Cargo.toml b/dnd/Cargo.toml index f977386..207195e 100644 --- a/dnd/Cargo.toml +++ b/dnd/Cargo.toml @@ -9,7 +9,7 @@ bitflags = "2.5.0" raw-window-handle = "0.6" [target.'cfg(all(unix, not(any(target_os="macos", target_os="android", target_os="emscripten", target_os="ios", target_os="redox"))))'.dependencies] -smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "pop-dnd-3", features = [ +smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "pop-dnd-4", features = [ "dnd", ] } sctk = { package = "smithay-client-toolkit", git = "https://github.com/Smithay/client-toolkit", default-features = false, features = [ diff --git a/mime/Cargo.toml b/mime/Cargo.toml index 61643a0..5628e18 100644 --- a/mime/Cargo.toml +++ b/mime/Cargo.toml @@ -6,4 +6,4 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [target.'cfg(all(unix, not(any(target_os="macos", target_os="android", target_os="emscripten", target_os="ios", target_os="redox"))))'.dependencies] -smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "pop-dnd-3" } +smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "pop-dnd-4" } diff --git a/wayland/Cargo.toml b/wayland/Cargo.toml index eafd122..46d0497 100644 --- a/wayland/Cargo.toml +++ b/wayland/Cargo.toml @@ -11,7 +11,7 @@ keywords = ["clipboard", "wayland"] [dependencies] -smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "pop-dnd-3", features = [ +smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "pop-dnd-4", features = [ "dnd", ] } mime = { path = "../mime" } From a5be70405574e98c292537fd3b01a1352550b9bf Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Tue, 11 Jun 2024 17:58:40 -0700 Subject: [PATCH 18/27] Update smithay-clipboard --- dnd/Cargo.toml | 6 +++--- mime/Cargo.toml | 2 +- wayland/Cargo.toml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dnd/Cargo.toml b/dnd/Cargo.toml index 207195e..85e61bb 100644 --- a/dnd/Cargo.toml +++ b/dnd/Cargo.toml @@ -9,9 +9,9 @@ bitflags = "2.5.0" raw-window-handle = "0.6" [target.'cfg(all(unix, not(any(target_os="macos", target_os="android", target_os="emscripten", target_os="ios", target_os="redox"))))'.dependencies] -smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "pop-dnd-4", features = [ +smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "pop-dnd-5", features = [ "dnd", ] } -sctk = { package = "smithay-client-toolkit", git = "https://github.com/Smithay/client-toolkit", default-features = false, features = [ +sctk = { package = "smithay-client-toolkit", version = "0.19.1", default-features = false, features = [ "calloop", -], rev = "3bed072" } +] } diff --git a/mime/Cargo.toml b/mime/Cargo.toml index 5628e18..176ca63 100644 --- a/mime/Cargo.toml +++ b/mime/Cargo.toml @@ -6,4 +6,4 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [target.'cfg(all(unix, not(any(target_os="macos", target_os="android", target_os="emscripten", target_os="ios", target_os="redox"))))'.dependencies] -smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "pop-dnd-4" } +smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "pop-dnd-5" } diff --git a/wayland/Cargo.toml b/wayland/Cargo.toml index 46d0497..d2f8c5b 100644 --- a/wayland/Cargo.toml +++ b/wayland/Cargo.toml @@ -11,7 +11,7 @@ keywords = ["clipboard", "wayland"] [dependencies] -smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "pop-dnd-4", features = [ +smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "pop-dnd-5", features = [ "dnd", ] } mime = { path = "../mime" } From 7c59b07b9172d8e0401f7e06609e1050575309c9 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Wed, 19 Jun 2024 12:37:26 -0400 Subject: [PATCH 19/27] fix: stubs for various platforms --- src/platform/android.rs | 48 ++++++++++++++++++++++++++++++++++++++--- src/platform/macos.rs | 44 ++++++++++++++++++++++++++++++++++++- src/platform/windows.rs | 44 +++++++++++++++++++++++++++++++++++-- 3 files changed, 130 insertions(+), 6 deletions(-) diff --git a/src/platform/android.rs b/src/platform/android.rs index c2d460a..885f80d 100644 --- a/src/platform/android.rs +++ b/src/platform/android.rs @@ -1,12 +1,15 @@ use crate::ClipboardProvider; +use crate::dnd::DndProvider; +use dnd::{DndAction, DndDestinationRectangle, DndSurface, Icon}; +use mime::{AllowedMimeTypes, AsMimeTypes}; use raw_window_handle::HasDisplayHandle; -use std::error::Error; +use std::{borrow::Cow, error::Error}; pub fn connect( _window: &W, ) -> Result> { - Ok(Clipboard::new()) + Clipboard::new() } pub struct Clipboard; @@ -36,7 +39,46 @@ impl ClipboardProvider for Clipboard { Err(Box::new(AndroidClipboardError::Unimplemented)) } - fn write(&mut self, contents: String) -> Result<(), Box> { + fn write(&mut self, _contents: String) -> Result<(), Box> { Err(Box::new(AndroidClipboardError::Unimplemented)) } } + +impl DndProvider for Clipboard { + fn init_dnd( + &self, + _tx: Box + Send + Sync + 'static>, + ) { + } + + fn start_dnd( + &self, + _internal: bool, + _source_surface: DndSurface, + _icon_surface: Option, + _content: D, + _actions: DndAction, + ) { + } + + fn end_dnd(&self) {} + + fn register_dnd_destination( + &self, + _surface: DndSurface, + _rectangles: Vec, + ) { + } + + fn set_action(&self, _action: DndAction) {} + + fn peek_offer( + &self, + _mime_type: Option>, + ) -> std::io::Result { + Err(std::io::Error::new( + std::io::ErrorKind::Other, + "DnD not supported", + )) + } +} diff --git a/src/platform/macos.rs b/src/platform/macos.rs index 084ace8..375799f 100644 --- a/src/platform/macos.rs +++ b/src/platform/macos.rs @@ -1,8 +1,11 @@ use crate::ClipboardProvider; +use crate::dnd::DndProvider; pub(crate) use clipboard_macos::Clipboard; +use dnd::{DndAction, DndDestinationRectangle, DndSurface, Icon}; +use mime::{AllowedMimeTypes, AsMimeTypes}; use raw_window_handle::HasDisplayHandle; -use std::error::Error; +use std::{borrow::Cow, error::Error}; pub fn connect( _window: &W, @@ -10,6 +13,45 @@ pub fn connect( Clipboard::new() } +impl DndProvider for Clipboard { + fn init_dnd( + &self, + _tx: Box + Send + Sync + 'static>, + ) { + } + + fn start_dnd( + &self, + _internal: bool, + _source_surface: DndSurface, + _icon_surface: Option, + _content: D, + _actions: DndAction, + ) { + } + + fn end_dnd(&self) {} + + fn register_dnd_destination( + &self, + _surface: DndSurface, + _rectangles: Vec, + ) { + } + + fn set_action(&self, _action: DndAction) {} + + fn peek_offer( + &self, + _mime_type: Option>, + ) -> std::io::Result { + Err(std::io::Error::new( + std::io::ErrorKind::Other, + "DnD not supported", + )) + } +} + impl ClipboardProvider for Clipboard { fn read(&self) -> Result> { self.read() diff --git a/src/platform/windows.rs b/src/platform/windows.rs index f317b10..de79fc6 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -1,15 +1,55 @@ use crate::ClipboardProvider; +use crate::dnd::DndProvider; use clipboard_win::{get_clipboard_string, set_clipboard_string}; +use dnd::{DndAction, DndDestinationRectangle, DndSurface, Icon}; +use mime::{AllowedMimeTypes, AsMimeTypes}; use raw_window_handle::HasDisplayHandle; - -use std::error::Error; +use std::{borrow::Cow, error::Error}; pub fn connect( _window: &W, ) -> Result> { Ok(Clipboard) } +impl DndProvider for Clipboard { + fn init_dnd( + &self, + _tx: Box + Send + Sync + 'static>, + ) { + } + + fn start_dnd( + &self, + _internal: bool, + _source_surface: DndSurface, + _icon_surface: Option, + _content: D, + _actions: DndAction, + ) { + } + + fn end_dnd(&self) {} + + fn register_dnd_destination( + &self, + _surface: DndSurface, + _rectangles: Vec, + ) { + } + + fn set_action(&self, _action: DndAction) {} + + fn peek_offer( + &self, + _mime_type: Option>, + ) -> std::io::Result { + Err(std::io::Error::new( + std::io::ErrorKind::Other, + "DnD not supported", + )) + } +} pub struct Clipboard; From 1832d5637b27ad9b0c781724ab30bec844366c63 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Fri, 20 Sep 2024 15:10:45 -0400 Subject: [PATCH 20/27] refactor: remove Sized bound --- dnd/src/lib.rs | 4 +--- src/lib.rs | 2 +- src/platform/ios.rs | 2 +- src/platform/linux.rs | 2 +- src/platform/macos.rs | 2 +- src/platform/windows.rs | 2 +- 6 files changed, 6 insertions(+), 8 deletions(-) diff --git a/dnd/src/lib.rs b/dnd/src/lib.rs index 16c99f9..66ccd8c 100644 --- a/dnd/src/lib.rs +++ b/dnd/src/lib.rs @@ -189,9 +189,7 @@ pub enum Icon { } #[derive(Clone)] -pub struct DndSurface( - pub Arc>, -); +pub struct DndSurface(pub Arc); impl Debug for DndSurface { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/src/lib.rs b/src/lib.rs index e671882..54d330f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,7 +62,7 @@ pub struct PlatformClipboard { impl PlatformClipboard { /// Safety: the display handle must be valid for the lifetime of `Clipboard` - pub unsafe fn connect( + pub unsafe fn connect( window: &W, ) -> Result> { Ok(PlatformClipboard { diff --git a/src/platform/ios.rs b/src/platform/ios.rs index 1ab6e84..fd961c7 100644 --- a/src/platform/ios.rs +++ b/src/platform/ios.rs @@ -3,7 +3,7 @@ use crate::ClipboardProvider; use raw_window_handle::HasDisplayHandle; use std::error::Error; -pub fn connect( +pub fn connect( _window: &W, ) -> Result> { Clipboard::new() diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 734f8f6..409a7e4 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -201,7 +201,7 @@ impl DndProvider for Clipboard { } } -pub unsafe fn connect( +pub unsafe fn connect( window: &W, ) -> Result> { let clipboard = match window.display_handle()?.as_raw() { diff --git a/src/platform/macos.rs b/src/platform/macos.rs index 375799f..d89c446 100644 --- a/src/platform/macos.rs +++ b/src/platform/macos.rs @@ -7,7 +7,7 @@ use mime::{AllowedMimeTypes, AsMimeTypes}; use raw_window_handle::HasDisplayHandle; use std::{borrow::Cow, error::Error}; -pub fn connect( +pub fn connect( _window: &W, ) -> Result> { Clipboard::new() diff --git a/src/platform/windows.rs b/src/platform/windows.rs index de79fc6..81d1e73 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -7,7 +7,7 @@ use mime::{AllowedMimeTypes, AsMimeTypes}; use raw_window_handle::HasDisplayHandle; use std::{borrow::Cow, error::Error}; -pub fn connect( +pub fn connect( _window: &W, ) -> Result> { Ok(Clipboard) From 68f111cfd0345c047723ec5c9f401e8da9666c6c Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Mon, 21 Oct 2024 11:13:59 -0400 Subject: [PATCH 21/27] fix: dummy --- src/platform/dummy.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/dummy.rs b/src/platform/dummy.rs index 3fc3779..c86bcbf 100644 --- a/src/platform/dummy.rs +++ b/src/platform/dummy.rs @@ -7,7 +7,7 @@ use std::borrow::Cow; pub struct Clipboard; -pub fn connect( +pub fn connect( _window: &W, ) -> Result> { Ok(Clipboard) From a83bf83784276aaa882ef13555295a2ad9edd265 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Mon, 21 Oct 2024 11:14:55 -0400 Subject: [PATCH 22/27] refactor: remove Box --- src/platform/linux.rs | 2 +- wayland/src/lib.rs | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 409a7e4..3d953f1 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -135,7 +135,7 @@ impl DndProvider for Clipboard { tx: Box + Send + Sync + 'static>, ) { match self { - Clipboard::Wayland(c) => c.init_dnd(DndSender(Arc::new(tx))), + Clipboard::Wayland(c) => c.init_dnd(DndSender(Arc::from(tx))), Clipboard::X11(_) => {} } } diff --git a/wayland/src/lib.rs b/wayland/src/lib.rs index deb59d9..298e87c 100644 --- a/wayland/src/lib.rs +++ b/wayland/src/lib.rs @@ -27,9 +27,7 @@ use smithay_clipboard::dnd::{Icon, Rectangle}; pub use smithay_clipboard::mime::{AllowedMimeTypes, AsMimeTypes, MimeType}; #[derive(Clone)] -pub struct DndSender( - pub Arc + 'static + Send + Sync>>, -); +pub struct DndSender(pub Arc + 'static + Send + Sync>); impl smithay_clipboard::dnd::Sender for DndSender { fn send( From 6b9faab87bea9cebec6ae036906fd67fed254f5f Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Fri, 27 Dec 2024 15:57:12 -0800 Subject: [PATCH 23/27] dnd: Don't call `Arc::get_mut` The caller of `start_dnd` may also have a reference, so this can panic. Either the public API shouldn't take an `Arc` or this should be avoided. There doesn't seem to be any reason this is needed anyway. --- dnd/src/platform/linux.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/dnd/src/platform/linux.rs b/dnd/src/platform/linux.rs index abafb4b..31a1879 100644 --- a/dnd/src/platform/linux.rs +++ b/dnd/src/platform/linux.rs @@ -45,13 +45,7 @@ impl AsMimeTypes for DataWrapper { impl smithay_clipboard::dnd::RawSurface for DndSurface { unsafe fn get_ptr(&mut self) -> *mut c_void { - // XXX won't panic because this is only called once before it could be - // cloned - Arc::get_mut(&mut self.0) - .unwrap() - .window_handle() - .unwrap() - .get_ptr() + self.0.window_handle().unwrap().get_ptr() } } From 3a7af79e54db6854d8aa9d9e2866a9288d0f95d5 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Mon, 2 Mar 2026 12:32:18 -0500 Subject: [PATCH 24/27] update smithay-clipboard and sctk --- dnd/Cargo.toml | 4 ++-- mime/Cargo.toml | 2 +- src/platform/linux.rs | 1 + wayland/Cargo.toml | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/dnd/Cargo.toml b/dnd/Cargo.toml index 85e61bb..0ddb926 100644 --- a/dnd/Cargo.toml +++ b/dnd/Cargo.toml @@ -9,9 +9,9 @@ bitflags = "2.5.0" raw-window-handle = "0.6" [target.'cfg(all(unix, not(any(target_os="macos", target_os="android", target_os="emscripten", target_os="ios", target_os="redox"))))'.dependencies] -smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "pop-dnd-5", features = [ +smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "sctk-0.20", features = [ "dnd", ] } -sctk = { package = "smithay-client-toolkit", version = "0.19.1", default-features = false, features = [ +sctk = { package = "smithay-client-toolkit", version = "0.20", default-features = false, features = [ "calloop", ] } diff --git a/mime/Cargo.toml b/mime/Cargo.toml index 176ca63..adacdb5 100644 --- a/mime/Cargo.toml +++ b/mime/Cargo.toml @@ -6,4 +6,4 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [target.'cfg(all(unix, not(any(target_os="macos", target_os="android", target_os="emscripten", target_os="ios", target_os="redox"))))'.dependencies] -smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "pop-dnd-5" } +smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "sctk-0.20" } diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 3d953f1..379c8ce 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -74,6 +74,7 @@ impl ClipboardProvider for Clipboard { { match self { Clipboard::Wayland(c) => { + dbg!("linux write data..."); Some(c.write_data::>(contents)) } Clipboard::X11(_) => None, diff --git a/wayland/Cargo.toml b/wayland/Cargo.toml index d2f8c5b..fd1d4fc 100644 --- a/wayland/Cargo.toml +++ b/wayland/Cargo.toml @@ -11,7 +11,7 @@ keywords = ["clipboard", "wayland"] [dependencies] -smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "pop-dnd-5", features = [ +smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "sctk-0.20", features = [ "dnd", ] } mime = { path = "../mime" } From f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Mon, 16 Mar 2026 16:51:48 -0400 Subject: [PATCH 25/27] cleanup: dbg --- src/platform/linux.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 379c8ce..3d953f1 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -74,7 +74,6 @@ impl ClipboardProvider for Clipboard { { match self { Clipboard::Wayland(c) => { - dbg!("linux write data..."); Some(c.write_data::>(contents)) } Clipboard::X11(_) => None, From 319db02e5219c557c8f03b0e33a8eb4075cabb85 Mon Sep 17 00:00:00 2001 From: leyoda Date: Fri, 24 Apr 2026 06:50:48 +0200 Subject: [PATCH 26/27] yoda: gate clipboard_x11 behind an opt-in feature Upstream pulled clipboard_x11 unconditionally on unix, which drags x11rb + x11rb-protocol and ~500 symbols of X11 clipboard code into every Wayland-only iced build. Add an x11 feature (default-on for compat) that gates: - the clipboard_x11 workspace dep (now optional) - `pub use clipboard_x11 as x11` re-export - the Clipboard::X11 enum variant - every match arm that handles it - the fallback branch in `unsafe fn connect` (returns a helpful error when called with a non-Wayland display handle) Wayland-only builds set `window_clipboard = { ..., default-features = false, features = ["wayland"] }` and skip the x11 backend entirely. --- Cargo.toml | 14 ++++++++++++-- src/platform/linux.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2aacaa0..4d13334 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,16 @@ readme = "README.md" keywords = ["clipboard", "window", "ui", "gui", "raw-window-handle"] categories = ["gui"] +[features] +# Yoda: put the unix clipboard backends behind opt-in features. Upstream +# pulled both X11 + Wayland unconditionally on unix — pure bloat for +# Wayland-only builds (clipboard_x11 pulls x11rb + its protocol machinery). +# Default keeps both enabled to preserve upstream behaviour; yoda consumers +# pass default-features=false + "wayland" at the dep declaration. +default = ["x11", "wayland"] +x11 = ["dep:clipboard_x11"] +wayland = ["dep:clipboard_wayland"] + [dependencies] raw-window-handle = { version = "0.6", features = ["std"] } thiserror = "1.0" @@ -24,8 +34,8 @@ clipboard-win = { version = "5.0", features = ["std"] } clipboard_macos = { version = "0.1", path = "./macos" } [target.'cfg(all(unix, not(any(target_os="macos", target_os="android", target_os="emscripten", target_os="ios", target_os="redox"))))'.dependencies] -clipboard_x11 = { version = "0.4.2", path = "./x11" } -clipboard_wayland = { version = "0.2.2", path = "./wayland" } +clipboard_x11 = { version = "0.4.2", path = "./x11", optional = true } +clipboard_wayland = { version = "0.2.2", path = "./wayland", optional = true } [dev-dependencies] rand = "0.8" diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 3d953f1..755dfb9 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -11,10 +11,12 @@ use std::{borrow::Cow, error::Error, sync::Arc}; use wayland::DndSender; pub use clipboard_wayland as wayland; +#[cfg(feature = "x11")] pub use clipboard_x11 as x11; pub enum Clipboard { Wayland(wayland::Clipboard), + #[cfg(feature = "x11")] X11(x11::Clipboard), } @@ -22,6 +24,7 @@ impl ClipboardProvider for Clipboard { fn read(&self) -> Result> { match self { Clipboard::Wayland(c) => c.read(), + #[cfg(feature = "x11")] Clipboard::X11(c) => c.read().map_err(Box::from), } } @@ -29,6 +32,7 @@ impl ClipboardProvider for Clipboard { fn write(&mut self, contents: String) -> Result<(), Box> { match self { Clipboard::Wayland(c) => c.write(contents), + #[cfg(feature = "x11")] Clipboard::X11(c) => c.write(contents).map_err(Box::from), } } @@ -36,6 +40,7 @@ impl ClipboardProvider for Clipboard { fn read_primary(&self) -> Option>> { match self { Clipboard::Wayland(c) => Some(c.read_primary()), + #[cfg(feature = "x11")] Clipboard::X11(c) => Some(c.read_primary().map_err(Box::from)), } } @@ -46,6 +51,7 @@ impl ClipboardProvider for Clipboard { ) -> Option>> { match self { Clipboard::Wayland(c) => Some(c.write_primary(contents)), + #[cfg(feature = "x11")] Clipboard::X11(c) => { Some(c.write_primary(contents).map_err(Box::from)) } @@ -61,6 +67,7 @@ impl ClipboardProvider for Clipboard { let ret = c.read_data::>(); Some(ret.map(|ret| ret.0)) } + #[cfg(feature = "x11")] Clipboard::X11(_) => None, } } @@ -76,6 +83,7 @@ impl ClipboardProvider for Clipboard { Clipboard::Wayland(c) => { Some(c.write_data::>(contents)) } + #[cfg(feature = "x11")] Clipboard::X11(_) => None, } } @@ -89,6 +97,7 @@ impl ClipboardProvider for Clipboard { let ret = c.read_primary_data::>(); Some(ret.map(|ret| ret.0)) } + #[cfg(feature = "x11")] Clipboard::X11(_) => None, } } @@ -99,6 +108,7 @@ impl ClipboardProvider for Clipboard { ) -> Option, String), Box>> { match self { Clipboard::Wayland(c) => Some(c.read_primary_raw(allowed)), + #[cfg(feature = "x11")] Clipboard::X11(_) => None, } } @@ -109,6 +119,7 @@ impl ClipboardProvider for Clipboard { ) -> Option, String), Box>> { match self { Clipboard::Wayland(c) => Some(c.read_raw(allowed)), + #[cfg(feature = "x11")] Clipboard::X11(_) => None, } } @@ -124,6 +135,7 @@ impl ClipboardProvider for Clipboard { Clipboard::Wayland(c) => { Some(c.write_primary_data::>(contents)) } + #[cfg(feature = "x11")] Clipboard::X11(_) => None, } } @@ -136,6 +148,7 @@ impl DndProvider for Clipboard { ) { match self { Clipboard::Wayland(c) => c.init_dnd(DndSender(Arc::from(tx))), + #[cfg(feature = "x11")] Clipboard::X11(_) => {} } } @@ -156,6 +169,7 @@ impl DndProvider for Clipboard { content, actions, ), + #[cfg(feature = "x11")] Clipboard::X11(_) => {} } } @@ -163,6 +177,7 @@ impl DndProvider for Clipboard { fn end_dnd(&self) { match self { Clipboard::Wayland(c) => c.end_dnd(), + #[cfg(feature = "x11")] Clipboard::X11(_) => {} } } @@ -176,6 +191,7 @@ impl DndProvider for Clipboard { Clipboard::Wayland(c) => { c.register_dnd_destination(surface, rectangles) } + #[cfg(feature = "x11")] Clipboard::X11(_) => {} } } @@ -183,6 +199,7 @@ impl DndProvider for Clipboard { fn set_action(&self, action: DndAction) { match self { Clipboard::Wayland(c) => c.set_action(action), + #[cfg(feature = "x11")] Clipboard::X11(_) => {} } } @@ -193,6 +210,7 @@ impl DndProvider for Clipboard { ) -> std::io::Result { match self { Clipboard::Wayland(c) => c.peek_offer::(mime_type), + #[cfg(feature = "x11")] Clipboard::X11(_) => Err(std::io::Error::new( std::io::ErrorKind::Other, "DnD not supported", @@ -208,7 +226,15 @@ pub unsafe fn connect( RawDisplayHandle::Wayland(handle) => Clipboard::Wayland( wayland::Clipboard::connect(handle.display.as_ptr()), ) as _, + #[cfg(feature = "x11")] _ => Clipboard::X11(x11::Clipboard::connect()?) as _, + #[cfg(not(feature = "x11"))] + _ => { + return Err(Box::from( + "Yoda window_clipboard: X11 feature disabled; \ + non-Wayland display handles are not supported", + )) + } }; Ok(clipboard) From af8e6a872d8ac4956e044b9cb05a615a48261a57 Mon Sep 17 00:00:00 2001 From: Lionel DARNIS Date: Mon, 25 May 2026 19:36:22 +0200 Subject: [PATCH 27/27] chore: use local smithay-clipboard --- dnd/Cargo.toml | 2 +- mime/Cargo.toml | 2 +- wayland/Cargo.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dnd/Cargo.toml b/dnd/Cargo.toml index 0ddb926..868bceb 100644 --- a/dnd/Cargo.toml +++ b/dnd/Cargo.toml @@ -9,7 +9,7 @@ bitflags = "2.5.0" raw-window-handle = "0.6" [target.'cfg(all(unix, not(any(target_os="macos", target_os="android", target_os="emscripten", target_os="ios", target_os="redox"))))'.dependencies] -smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "sctk-0.20", features = [ +smithay-clipboard = { path = "../../smithay-clipboard", features = [ "dnd", ] } sctk = { package = "smithay-client-toolkit", version = "0.20", default-features = false, features = [ diff --git a/mime/Cargo.toml b/mime/Cargo.toml index adacdb5..259b4f0 100644 --- a/mime/Cargo.toml +++ b/mime/Cargo.toml @@ -6,4 +6,4 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [target.'cfg(all(unix, not(any(target_os="macos", target_os="android", target_os="emscripten", target_os="ios", target_os="redox"))))'.dependencies] -smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "sctk-0.20" } +smithay-clipboard = { path = "../../smithay-clipboard" } diff --git a/wayland/Cargo.toml b/wayland/Cargo.toml index fd1d4fc..615f76c 100644 --- a/wayland/Cargo.toml +++ b/wayland/Cargo.toml @@ -11,7 +11,7 @@ keywords = ["clipboard", "wayland"] [dependencies] -smithay-clipboard = { git = "https://github.com/pop-os/smithay-clipboard", tag = "sctk-0.20", features = [ +smithay-clipboard = { path = "../../smithay-clipboard", features = [ "dnd", ] } mime = { path = "../mime" }