-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use
objc2
and its framework crates (#15)
This makes the memory management very clear, and uses a type-safe API to access everything.
- Loading branch information
Showing
5 changed files
with
178 additions
and
81 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,57 +1,70 @@ | ||
use crate::{CAMetalLayer, Layer}; | ||
use core::ffi::c_void; | ||
use core_graphics::{base::CGFloat, geometry::CGRect}; | ||
use objc::{ | ||
msg_send, | ||
runtime::{BOOL, YES}, | ||
}; | ||
use objc2::rc::Retained; | ||
use objc2::ClassType; | ||
use objc2_foundation::{NSObject, NSObjectProtocol}; | ||
use objc2_quartz_core::CAMetalLayer; | ||
use raw_window_handle::AppKitWindowHandle; | ||
use std::ptr::NonNull; | ||
|
||
use crate::Layer; | ||
|
||
/// Get or create a new [`Layer`] associated with the given | ||
/// [`AppKitWindowHandle`]. | ||
/// | ||
/// # Safety | ||
/// | ||
/// The handle must be valid. | ||
pub unsafe fn metal_layer_from_handle(handle: AppKitWindowHandle) -> Layer { | ||
metal_layer_from_ns_view(handle.ns_view) | ||
unsafe { metal_layer_from_ns_view(handle.ns_view) } | ||
} | ||
|
||
/// Get or create a new [`Layer`] associated with the given `NSView`. | ||
/// | ||
/// # Safety | ||
/// | ||
/// The view must be a valid instance of `NSView`. | ||
pub unsafe fn metal_layer_from_ns_view(view: NonNull<c_void>) -> Layer { | ||
let view: cocoa::base::id = view.cast().as_ptr(); | ||
// SAFETY: Caller ensures that the view is valid. | ||
let obj = unsafe { view.cast::<NSObject>().as_ref() }; | ||
|
||
// Check if the view is a CAMetalLayer | ||
let class = class!(CAMetalLayer); | ||
let is_actually_layer: BOOL = msg_send![view, isKindOfClass: class]; | ||
if is_actually_layer == YES { | ||
return Layer::Existing(view); | ||
// Check if the view is a `CAMetalLayer`. | ||
if obj.is_kind_of::<CAMetalLayer>() { | ||
// SAFETY: Just checked that the view is a `CAMetalLayer`. | ||
let layer = unsafe { view.cast::<CAMetalLayer>().as_ref() }; | ||
return Layer { | ||
layer: layer.retain(), | ||
pre_existing: true, | ||
}; | ||
} | ||
// Otherwise assume the view is `NSView`. | ||
let view = unsafe { view.cast::<objc2_app_kit::NSView>().as_ref() }; | ||
|
||
// Check if the view contains a valid CAMetalLayer | ||
let existing: CAMetalLayer = msg_send![view, layer]; | ||
let use_current = if existing.is_null() { | ||
false | ||
} else { | ||
let result: BOOL = msg_send![existing, isKindOfClass: class]; | ||
result == YES | ||
}; | ||
|
||
let render_layer = if use_current { | ||
Layer::Existing(existing) | ||
} else { | ||
// Allocate a new CAMetalLayer for the current view | ||
let layer: CAMetalLayer = msg_send![class, new]; | ||
let () = msg_send![view, setLayer: layer]; | ||
let () = msg_send![view, setWantsLayer: YES]; | ||
let bounds: CGRect = msg_send![view, bounds]; | ||
let () = msg_send![layer, setBounds: bounds]; | ||
|
||
let window: cocoa::base::id = msg_send![view, window]; | ||
if !window.is_null() { | ||
let scale_factor: CGFloat = msg_send![window, backingScaleFactor]; | ||
let () = msg_send![layer, setContentsScale: scale_factor]; | ||
// Check if the view contains a valid `CAMetalLayer`. | ||
let existing = unsafe { view.layer() }; | ||
if let Some(existing) = existing { | ||
if existing.is_kind_of::<CAMetalLayer>() { | ||
// SAFETY: Just checked that the layer is a `CAMetalLayer`. | ||
let layer = unsafe { Retained::cast::<CAMetalLayer>(existing) }; | ||
return Layer { | ||
layer, | ||
pre_existing: true, | ||
}; | ||
} | ||
} | ||
|
||
Layer::Allocated(layer) | ||
}; | ||
// If the layer was not `CAMetalLayer`, allocate a new one for the view. | ||
let layer = unsafe { CAMetalLayer::new() }; | ||
unsafe { view.setLayer(Some(&layer)) }; | ||
view.setWantsLayer(true); | ||
layer.setBounds(view.bounds()); | ||
|
||
let _: *mut c_void = msg_send![view, retain]; | ||
render_layer | ||
if let Some(window) = view.window() { | ||
let scale_factor = window.backingScaleFactor(); | ||
layer.setContentsScale(scale_factor); | ||
} | ||
|
||
Layer { | ||
layer, | ||
pre_existing: false, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,55 @@ | ||
#![cfg(any(target_os = "macos", target_os = "ios"))] | ||
#![allow(clippy::missing_safety_doc, clippy::let_unit_value)] | ||
#![cfg(target_vendor = "apple")] | ||
#![allow(clippy::missing_safety_doc)] | ||
#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg_hide), doc(cfg_hide(doc)))] | ||
#![deny(unsafe_op_in_unsafe_fn)] | ||
|
||
#[macro_use] | ||
extern crate objc; | ||
|
||
use objc::runtime::Object; | ||
use objc2::rc::Retained; | ||
use objc2_quartz_core::CAMetalLayer; | ||
use std::ffi::c_void; | ||
|
||
#[cfg(any(target_os = "macos", doc))] | ||
pub mod appkit; | ||
|
||
#[cfg(any(not(target_os = "macos"), doc))] | ||
pub mod uikit; | ||
|
||
pub type CAMetalLayer = *mut Object; | ||
/// A wrapper around [`CAMetalLayer`]. | ||
pub struct Layer { | ||
layer: Retained<CAMetalLayer>, | ||
pre_existing: bool, | ||
} | ||
|
||
impl Layer { | ||
/// Get a pointer to the underlying [`CAMetalLayer`]. The pointer is valid | ||
/// for at least as long as the [`Layer`] is valid, but can be extended by | ||
/// retaining it. | ||
/// | ||
/// | ||
/// # Example | ||
/// | ||
/// ```no_run | ||
/// use objc2::rc::Retained; | ||
/// use objc2_quartz_core::CAMetalLayer; | ||
/// use raw_window_metal::Layer; | ||
/// | ||
/// let layer: Layer; | ||
/// # layer = unimplemented!(); | ||
/// | ||
/// let layer: *mut CAMetalLayer = layer.as_ptr().cast(); | ||
/// // SAFETY: The pointer is a valid `CAMetalLayer`. | ||
/// let layer = unsafe { Retained::retain(layer).unwrap() }; | ||
/// | ||
/// // Use the `CAMetalLayer` here. | ||
/// ``` | ||
#[inline] | ||
pub fn as_ptr(&self) -> *mut c_void { | ||
let ptr: *const CAMetalLayer = Retained::as_ptr(&self.layer); | ||
ptr as *mut _ | ||
} | ||
|
||
pub enum Layer { | ||
Existing(CAMetalLayer), | ||
Allocated(CAMetalLayer), | ||
/// Whether `raw-window-metal` created a new [`CAMetalLayer`] for you. | ||
#[inline] | ||
pub fn pre_existing(&self) -> bool { | ||
self.pre_existing | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters