// Copyright 2023 GFX developers // // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be // copied, modified, or distributed except according to those terms. #![allow(deprecated)] #![allow(non_snake_case)] #![allow(non_upper_case_globals)] #[macro_use] pub extern crate bitflags; #[macro_use] pub extern crate log; #[macro_use] pub extern crate objc; #[macro_use] pub extern crate foreign_types; #[macro_use] pub extern crate paste; use std::{ borrow::{Borrow, ToOwned}, marker::PhantomData, mem, ops::Deref, os::raw::c_void, }; use core_graphics_types::{base::CGFloat, geometry::CGSize}; use foreign_types::ForeignType; use objc::runtime::{Object, NO, YES}; /// See #[cfg(target_pointer_width = "64")] pub type NSInteger = i64; /// See #[cfg(not(target_pointer_width = "64"))] pub type NSInteger = i32; /// See #[cfg(target_pointer_width = "64")] pub type NSUInteger = u64; /// See #[cfg(target_pointer_width = "32")] pub type NSUInteger = u32; /// See #[repr(C)] #[derive(Copy, Clone)] pub struct NSRange { pub location: NSUInteger, pub length: NSUInteger, } impl NSRange { #[inline] pub fn new(location: NSUInteger, length: NSUInteger) -> NSRange { NSRange { location, length } } } fn nsstring_as_str(nsstr: &objc::runtime::Object) -> &str { let bytes = unsafe { let bytes: *const std::os::raw::c_char = msg_send![nsstr, UTF8String]; bytes as *const u8 }; let len: NSUInteger = unsafe { msg_send![nsstr, length] }; unsafe { let bytes = std::slice::from_raw_parts(bytes, len as usize); std::str::from_utf8(bytes).unwrap() } } fn nsstring_from_str(string: &str) -> *mut objc::runtime::Object { const UTF8_ENCODING: usize = 4; let cls = class!(NSString); let bytes = string.as_ptr() as *const c_void; unsafe { let obj: *mut objc::runtime::Object = msg_send![cls, alloc]; let obj: *mut objc::runtime::Object = msg_send![ obj, initWithBytes:bytes length:string.len() encoding:UTF8_ENCODING ]; let _: *mut c_void = msg_send![obj, autorelease]; obj } } /// Define a Rust wrapper for an Objective-C opaque type. /// /// This macro adapts the `foreign-types` crate's [`foreign_type!`] /// macro to Objective-C, defining Rust types that represent owned and /// borrowed forms of some underlying Objective-C type, using /// Objective-C's reference counting to manage its lifetime. /// /// Given a use of the form: /// /// ```ignore /// foreign_obj_type! { /// type CType = MTLBuffer; // underlying Objective-C type /// pub struct Buffer; // owned Rust type /// pub struct BufferRef; // borrowed Rust type /// type ParentType = ResourceRef; // borrowed parent class /// } /// ``` /// /// This defines the types `Buffer` and `BufferRef` as owning and /// non-owning types, analogous to `String` and `str`, that manage /// some underlying `*mut MTLBuffer`: /// /// - Both `Buffer` and `BufferRef` implement [`obj::Message`], indicating /// that they can be sent Objective-C messages. /// /// - Dropping a `Buffer` sends the underlying `MTLBuffer` a `release` /// message, and cloning a `BufferRef` sends a `retain` message and /// returns a new `Buffer`. /// /// - `Buffer` dereferences to `BufferRef`. /// /// - `BufferRef` dereferences to its parent type `ResourceRef`. The /// `ParentType` component is optional; if omitted, the `Ref` type /// doesn't implement `Deref` or `DerefMut`. /// /// - Both `Buffer` and `BufferRef` implement `std::fmt::Debug`, /// sending an Objective-C `debugDescription` message to the /// underlying `MTLBuffer`. /// /// Following the `foreign_types` crate's nomenclature, the `Ref` /// suffix indicates that `BufferRef` and `ResourceRef` are non-owning /// types, used *by reference*, like `&BufferRef` or `&ResourceRef`. /// These types are not, themselves, references. macro_rules! foreign_obj_type { { type CType = $raw_ident:ident; pub struct $owned_ident:ident; type ParentType = $parent_ident:ident; } => { foreign_obj_type! { type CType = $raw_ident; pub struct $owned_ident; } impl ::std::ops::Deref for paste!{[<$owned_ident Ref>]} { type Target = paste!{[<$parent_ident Ref>]}; #[inline] fn deref(&self) -> &Self::Target { unsafe { &*(self as *const Self as *const Self::Target) } } } impl ::std::convert::From<$owned_ident> for $parent_ident { fn from(item: $owned_ident) -> Self { unsafe { Self::from_ptr(::std::mem::transmute(item.into_ptr())) } } } }; { type CType = $raw_ident:ident; pub struct $owned_ident:ident; } => { foreign_type! { pub unsafe type $owned_ident: Sync + Send { type CType = $raw_ident; fn drop = crate::obj_drop; fn clone = crate::obj_clone; } } unsafe impl ::objc::Message for $raw_ident { } unsafe impl ::objc::Message for paste!{[<$owned_ident Ref>]} { } impl ::std::fmt::Debug for paste!{[<$owned_ident Ref>]} { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { unsafe { let string: *mut ::objc::runtime::Object = msg_send![self, debugDescription]; write!(f, "{}", crate::nsstring_as_str(&*string)) } } } impl ::std::fmt::Debug for $owned_ident { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { ::std::ops::Deref::deref(self).fmt(f) } } }; } macro_rules! try_objc { { $err_name: ident => $body:expr } => { { let mut $err_name: *mut Object = ::std::ptr::null_mut(); let value = $body; if !$err_name.is_null() { let desc: *mut Object = msg_send![$err_name, localizedDescription]; let compile_error: *const std::os::raw::c_char = msg_send![desc, UTF8String]; let message = CStr::from_ptr(compile_error).to_string_lossy().into_owned(); return Err(message); } value } }; } macro_rules! msg_send_bool { ($obj:expr, $name:ident) => {{ match msg_send![$obj, $name] { YES => true, NO => false, #[cfg(not(target_arch = "aarch64"))] _ => unreachable!(), } }}; ($obj:expr, $name:ident : $arg:expr) => {{ match msg_send![$obj, $name: $arg] { YES => true, NO => false, #[cfg(not(target_arch = "aarch64"))] _ => unreachable!(), } }}; } macro_rules! msg_send_bool_error_check { ($obj:expr, $name:ident: $arg:expr) => {{ let mut err: *mut Object = ptr::null_mut(); let result: BOOL = msg_send![$obj, $name:$arg error:&mut err]; if !err.is_null() { let desc: *mut Object = msg_send![err, localizedDescription]; let c_msg: *const c_char = msg_send![desc, UTF8String]; let message = CStr::from_ptr(c_msg).to_string_lossy().into_owned(); Err(message) } else { match result { YES => Ok(true), NO => Ok(false), #[cfg(not(target_arch = "aarch64"))] _ => unreachable!(), } } }}; } /// See pub struct NSArray { _phantom: PhantomData, } pub struct Array(*mut NSArray) where T: ForeignType + 'static, T::Ref: objc::Message + 'static; pub struct ArrayRef(foreign_types::Opaque, PhantomData) where T: ForeignType + 'static, T::Ref: objc::Message + 'static; impl Drop for Array where T: ForeignType + 'static, T::Ref: objc::Message + 'static, { fn drop(&mut self) { unsafe { let () = msg_send![self.0, release]; } } } impl Clone for Array where T: ForeignType + 'static, T::Ref: objc::Message + 'static, { fn clone(&self) -> Self { unsafe { Array(msg_send![self.0, retain]) } } } unsafe impl objc::Message for NSArray where T: ForeignType + 'static, T::Ref: objc::Message + 'static, { } unsafe impl objc::Message for ArrayRef where T: ForeignType + 'static, T::Ref: objc::Message + 'static, { } impl Array where T: ForeignType + 'static, T::Ref: objc::Message + 'static, { pub fn from_slice<'a>(s: &[&T::Ref]) -> &'a ArrayRef { unsafe { let class = class!(NSArray); msg_send![class, arrayWithObjects: s.as_ptr() count: s.len()] } } pub fn from_owned_slice<'a>(s: &[T]) -> &'a ArrayRef { unsafe { let class = class!(NSArray); msg_send![class, arrayWithObjects: s.as_ptr() count: s.len()] } } } unsafe impl foreign_types::ForeignType for Array where T: ForeignType + 'static, T::Ref: objc::Message + 'static, { type CType = NSArray; type Ref = ArrayRef; unsafe fn from_ptr(p: *mut NSArray) -> Self { Array(p) } fn as_ptr(&self) -> *mut NSArray { self.0 } } unsafe impl foreign_types::ForeignTypeRef for ArrayRef where T: ForeignType + 'static, T::Ref: objc::Message + 'static, { type CType = NSArray; } impl Deref for Array where T: ForeignType + 'static, T::Ref: objc::Message + 'static, { type Target = ArrayRef; #[inline] fn deref(&self) -> &ArrayRef { unsafe { mem::transmute(self.as_ptr()) } } } impl Borrow> for Array where T: ForeignType + 'static, T::Ref: objc::Message + 'static, { fn borrow(&self) -> &ArrayRef { unsafe { mem::transmute(self.as_ptr()) } } } impl ToOwned for ArrayRef where T: ForeignType + 'static, T::Ref: objc::Message + 'static, { type Owned = Array; fn to_owned(&self) -> Array { unsafe { Array::from_ptr(msg_send![self, retain]) } } } /// See pub enum CAMetalDrawable {} foreign_obj_type! { type CType = CAMetalDrawable; pub struct MetalDrawable; type ParentType = Drawable; } impl MetalDrawableRef { pub fn texture(&self) -> &TextureRef { unsafe { msg_send![self, texture] } } } pub enum NSObject {} foreign_obj_type! { type CType = NSObject; pub struct NsObject; } impl NsObjectRef { pub fn conforms_to_protocol(&self) -> Result { let name = ::std::any::type_name::(); if let Some(name) = name.split("::").last() { if let Some(protocol) = objc::runtime::Protocol::get(name) { Ok(unsafe { msg_send![self, conformsToProtocol: protocol] }) } else { Err(format!("Can not find the protocol for type: {}.", name)) } } else { Err(format!("Unexpected type name: {}.", name)) } } } // See pub enum CAMetalLayer {} foreign_obj_type! { type CType = CAMetalLayer; pub struct MetalLayer; } impl MetalLayer { pub fn new() -> Self { unsafe { let class = class!(CAMetalLayer); msg_send![class, new] } } } impl MetalLayerRef { pub fn device(&self) -> &DeviceRef { unsafe { msg_send![self, device] } } pub fn set_device(&self, device: &DeviceRef) { unsafe { msg_send![self, setDevice: device] } } pub fn pixel_format(&self) -> MTLPixelFormat { unsafe { msg_send![self, pixelFormat] } } pub fn set_pixel_format(&self, pixel_format: MTLPixelFormat) { unsafe { msg_send![self, setPixelFormat: pixel_format] } } pub fn drawable_size(&self) -> CGSize { unsafe { msg_send![self, drawableSize] } } pub fn set_drawable_size(&self, size: CGSize) { unsafe { msg_send![self, setDrawableSize: size] } } pub fn presents_with_transaction(&self) -> bool { unsafe { msg_send_bool![self, presentsWithTransaction] } } pub fn set_presents_with_transaction(&self, transaction: bool) { unsafe { msg_send![self, setPresentsWithTransaction: transaction] } } pub fn display_sync_enabled(&self) -> bool { unsafe { msg_send_bool![self, displaySyncEnabled] } } pub fn set_display_sync_enabled(&self, enabled: bool) { unsafe { msg_send![self, setDisplaySyncEnabled: enabled] } } pub fn maximum_drawable_count(&self) -> NSUInteger { unsafe { msg_send![self, maximumDrawableCount] } } pub fn set_maximum_drawable_count(&self, count: NSUInteger) { unsafe { msg_send![self, setMaximumDrawableCount: count] } } pub fn set_edge_antialiasing_mask(&self, mask: u64) { unsafe { msg_send![self, setEdgeAntialiasingMask: mask] } } pub fn set_masks_to_bounds(&self, masks: bool) { unsafe { msg_send![self, setMasksToBounds: masks] } } pub fn remove_all_animations(&self) { unsafe { msg_send![self, removeAllAnimations] } } pub fn next_drawable(&self) -> Option<&MetalDrawableRef> { unsafe { msg_send![self, nextDrawable] } } pub fn contents_scale(&self) -> CGFloat { unsafe { msg_send![self, contentsScale] } } pub fn set_contents_scale(&self, scale: CGFloat) { unsafe { msg_send![self, setContentsScale: scale] } } /// [framebufferOnly Apple Docs](https://developer.apple.com/documentation/metal/mtltexture/1515749-framebufferonly?language=objc) pub fn framebuffer_only(&self) -> bool { unsafe { msg_send_bool!(self, framebufferOnly) } } pub fn set_framebuffer_only(&self, framebuffer_only: bool) { unsafe { msg_send![self, setFramebufferOnly: framebuffer_only] } } pub fn is_opaque(&self) -> bool { unsafe { msg_send_bool!(self, isOpaque) } } pub fn set_opaque(&self, opaque: bool) { unsafe { msg_send![self, setOpaque: opaque] } } pub fn wants_extended_dynamic_range_content(&self) -> bool { unsafe { msg_send_bool![self, wantsExtendedDynamicRangeContent] } } pub fn set_wants_extended_dynamic_range_content( &self, wants_extended_dynamic_range_content: bool, ) { unsafe { msg_send![ self, setWantsExtendedDynamicRangeContent: wants_extended_dynamic_range_content ] } } } mod accelerator_structure; mod argument; mod blitpass; mod buffer; mod capturedescriptor; mod capturemanager; mod commandbuffer; mod commandqueue; mod computepass; mod constants; mod counters; mod depthstencil; mod device; mod drawable; mod encoder; mod heap; mod indirect_encoder; mod library; #[cfg(feature = "mps")] pub mod mps; mod pipeline; mod renderpass; mod resource; mod sampler; mod sync; mod texture; mod types; mod vertexdescriptor; #[rustfmt::skip] pub use { accelerator_structure::*, argument::*, blitpass::*, buffer::*, counters::*, computepass::*, capturedescriptor::*, capturemanager::*, commandbuffer::*, commandqueue::*, constants::*, depthstencil::*, device::*, drawable::*, encoder::*, heap::*, indirect_encoder::*, library::*, pipeline::*, renderpass::*, resource::*, sampler::*, texture::*, types::*, vertexdescriptor::*, sync::*, }; #[inline] unsafe fn obj_drop(p: *mut T) { msg_send![(p as *mut Object), release] } #[inline] unsafe fn obj_clone(p: *mut T) -> *mut T { msg_send![(p as *mut Object), retain] } #[allow(non_camel_case_types)] type c_size_t = usize; // TODO: expand supported interface /// See pub enum NSURL {} foreign_obj_type! { type CType = NSURL; pub struct URL; } impl URL { pub fn new_with_string(string: &str) -> Self { unsafe { let ns_str = crate::nsstring_from_str(string); let class = class!(NSURL); msg_send![class, URLWithString: ns_str] } } } impl URLRef { pub fn absolute_string(&self) -> &str { unsafe { let absolute_string = msg_send![self, absoluteString]; crate::nsstring_as_str(absolute_string) } } pub fn path(&self) -> &str { unsafe { let path = msg_send![self, path]; crate::nsstring_as_str(path) } } }