diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a01535..730387f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Unreleased +- Update `objc2` dep to `0.6`. + # 1.0.0 (2024-09-09) - Bump Rust Edition from 2018 to 2021. diff --git a/Cargo.toml b/Cargo.toml index 3660435..3b00646 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,9 @@ readme = "README.md" keywords = ["window", "metal", "graphics"] categories = ["game-development", "graphics", "os::macos-apis"] exclude = [".github/*"] +# Current, though not specified by policy, see: +# https://github.com/rust-windowing/raw-window-metal/issues/26 +rust-version = "1.71" [features] default = ["std"] @@ -18,8 +21,9 @@ std = ["alloc"] alloc = [] [target.'cfg(target_vendor = "apple")'.dependencies] -objc2 = "0.5.2" -objc2-foundation = { version = "0.2.2", features = [ +objc2 = "0.6.0" +objc2-core-foundation = { version = "0.3.0", features = ["CFCGTypes"] } +objc2-foundation = { version = "0.3.0", features = [ "NSDictionary", "NSGeometry", "NSKeyValueObserving", @@ -28,7 +32,7 @@ objc2-foundation = { version = "0.2.2", features = [ "NSThread", "NSValue", ] } -objc2-quartz-core = { version = "0.2.2", features = [ +objc2-quartz-core = { version = "0.3.0", features = [ "CALayer", "CAMetalLayer", "objc2-metal", @@ -38,10 +42,10 @@ objc2-quartz-core = { version = "0.2.2", features = [ raw-window-handle = "0.6.0" [target.'cfg(target_os = "macos")'.dev-dependencies] -objc2-app-kit = { version = "0.2.2", features = ["NSResponder", "NSView"] } +objc2-app-kit = { version = "0.3.0", features = ["NSResponder", "NSView"] } [target.'cfg(all(target_vendor = "apple", not(target_os = "macos")))'.dev-dependencies] -objc2-ui-kit = { version = "0.2.2", features = ["UIResponder", "UIView"] } +objc2-ui-kit = { version = "0.3.0", features = ["UIResponder", "UIView"] } [package.metadata.docs.rs] targets = [ diff --git a/src/lib.rs b/src/lib.rs index e53ac34..35e802d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,7 @@ //! use raw_window_handle::{RawWindowHandle, HasWindowHandle}; //! use raw_window_metal::Layer; //! # -//! # let mtm = objc2_foundation::MainThreadMarker::new().expect("doc tests to run on main thread"); +//! # let mtm = objc2::MainThreadMarker::new().expect("doc tests to run on main thread"); //! # //! # #[cfg(target_os = "macos")] //! # let view = unsafe { objc2_app_kit::NSView::new(mtm) }; @@ -136,17 +136,19 @@ mod observer; -use crate::observer::ObserverLayer; -use core::ffi::c_void; +use core::ffi::{c_void, CStr}; use core::hash; use core::panic::{RefUnwindSafe, UnwindSafe}; use core::ptr::NonNull; + +use objc2::rc::Retained; use objc2::runtime::AnyClass; -use objc2::{msg_send, rc::Retained}; -use objc2::{msg_send_id, ClassType}; -use objc2_foundation::{MainThreadMarker, NSObject, NSObjectProtocol}; +use objc2::{msg_send, ClassType, MainThreadMarker, Message}; +use objc2_foundation::{NSObject, NSObjectProtocol}; use objc2_quartz_core::{CALayer, CAMetalLayer}; +use crate::observer::ObserverLayer; + #[cfg(not(feature = "alloc"))] compile_error!("The `alloc` feature must currently be enabled."); @@ -325,38 +327,32 @@ impl Layer { ); } - // Check if the view's layer is already a `CAMetalLayer`. - if root_layer.is_kind_of::() { - let layer = root_layer.retain(); - // SAFETY: Just checked that the layer is a `CAMetalLayer`. - let layer: Retained = unsafe { Retained::cast(layer) }; + if let Some(layer) = root_layer.downcast_ref::() { Layer { - layer, + layer: layer.retain(), pre_existing: true, } } else { let layer = ObserverLayer::new(root_layer); Layer { - layer: Retained::into_super(layer), + layer: layer.into_super(), pre_existing: false, } } } fn from_retained_layer(root_layer: Retained) -> Self { - // Check if the view's layer is already a `CAMetalLayer`. - if root_layer.is_kind_of::() { - // SAFETY: Just checked that the layer is a `CAMetalLayer`. - let layer: Retained = unsafe { Retained::cast(root_layer) }; - Layer { + match root_layer.downcast::() { + Ok(layer) => Layer { layer, pre_existing: true, - } - } else { - let layer = ObserverLayer::new(&root_layer); - Layer { - layer: Retained::into_super(layer), - pre_existing: false, + }, + Err(root_layer) => { + let layer = ObserverLayer::new(&root_layer); + Layer { + layer: layer.into_super(), + pre_existing: false, + } } } } @@ -391,7 +387,7 @@ impl Layer { /// use raw_window_metal::Layer; /// /// let handle: AppKitWindowHandle; - /// # let mtm = objc2_foundation::MainThreadMarker::new().expect("doc tests to run on main thread"); + /// # let mtm = objc2::MainThreadMarker::new().expect("doc tests to run on main thread"); /// # #[cfg(target_os = "macos")] /// # let view = unsafe { objc2_app_kit::NSView::new(mtm) }; /// # #[cfg(target_os = "macos")] @@ -415,7 +411,7 @@ impl Layer { if cfg!(debug_assertions) { // Load the class at runtime (instead of using `class!`) // to ensure that this still works if AppKit isn't linked. - let cls = AnyClass::get("NSView").unwrap(); + let cls = AnyClass::get(CStr::from_bytes_with_nul(b"NSView\0").unwrap()).unwrap(); assert!(ns_view.isKindOfClass(cls), "view was not a valid NSView"); } @@ -424,7 +420,7 @@ impl Layer { let _: () = unsafe { msg_send![ns_view, setWantsLayer: true] }; // SAFETY: `-[NSView layer]` returns an optional `CALayer` - let root_layer: Option> = unsafe { msg_send_id![ns_view, layer] }; + let root_layer: Option> = unsafe { msg_send![ns_view, layer] }; let root_layer = root_layer.expect("failed making the view layer-backed"); Self::from_retained_layer(root_layer) @@ -475,12 +471,12 @@ impl Layer { if cfg!(debug_assertions) { // Load the class at runtime (instead of using `class!`) // to ensure that this still works if UIKit isn't linked. - let cls = AnyClass::get("UIView").unwrap(); + let cls = AnyClass::get(CStr::from_bytes_with_nul(b"UIView\0").unwrap()).unwrap(); assert!(ui_view.isKindOfClass(cls), "view was not a valid UIView"); } // SAFETY: `-[UIView layer]` returns a non-optional `CALayer` - let root_layer: Retained = unsafe { msg_send_id![ui_view, layer] }; + let root_layer: Retained = unsafe { msg_send![ui_view, layer] }; // Unlike on macOS, we cannot replace the main view as `UIView` does // not allow it (when `NSView` does). diff --git a/src/observer.rs b/src/observer.rs index 8b1abc1..6ed7cf6 100644 --- a/src/observer.rs +++ b/src/observer.rs @@ -1,7 +1,7 @@ use core::ffi::c_void; use objc2::rc::{Retained, Weak}; use objc2::runtime::{AnyClass, AnyObject}; -use objc2::{declare_class, msg_send, msg_send_id, mutability, ClassType, DeclaredClass}; +use objc2::{define_class, msg_send, AllocAnyThread, ClassType, DefinedClass}; use objc2_foundation::{ ns_string, NSDictionary, NSKeyValueChangeKey, NSKeyValueChangeNewKey, NSKeyValueObservingOptions, NSNumber, NSObjectNSKeyValueObserverRegistration, NSString, @@ -9,7 +9,7 @@ use objc2_foundation::{ }; use objc2_quartz_core::{CALayer, CAMetalLayer}; -declare_class!( +define_class!( /// A `CAMetalLayer` layer that will automatically update its bounds and scale factor to match /// its super layer. /// @@ -18,29 +18,22 @@ declare_class!( /// /// See the documentation on Key-Value Observing for details on how this works in general: /// - pub(crate) struct ObserverLayer; - + // // SAFETY: // - The superclass CAMetalLayer does not have any subclassing requirements. - // - Interior mutability is a safe default. // - CustomLayer implements `Drop` and ensures that: // - It does not call an overridden method. // - It does not `retain` itself. - unsafe impl ClassType for ObserverLayer { - type Super = CAMetalLayer; - type Mutability = mutability::InteriorMutable; - const NAME: &'static str = "RawWindowMetalLayer"; - } - - impl DeclaredClass for ObserverLayer { - type Ivars = Weak; - } + #[unsafe(super(CAMetalLayer))] + #[name = "RawWindowMetalLayer"] + #[ivars = Weak] + pub(crate) struct ObserverLayer; // `NSKeyValueObserving` category. // // SAFETY: The method is correctly defined. - unsafe impl ObserverLayer { - #[method(observeValueForKeyPath:ofObject:change:context:)] + impl ObserverLayer { + #[unsafe(method(observeValueForKeyPath:ofObject:change:context:))] fn _observe_value( &self, key_path: Option<&NSString>, @@ -82,7 +75,7 @@ impl ObserverLayer { pub fn new(root_layer: &CALayer) -> Retained { let this = Self::alloc().set_ivars(Weak::new(root_layer)); // SAFETY: Initializing `CAMetalLayer` is safe. - let this: Retained = unsafe { msg_send_id![super(this), init] }; + let this: Retained = unsafe { msg_send![super(this), init] }; // Add the layer as a sublayer of the root layer. root_layer.addSublayer(&this); @@ -115,15 +108,13 @@ impl ObserverLayer { root_layer.addObserver_forKeyPath_options_context( &this, ns_string!("contentsScale"), - NSKeyValueObservingOptions::NSKeyValueObservingOptionNew - | NSKeyValueObservingOptions::NSKeyValueObservingOptionInitial, + NSKeyValueObservingOptions::New | NSKeyValueObservingOptions::Initial, ObserverLayer::context(), ); root_layer.addObserver_forKeyPath_options_context( &this, ns_string!("bounds"), - NSKeyValueObservingOptions::NSKeyValueObservingOptionNew - | NSKeyValueObservingOptions::NSKeyValueObservingOptionInitial, + NSKeyValueObservingOptions::New | NSKeyValueObservingOptions::Initial, ObserverLayer::context(), ); } @@ -167,7 +158,7 @@ impl ObserverLayer { // SAFETY: The static is declared with the correct type in `objc2`. let key = unsafe { NSKeyValueChangeNewKey }; let new = change - .get(key) + .objectForKey(key) .expect("requested change dictionary did not contain `NSKeyValueChangeNewKey`"); // NOTE: Setting these values usually causes a quarter second animation to occur, which is @@ -177,16 +168,16 @@ impl ObserverLayer { // ongoing, and as such we don't need to wrap this in a `CATransaction` ourselves. if key_path == Some(ns_string!("contentsScale")) { - // SAFETY: `contentsScale` is a CGFloat, and so the observed value is always a NSNumber. - let new = unsafe { &*(new as *const AnyObject as *const NSNumber) }; + // `contentsScale` is a CGFloat, and so the observed value is always a NSNumber. + let new = new.downcast::().unwrap(); let scale_factor = new.as_cgfloat(); // Set the scale factor of the layer to match the root layer when it changes (e.g. if // moved to a different monitor, or monitor settings changed). self.setContentsScale(scale_factor); } else if key_path == Some(ns_string!("bounds")) { - // SAFETY: `bounds` is a CGRect, and so the observed value is always a NSValue. - let new = unsafe { &*(new as *const AnyObject as *const NSValue) }; + // `bounds` is a CGRect, and so the observed value is always a NSNumber. + let new = new.downcast::().unwrap(); let bounds = new.get_rect().expect("new bounds value was not CGRect"); // Set `bounds` and `position` so that the new layer is inside the superlayer. @@ -202,7 +193,7 @@ impl ObserverLayer { #[cfg(test)] mod tests { - use objc2_foundation::{CGPoint, CGRect, CGSize}; + use objc2_core_foundation::{CGPoint, CGRect, CGSize}; use super::*;