diff --git a/examples/set_html.rs b/examples/set_html.rs
new file mode 100644
index 0000000..2662d31
--- /dev/null
+++ b/examples/set_html.rs
@@ -0,0 +1,19 @@
+use arboard::Clipboard;
+use simple_logger::SimpleLogger;
+use std::{thread, time::Duration};
+
+fn main() {
+ SimpleLogger::new().init().unwrap();
+ let mut ctx = Clipboard::new().unwrap();
+
+ let html = r#"
Hello, World!
+Lorem ipsum dolor sit amet,
+consectetur adipiscing elit."#;
+
+ let alt_text = r#"Hello, World!
+Lorem ipsum dolor sit amet,
+consectetur adipiscing elit."#;
+
+ ctx.set_html(html, Some(alt_text)).unwrap();
+ thread::sleep(Duration::from_secs(5));
+}
diff --git a/src/lib.rs b/src/lib.rs
index 447e4f4..25c5864 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -60,6 +60,17 @@ impl Clipboard {
self.set().text(text)
}
+ /// Places the HTML as well as a plain-text alternative onto the clipboard.
+ ///
+ /// Any valid utf-8 string is accepted.
+ pub fn set_html<'a, T: Into>>(
+ &mut self,
+ html: T,
+ alt_text: Option,
+ ) -> Result<(), Error> {
+ self.set().html(html, alt_text)
+ }
+
/// Fetches image data from the clipboard, and returns the decoded pixels.
///
/// Any image data placed on the clipboard with `set_image` will be possible read back, using
@@ -142,6 +153,20 @@ impl Set<'_> {
self.platform.text(text)
}
+ /// Completes the "set" operation by placing HTML as well as a plain-text alternative onto the
+ /// clipboard.
+ ///
+ /// Any valid UTF-8 string is accepted.
+ pub fn html<'a, T: Into>>(
+ self,
+ html: T,
+ alt_text: Option,
+ ) -> Result<(), Error> {
+ let html = html.into();
+ let alt_text = alt_text.map(|e| e.into());
+ self.platform.html(html, alt_text)
+ }
+
/// Completes the "set" operation by placing an image onto the clipboard.
///
/// The chosen output format, depending on the platform is the following:
@@ -219,6 +244,27 @@ fn all_tests() {
// confirm it is OK to clear when already empty.
ctx.clear().unwrap();
}
+ {
+ let mut ctx = Clipboard::new().unwrap();
+ let html = "hello world!";
+
+ ctx.set_html(html, None).unwrap();
+
+ match ctx.get_text() {
+ Ok(text) => assert!(text.is_empty()),
+ Err(Error::ContentNotAvailable) => {}
+ Err(e) => panic!("unexpected error: {}", e),
+ };
+ }
+ {
+ let mut ctx = Clipboard::new().unwrap();
+
+ let html = "hello world!";
+ let alt_text = "hello world!";
+
+ ctx.set_html(html, Some(alt_text)).unwrap();
+ assert_eq!(ctx.get_text().unwrap(), alt_text);
+ }
#[cfg(feature = "image-data")]
{
let mut ctx = Clipboard::new().unwrap();
diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs
index 47519fc..14dc9fe 100644
--- a/src/platform/linux/mod.rs
+++ b/src/platform/linux/mod.rs
@@ -159,6 +159,14 @@ impl<'clipboard> Set<'clipboard> {
}
}
+ pub(crate) fn html(self, html: Cow<'_, str>, alt: Option>) -> Result<(), Error> {
+ match self.clipboard {
+ Clipboard::X11(clipboard) => clipboard.set_html(html, alt, self.selection, self.wait),
+ #[cfg(feature = "wayland-data-control")]
+ Clipboard::WlDataControl(clipboard) => clipboard.set_html(html, alt, self.selection, self.wait),
+ }
+ }
+
#[cfg(feature = "image-data")]
pub(crate) fn image(self, image: ImageData<'_>) -> Result<(), Error> {
match self.clipboard {
diff --git a/src/platform/linux/wayland.rs b/src/platform/linux/wayland.rs
index 51051f0..d88a0fa 100644
--- a/src/platform/linux/wayland.rs
+++ b/src/platform/linux/wayland.rs
@@ -3,7 +3,7 @@ use std::convert::TryInto;
use std::io::Read;
use wl_clipboard_rs::{
- copy::{self, Error as CopyError, Options, Source},
+ copy::{self, Error as CopyError, MimeSource, MimeType, Options, Source},
paste::{self, get_contents, Error as PasteError, Seat},
utils::is_primary_selection_supported,
};
@@ -81,7 +81,6 @@ impl Clipboard {
selection: LinuxClipboardKind,
wait: bool,
) -> Result<(), Error> {
- use wl_clipboard_rs::copy::MimeType;
let mut opts = Options::new();
opts.foreground(wait);
opts.clipboard(selection.try_into()?);
@@ -93,6 +92,36 @@ impl Clipboard {
Ok(())
}
+ pub(crate) fn set_html(
+ &self,
+ html: Cow<'_, str>,
+ alt: Option>,
+ selection: LinuxClipboardKind,
+ wait: bool,
+ ) -> Result<(), Error> {
+ let html_mime = MimeType::Specific(String::from("text/html"));
+ let mut opts = Options::new();
+ opts.foreground(wait);
+ opts.clipboard(selection.try_into()?);
+ let html_source = Source::Bytes(html.into_owned().into_bytes().into_boxed_slice());
+ match alt {
+ Some(alt_text) => {
+ let alt_source =
+ Source::Bytes(alt_text.into_owned().into_bytes().into_boxed_slice());
+ opts.copy_multi(vec![
+ MimeSource { source: alt_source, mime_type: MimeType::Text },
+ MimeSource { source: html_source, mime_type: html_mime },
+ ])
+ }
+ None => opts.copy(html_source, html_mime),
+ }
+ .map_err(|e| match e {
+ CopyError::PrimarySelectionUnsupported => Error::ClipboardNotSupported,
+ other => into_unknown(other),
+ })?;
+ Ok(())
+ }
+
#[cfg(feature = "image-data")]
pub(crate) fn get_image(
&mut self,
@@ -140,8 +169,6 @@ impl Clipboard {
selection: LinuxClipboardKind,
wait: bool,
) -> Result<(), Error> {
- use wl_clipboard_rs::copy::MimeType;
-
let image = encode_as_png(&image)?;
let mut opts = Options::new();
opts.foreground(wait);
diff --git a/src/platform/linux/x11.rs b/src/platform/linux/x11.rs
index 59408c5..14c7865 100644
--- a/src/platform/linux/x11.rs
+++ b/src/platform/linux/x11.rs
@@ -77,6 +77,8 @@ x11rb::atom_manager! {
TEXT,
TEXT_MIME_UNKNOWN: b"text/plain",
+ HTML: b"text/html",
+
PNG_MIME: b"image/png",
// This is just some random name for the property on our window, into which
@@ -172,7 +174,7 @@ impl XContext {
#[derive(Default)]
struct Selection {
- data: RwLock