/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ //! Objective-C bindings and helpers. // Forward all exports from the `objc` crate. pub use objc::*; /// An objc class instance which contains rust data `T`. #[derive(Clone, Copy)] #[repr(transparent)] pub struct Objc { pub instance: cocoa::id, _phantom: std::marker::PhantomData<*mut T>, } impl Objc { pub fn new(instance: cocoa::id) -> Self { Objc { instance, _phantom: std::marker::PhantomData, } } pub fn data(&self) -> &T { let data = *unsafe { (*self.instance).get_ivar::("rust_self") } as *mut T; unsafe { &*data } } pub fn data_mut(&mut self) -> &mut T { let data = *unsafe { (*self.instance).get_ivar::("rust_self") } as *mut T; unsafe { &mut *data } } } impl std::ops::Deref for Objc { type Target = T; fn deref(&self) -> &Self::Target { self.data() } } impl std::ops::DerefMut for Objc { fn deref_mut(&mut self) -> &mut Self::Target { self.data_mut() } } unsafe impl Encode for Objc { fn encode() -> Encoding { cocoa::id::encode() } } /// Wrapper to provide `Encode` for bindgen-generated types (bindgen should do this in the future). #[repr(transparent)] pub struct Ptr(pub T); unsafe impl Encode for Ptr { fn encode() -> Encoding { cocoa::id::encode() } } /// A strong objective-c reference to `T`. #[repr(transparent)] pub struct StrongRef { ptr: rc::StrongPtr, _phantom: std::marker::PhantomData, } impl Clone for StrongRef { fn clone(&self) -> Self { StrongRef { ptr: self.ptr.clone(), _phantom: self._phantom, } } } impl std::ops::Deref for StrongRef { type Target = T; fn deref(&self) -> &Self::Target { let obj: &cocoa::id = &*self.ptr; unsafe { std::mem::transmute(obj) } } } impl StrongRef { /// Assume the given pointer-wrapper is an already-retained strong reference. /// /// # Safety /// The type _must_ be the same size as cocoa::id and contain only a cocoa::id. pub unsafe fn new(v: T) -> Self { std::mem::transmute_copy(&v) } /// Retain the given pointer-wrapper. /// /// # Safety /// The type _must_ be the same size as cocoa::id and contain only a cocoa::id. #[allow(dead_code)] pub unsafe fn retain(v: T) -> Self { let obj: cocoa::id = std::mem::transmute_copy(&v); StrongRef { ptr: rc::StrongPtr::retain(obj), _phantom: std::marker::PhantomData, } } pub fn autorelease(self) -> T { let obj = self.ptr.autorelease(); unsafe { std::mem::transmute_copy(&obj) } } pub fn weak(&self) -> WeakRef { WeakRef { ptr: self.ptr.weak(), _phantom: std::marker::PhantomData, } } /// Unwrap the StrongRef value without affecting reference counts. /// /// This is the opposite of `new`. #[allow(dead_code)] pub fn unwrap(self: Self) -> T { let v = unsafe { std::mem::transmute_copy(&self) }; std::mem::forget(self); v } /// Cast to a base class. /// /// Bindgen pointer-wrappers have trival `From for Base` implementations. pub fn cast>(self) -> StrongRef { StrongRef { ptr: self.ptr, _phantom: std::marker::PhantomData, } } } /// A weak objective-c reference to `T`. #[derive(Clone)] #[repr(transparent)] pub struct WeakRef { ptr: rc::WeakPtr, _phantom: std::marker::PhantomData, } impl WeakRef { pub fn lock(&self) -> Option> { let ptr = self.ptr.load(); if ptr.is_null() { None } else { Some(StrongRef { ptr, _phantom: std::marker::PhantomData, }) } } } /// A macro for creating an objc class. /// /// Classes _must_ be registered before use (`Objc::register()`). /// /// Example: /// ``` /// struct Foo(u8); /// /// objc_class! { /// impl Foo: NSObject { /// #[sel(mySelector:)] /// fn my_selector(&mut self, arg: u8) -> u8 { /// self.0 + arg /// } /// } /// } /// /// fn make_foo() -> StrongRef> { /// Foo(42).into_object() /// } /// ``` /// /// Call `T::into_object()` to create the objective-c class instance. macro_rules! objc_class { ( impl $name:ident : $base:ident $(<$($protocol:ident),+>)? { $( #[sel($($sel:tt)+)] fn $mname:ident (&mut $self:ident $(, $argname:ident : $argtype:ty )*) $(-> $rettype:ty)? $body:block )* }) => { impl Objc<$name> { pub fn register() { let mut decl = declare::ClassDecl::new(concat!("CR", stringify!($name)), class!($base)).expect(concat!("failed to declare ", stringify!($name), " class")); $($(decl.add_protocol(runtime::Protocol::get(stringify!($protocol)).expect(concat!("failed to find ",stringify!($protocol)," protocol")));)+)? decl.add_ivar::("rust_self"); $({ extern "C" fn method_impl(obj: &mut runtime::Object, _: runtime::Sel $(, $argname: $argtype )*) $(-> $rettype)? { Objc::<$name>::new(obj).$mname($($argname),*) } unsafe { decl.add_method(sel!($($sel)+), method_impl as extern "C" fn(&mut runtime::Object, runtime::Sel $(, $argname: $argtype )*) $(-> $rettype)?); } })* { extern "C" fn dealloc_impl(obj: &runtime::Object, _: runtime::Sel) { drop(unsafe { Box::from_raw(*obj.get_ivar::("rust_self") as *mut $name) }); unsafe { let _: () = msg_send![super(obj, class!(NSObject)), dealloc]; } } unsafe { decl.add_method(sel!(dealloc), dealloc_impl as extern "C" fn(&runtime::Object, runtime::Sel)); } } decl.register(); } pub fn class() -> &'static runtime::Class { runtime::Class::get(concat!("CR", stringify!($name))).expect("class not registered") } $(fn $mname (&mut $self $(, $argname : $argtype )*) $(-> $rettype)? $body)* } impl $name { pub fn into_object(self) -> StrongRef> { let obj: *mut runtime::Object = unsafe { msg_send![Objc::::class(), alloc] }; unsafe { (*obj).set_ivar("rust_self", Box::into_raw(Box::new(self)) as usize) }; let obj: *mut runtime::Object = unsafe { msg_send![obj, init] }; unsafe { StrongRef::new(Objc::new(obj)) } } } } } pub(crate) use objc_class;