-
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.
This makes the memory management very clear, and uses a type-safe API to access everything.
- Loading branch information
Showing
5 changed files
with
161 additions
and
82 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,61 @@ | ||
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; | ||
|
||
/// | ||
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) } | ||
} | ||
|
||
/// | ||
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); | ||
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]; | ||
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