diff --git a/.gitignore b/.gitignore index 8d723be..0decf06 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ target .vscode *.png +.idea/ \ No newline at end of file diff --git a/src/platform/osx.rs b/src/platform/osx.rs index a0fbf7b..706c785 100644 --- a/src/platform/osx.rs +++ b/src/platform/osx.rs @@ -20,11 +20,16 @@ use core_graphics::{ }; use objc::{ msg_send, - runtime::{Class, Object}, + rc::autoreleasepool, + runtime::{Class, Object, BOOL, NO}, sel, sel_impl, }; -use objc_foundation::{INSArray, INSObject, INSString, NSArray, NSDictionary, NSObject, NSString}; -use objc_id::{Id, Owned}; +use objc_foundation::{INSArray, INSString, NSArray, NSString}; +#[cfg(feature = "image-data")] +use objc_foundation::{INSObject, NSDictionary, NSObject}; +use objc_id::Id; +#[cfg(feature = "image-data")] +use objc_id::Owned; use once_cell::sync::Lazy; use std::borrow::Cow; @@ -35,6 +40,7 @@ extern "C" { static NSPasteboardTypeString: *const Object; } +#[allow(unused)] static NSSTRING_CLASS: Lazy<&Class> = Lazy::new(|| Class::get("NSString").unwrap()); #[cfg(feature = "image-data")] static NSIMAGE_CLASS: Lazy<&Class> = Lazy::new(|| Class::get("NSImage").unwrap()); @@ -179,73 +185,72 @@ impl<'clipboard> Get<'clipboard> { } pub(crate) fn text(self) -> Result { - let string_class = object_class(&NSSTRING_CLASS); - let classes: Id> = NSArray::from_vec(vec![string_class]); - let options: Id> = NSDictionary::new(); + autoreleasepool(|| unsafe { + let types: *mut NSArray<*mut NSString> = msg_send![self.pasteboard, types]; + let has_str: BOOL = msg_send![types, containsObject: NSPasteboardTypeString]; + + if has_str == NO { + return Err(Error::ContentNotAvailable); + } - let string_array: Id> = unsafe { - let obj: *mut NSArray = - msg_send![self.pasteboard, readObjectsForClasses:&*classes options:&*options]; + let text: *mut NSString = + msg_send![self.pasteboard, stringForType: NSPasteboardTypeString]; - if obj.is_null() { + if text.is_null() { return Err(Error::ContentNotAvailable); - } else { - Id::from_ptr(obj) } - }; - string_array - .first_object() - .map(|obj| obj.as_str().to_owned()) - .ok_or(Error::ContentNotAvailable) + Ok((*text).as_str().to_owned()) + }) } #[cfg(feature = "image-data")] pub(crate) fn image(self) -> Result, Error> { use std::io::Cursor; - - let image_class: Id = object_class(&NSIMAGE_CLASS); - let classes = vec![image_class]; - let classes: Id> = NSArray::from_vec(classes); - let options: Id> = NSDictionary::new(); - - let contents: Id> = unsafe { - let obj: *mut NSArray = - msg_send![self.pasteboard, readObjectsForClasses:&*classes options:&*options]; - - if obj.is_null() { - return Err(Error::ContentNotAvailable); - } else { - Id::from_ptr(obj) + autoreleasepool(|| unsafe { + let image_class: Id = object_class(&NSIMAGE_CLASS); + let classes = vec![image_class]; + let classes: Id> = NSArray::from_vec(classes); + let options: Id> = NSDictionary::new(); + + let contents: Id> = { + let obj: *mut NSArray = + msg_send![self.pasteboard, readObjectsForClasses:&*classes options:&*options]; + + if obj.is_null() { + return Err(Error::ContentNotAvailable); + } else { + Id::from_ptr(obj) + } + }; + + let obj = match contents.first_object() { + Some(obj) if obj.is_kind_of(&NSIMAGE_CLASS) => obj, + Some(_) | None => return Err(Error::ContentNotAvailable), + }; + + let data = { + let tiff: &NSArray = msg_send![obj, TIFFRepresentation]; + let len: usize = msg_send![tiff, length]; + let bytes: *const u8 = msg_send![tiff, bytes]; + Cursor::new(std::slice::from_raw_parts(bytes, len)) + }; + + let reader = image::io::Reader::with_format(data, image::ImageFormat::Tiff); + match reader.decode() { + Ok(img) => { + let rgba = img.into_rgba8(); + let (width, height) = rgba.dimensions(); + + Ok(ImageData { + width: width as usize, + height: height as usize, + bytes: rgba.into_raw().into(), + }) + } + Err(_) => Err(Error::ConversionFailure), } - }; - - let obj = match contents.first_object() { - Some(obj) if obj.is_kind_of(&NSIMAGE_CLASS) => obj, - Some(_) | None => return Err(Error::ContentNotAvailable), - }; - - let tiff: &NSArray = unsafe { msg_send![obj, TIFFRepresentation] }; - let data = unsafe { - let len: usize = msg_send![tiff, length]; - let bytes: *const u8 = msg_send![tiff, bytes]; - - Cursor::new(std::slice::from_raw_parts(bytes, len)) - }; - let reader = image::io::Reader::with_format(data, image::ImageFormat::Tiff); - match reader.decode() { - Ok(img) => { - let rgba = img.into_rgba8(); - let (width, height) = rgba.dimensions(); - - Ok(ImageData { - width: width as usize, - height: height as usize, - bytes: rgba.into_raw().into(), - }) - } - Err(_) => Err(Error::ConversionFailure), - } + }) } } @@ -341,6 +346,7 @@ impl<'clipboard> Clear<'clipboard> { /// Convenience function to get an Objective-C object from a /// specific class. +#[cfg(feature = "image-data")] fn object_class(class: &'static Class) -> Id { // SAFETY: `Class` is a valid object and `Id` will not mutate it unsafe { Id::from_ptr(class as *const Class as *mut NSObject) }