diff --git a/Cargo.toml b/Cargo.toml index 34e001c..fa92f15 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ wayland = ["smithay-clipboard"] clipboard-win = "3.0.2" [target.'cfg(target_os = "macos")'.dependencies] +lazy_static = "1.4" objc = "0.2" objc_id = "0.1" objc-foundation = "0.1" diff --git a/src/osx_clipboard.rs b/src/osx_clipboard.rs index ef1e7d9..a2b72a5 100644 --- a/src/osx_clipboard.rs +++ b/src/osx_clipboard.rs @@ -13,7 +13,9 @@ // limitations under the License. use std::mem::transmute; +use std::sync::Mutex; +use lazy_static::lazy_static; use objc::runtime::{Class, Object}; use objc::{msg_send, sel, sel_impl}; use objc_foundation::{INSArray, INSObject, INSString}; @@ -22,6 +24,11 @@ use objc_id::{Id, Owned}; use crate::common::*; +// creating or accessing the context is not thread-safe, and needs to be protected +lazy_static! { + static ref CLIPBOARD_CONTEXT_MUTEX: Mutex<()> = Mutex::new(()); +} + pub struct OSXClipboardContext { pasteboard: Id, } @@ -32,6 +39,10 @@ extern "C" {} impl OSXClipboardContext { pub fn new() -> Result { + let lock = CLIPBOARD_CONTEXT_MUTEX.lock(); + if !lock.is_ok() { + return Err("could not acquire mutex".into()); + } let cls = Class::get("NSPasteboard").ok_or("Class::get(\"NSPasteboard\")")?; let pasteboard: *mut Object = unsafe { msg_send![cls, generalPasteboard] }; if pasteboard.is_null() { @@ -44,6 +55,10 @@ impl OSXClipboardContext { impl ClipboardProvider for OSXClipboardContext { fn get_contents(&mut self) -> Result { + let lock = CLIPBOARD_CONTEXT_MUTEX.lock(); + if !lock.is_ok() { + return Err("could not acquire mutex".into()); + } let string_class: Id = { let cls: Id = unsafe { Id::from_ptr(class("NSString")) }; unsafe { transmute(cls) } @@ -66,6 +81,10 @@ impl ClipboardProvider for OSXClipboardContext { } fn set_contents(&mut self, data: String) -> Result<()> { + let lock = CLIPBOARD_CONTEXT_MUTEX.lock(); + if !lock.is_ok() { + return Err("could not acquire mutex".into()); + } let string_array = NSArray::from_vec(vec![NSString::from_str(&data)]); let _: usize = unsafe { msg_send![self.pasteboard, clearContents] }; let success: bool = unsafe { msg_send![self.pasteboard, writeObjects: string_array] }; diff --git a/tests/concurrency.rs b/tests/concurrency.rs new file mode 100644 index 0000000..f2b1204 --- /dev/null +++ b/tests/concurrency.rs @@ -0,0 +1,29 @@ +use copypasta::{ClipboardContext, ClipboardProvider}; + +fn some_other_fn() { + let mut ctx = ClipboardContext::new().unwrap(); + ctx.get_contents().unwrap(); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn foo() { + let mut ctx = ClipboardContext::new().unwrap(); + ctx.set_contents("Dummy".into()).unwrap(); + ctx.get_contents().unwrap(); + + some_other_fn(); + } + + #[test] + fn bar() { + let mut ctx = ClipboardContext::new().unwrap(); + ctx.set_contents("Dummy".into()).unwrap(); + ctx.get_contents().unwrap(); + + some_other_fn(); + } +}