diff --git a/src/common.rs b/src/common.rs index adda67c..2e10fea 100644 --- a/src/common.rs +++ b/src/common.rs @@ -174,9 +174,6 @@ impl Drop for ScopeGuard { /// Common trait for sealing platform extension traits. pub(crate) mod private { - // This is currently unused on macOS, so silence the warning which appears - // since there's no extension traits making use of this trait sealing structure. - #[cfg_attr(target_vendor = "apple", allow(unreachable_pub, dead_code))] pub trait Sealed {} impl Sealed for crate::Get<'_> {} diff --git a/src/lib.rs b/src/lib.rs index 57256d5..ee752f6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,6 +27,9 @@ pub use platform::{ClearExtLinux, GetExtLinux, LinuxClipboardKind, SetExtLinux}; #[cfg(windows)] pub use platform::SetExtWindows; +#[cfg(target_os = "macos")] +pub use platform::SetExtApple; + /// The OS independent struct for accessing the clipboard. /// /// Any number of `Clipboard` instances are allowed to exist at a single point in time. Note however diff --git a/src/platform/mod.rs b/src/platform/mod.rs index b336463..268eb47 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -14,4 +14,4 @@ pub use windows::*; #[cfg(target_os = "macos")] mod osx; #[cfg(target_os = "macos")] -pub(crate) use osx::*; +pub use osx::*; diff --git a/src/platform/osx.rs b/src/platform/osx.rs index ac4dc79..ec5f881 100644 --- a/src/platform/osx.rs +++ b/src/platform/osx.rs @@ -8,9 +8,9 @@ the Apache 2.0 or the MIT license at the licensee's choice. The terms and conditions of the chosen license apply to this file. */ -use crate::common::Error; #[cfg(feature = "image-data")] use crate::common::ImageData; +use crate::common::{private, Error}; use objc2::{ msg_send_id, rc::{autoreleasepool, Id}, @@ -18,7 +18,7 @@ use objc2::{ ClassType, }; use objc2_app_kit::{NSPasteboard, NSPasteboardTypeHTML, NSPasteboardTypeString}; -use objc2_foundation::{NSArray, NSString}; +use objc2_foundation::{ns_string, NSArray, NSString}; use std::{ borrow::Cow, panic::{RefUnwindSafe, UnwindSafe}, @@ -235,11 +235,12 @@ impl<'clipboard> Get<'clipboard> { pub(crate) struct Set<'clipboard> { clipboard: &'clipboard mut Clipboard, + exclude_from_history: bool, } impl<'clipboard> Set<'clipboard> { pub(crate) fn new(clipboard: &'clipboard mut Clipboard) -> Self { - Self { clipboard } + Self { clipboard, exclude_from_history: false } } pub(crate) fn text(self, data: Cow<'_, str>) -> Result<(), Error> { @@ -248,6 +249,9 @@ impl<'clipboard> Set<'clipboard> { let string_array = NSArray::from_vec(vec![ProtocolObject::from_id(NSString::from_str(&data))]); let success = unsafe { self.clipboard.pasteboard.writeObjects(&string_array) }; + + add_clipboard_exclusions(self.clipboard, self.exclude_from_history); + if success { Ok(()) } else { @@ -279,6 +283,9 @@ impl<'clipboard> Set<'clipboard> { }; } } + + add_clipboard_exclusions(self.clipboard, self.exclude_from_history); + if success { Ok(()) } else { @@ -296,6 +303,9 @@ impl<'clipboard> Set<'clipboard> { let image_array = NSArray::from_vec(vec![ProtocolObject::from_id(image)]); let success = unsafe { self.clipboard.pasteboard.writeObjects(&image_array) }; + + add_clipboard_exclusions(self.clipboard, self.exclude_from_history); + if success { Ok(()) } else { @@ -322,3 +332,33 @@ impl<'clipboard> Clear<'clipboard> { Ok(()) } } + +fn add_clipboard_exclusions(clipboard: &mut Clipboard, exclude_from_history: bool) { + // On Mac there isn't an official standard for excluding data from clipboard, however + // there is an unofficial standard which is to set `org.nspasteboard.ConcealedType`. + // + // See http://nspasteboard.org/ for details about the community standard. + if exclude_from_history { + unsafe { + clipboard + .pasteboard + .setString_forType(ns_string!(""), ns_string!("org.nspasteboard.ConcealedType")); + } + } +} + +/// Apple-specific extensions to the [`Set`](crate::Set) builder. +pub trait SetExtApple: private::Sealed { + /// Excludes the data which will be set on the clipboard from being added to + /// third party clipboard history software. + /// + /// See http://nspasteboard.org/ for details about the community standard. + fn exclude_from_history(self) -> Self; +} + +impl SetExtApple for crate::Set<'_> { + fn exclude_from_history(mut self) -> Self { + self.platform.exclude_from_history = true; + self + } +}