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()?) + } }