From 24e2c6914af70e87049b1383f45bf10e5465ca61 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sun, 20 Apr 2025 07:36:49 +0200 Subject: [PATCH] macOS:ios: use next objc2 version A lot of CoreFoundation methods have been marked safe, and converted into methods. Note that the old functions are still available, just deprecated. --- Cargo.toml | 22 +++--- src/platform_impl/apple/appkit/event.rs | 4 +- src/platform_impl/apple/appkit/ffi.rs | 62 +-------------- src/platform_impl/apple/appkit/monitor.rs | 79 +++++++------------ src/platform_impl/apple/appkit/observer.rs | 38 ++++----- .../apple/appkit/window_delegate.rs | 37 ++++++--- src/platform_impl/apple/event_loop_proxy.rs | 16 ++-- src/platform_impl/apple/uikit/app_state.rs | 37 +++++---- src/platform_impl/apple/uikit/event_loop.rs | 47 ++++------- 9 files changed, 125 insertions(+), 217 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1e8b7712..8ee6eea3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -104,13 +104,13 @@ ndk = { version = "0.9.0", features = ["rwh_06"], default-features = false } # AppKit or UIKit [target.'cfg(target_vendor = "apple")'.dependencies] -block2 = "0.6.0" -dispatch2 = { version = "0.2.0", default-features = false, features = ["std", "objc2"] } -objc2 = "0.6.0" +block2 = "0.6.1" +dispatch2 = { version = "0.3.0", default-features = false, features = ["std", "objc2"] } +objc2 = "0.6.1" # AppKit [target.'cfg(target_os = "macos")'.dependencies] -objc2-app-kit = { version = "0.3.0", default-features = false, features = [ +objc2-app-kit = { version = "0.3.1", default-features = false, features = [ "std", "objc2-core-foundation", "NSAppearance", @@ -142,7 +142,7 @@ objc2-app-kit = { version = "0.3.0", default-features = false, features = [ "NSWindowScripting", "NSWindowTabGroup", ] } -objc2-core-foundation = { version = "0.3.0", default-features = false, features = [ +objc2-core-foundation = { version = "0.3.1", default-features = false, features = [ "std", "block2", "CFBase", @@ -152,7 +152,7 @@ objc2-core-foundation = { version = "0.3.0", default-features = false, features "CFString", "CFUUID", ] } -objc2-core-graphics = { version = "0.3.0", default-features = false, features = [ +objc2-core-graphics = { version = "0.3.1", default-features = false, features = [ "std", "libc", "CGDirectDisplay", @@ -162,14 +162,14 @@ objc2-core-graphics = { version = "0.3.0", default-features = false, features = "CGRemoteOperation", "CGWindowLevel", ] } -objc2-core-video = { version = "0.3.0", default-features = false, features = [ +objc2-core-video = { version = "0.3.1", default-features = false, features = [ "std", "objc2-core-graphics", "CVBase", "CVReturn", "CVDisplayLink", ] } -objc2-foundation = { version = "0.3.0", default-features = false, features = [ +objc2-foundation = { version = "0.3.1", default-features = false, features = [ "std", "block2", "objc2-core-foundation", @@ -194,14 +194,14 @@ objc2-foundation = { version = "0.3.0", default-features = false, features = [ # UIKit [target.'cfg(all(target_vendor = "apple", not(target_os = "macos")))'.dependencies] -objc2-core-foundation = { version = "0.3.0", default-features = false, features = [ +objc2-core-foundation = { version = "0.3.1", default-features = false, features = [ "std", "CFCGTypes", "CFBase", "CFRunLoop", "CFString", ] } -objc2-foundation = { version = "0.3.0", default-features = false, features = [ +objc2-foundation = { version = "0.3.1", default-features = false, features = [ "std", "block2", "objc2-core-foundation", @@ -214,7 +214,7 @@ objc2-foundation = { version = "0.3.0", default-features = false, features = [ "NSThread", "NSSet", ] } -objc2-ui-kit = { version = "0.3.0", default-features = false, features = [ +objc2-ui-kit = { version = "0.3.1", default-features = false, features = [ "std", "objc2-core-foundation", "UIApplication", diff --git a/src/platform_impl/apple/appkit/event.rs b/src/platform_impl/apple/appkit/event.rs index acde964a..3317fa94 100644 --- a/src/platform_impl/apple/appkit/event.rs +++ b/src/platform_impl/apple/appkit/event.rs @@ -3,7 +3,7 @@ use std::ptr::NonNull; use dispatch2::run_on_main; use objc2::rc::Retained; use objc2_app_kit::{NSEvent, NSEventModifierFlags, NSEventSubtype, NSEventType}; -use objc2_core_foundation::{CFData, CFDataGetBytePtr, CFRetained}; +use objc2_core_foundation::{CFData, CFRetained}; use objc2_foundation::NSPoint; use smol_str::SmolStr; @@ -30,7 +30,7 @@ pub fn get_modifierless_char(scancode: u16) -> Key { return Key::Unidentified(NativeKey::MacOS(scancode)); }; - let layout = unsafe { CFDataGetBytePtr(layout_data).cast() }; + let layout = layout_data.byte_ptr().cast(); let keyboard_type = run_on_main(|_mtm| unsafe { ffi::LMGetKbdType() }); let mut result_len = 0; diff --git a/src/platform_impl/apple/appkit/ffi.rs b/src/platform_impl/apple/appkit/ffi.rs index 21241c60..b35fc84c 100644 --- a/src/platform_impl/apple/appkit/ffi.rs +++ b/src/platform_impl/apple/appkit/ffi.rs @@ -1,6 +1,6 @@ // TODO: Upstream these -#![allow(dead_code, non_snake_case, non_upper_case_globals)] +#![allow(non_upper_case_globals)] use std::ffi::c_void; @@ -9,27 +9,10 @@ use objc2::runtime::AnyObject; use objc2_core_foundation::{cf_type, CFString, CFUUID}; use objc2_core_graphics::CGDirectDisplayID; -pub const kCGDisplayBlendNormal: f32 = 0.0; -pub const kCGDisplayBlendSolidColor: f32 = 1.0; - -pub type CGDisplayFadeReservationToken = u32; -pub const kCGDisplayFadeReservationInvalidToken: CGDisplayFadeReservationToken = 0; - -pub const IO1BitIndexedPixels: &str = "P"; -pub const IO2BitIndexedPixels: &str = "PP"; -pub const IO4BitIndexedPixels: &str = "PPPP"; -pub const IO8BitIndexedPixels: &str = "PPPPPPPP"; pub const IO16BitDirectPixels: &str = "-RRRRRGGGGGBBBBB"; pub const IO32BitDirectPixels: &str = "--------RRRRRRRRGGGGGGGGBBBBBBBB"; pub const kIO30BitDirectPixels: &str = "--RRRRRRRRRRGGGGGGGGGGBBBBBBBBBB"; -pub const kIO64BitDirectPixels: &str = "-16R16G16B16"; - -pub const kIO16BitFloatPixels: &str = "-16FR16FG16FB16"; -pub const kIO32BitFloatPixels: &str = "-32FR32FG32FB32"; - -pub const IOYUV422Pixels: &str = "Y4U2V2"; -pub const IO8BitOverlayPixels: &str = "O8"; // `CGDisplayCreateUUIDFromDisplayID` comes from the `ColorSync` framework. // However, that framework was only introduced "publicly" in macOS 10.13. @@ -58,7 +41,6 @@ extern "C" { pub struct TISInputSource(std::ffi::c_void); cf_type!( - #[encoding_name = "__TISInputSource"] unsafe impl TISInputSource {} ); @@ -103,45 +85,3 @@ extern "C" { unicodeString: *mut UniChar, ) -> OSStatus; } - -// CGWindowLevel.h -// -// Note: There are two different things at play in this header: -// `CGWindowLevel` and `CGWindowLevelKey`. -// -// It seems like there was a push towards using "key" values instead of the -// raw window level values, and then you were supposed to use -// `CGWindowLevelForKey` to get the actual level. -// -// But the values that `NSWindowLevel` has are compiled in, and as such has -// to remain ABI compatible, so they're safe for us to hardcode as well. -#[allow(dead_code, non_upper_case_globals)] -mod window_level { - const kCGNumReservedWindowLevels: i32 = 16; - const kCGNumReservedBaseWindowLevels: i32 = 5; - - pub const kCGBaseWindowLevel: i32 = i32::MIN; - pub const kCGMinimumWindowLevel: i32 = kCGBaseWindowLevel + kCGNumReservedBaseWindowLevels; - pub const kCGMaximumWindowLevel: i32 = i32::MAX - kCGNumReservedWindowLevels; - - pub const kCGDesktopWindowLevel: i32 = kCGMinimumWindowLevel + 20; - pub const kCGDesktopIconWindowLevel: i32 = kCGDesktopWindowLevel + 20; - pub const kCGBackstopMenuLevel: i32 = -20; - pub const kCGNormalWindowLevel: i32 = 0; - pub const kCGFloatingWindowLevel: i32 = 3; - pub const kCGTornOffMenuWindowLevel: i32 = 3; - pub const kCGModalPanelWindowLevel: i32 = 8; - pub const kCGUtilityWindowLevel: i32 = 19; - pub const kCGDockWindowLevel: i32 = 20; - pub const kCGMainMenuWindowLevel: i32 = 24; - pub const kCGStatusWindowLevel: i32 = 25; - pub const kCGPopUpMenuWindowLevel: i32 = 101; - pub const kCGOverlayWindowLevel: i32 = 102; - pub const kCGHelpWindowLevel: i32 = 200; - pub const kCGDraggingWindowLevel: i32 = 500; - pub const kCGScreenSaverWindowLevel: i32 = 1000; - pub const kCGAssistiveTechHighWindowLevel: i32 = 1500; - pub const kCGCursorWindowLevel: i32 = kCGMaximumWindowLevel - 1; -} - -pub use window_level::*; diff --git a/src/platform_impl/apple/appkit/monitor.rs b/src/platform_impl/apple/appkit/monitor.rs index 6d65bc5e..de33e62a 100644 --- a/src/platform_impl/apple/appkit/monitor.rs +++ b/src/platform_impl/apple/appkit/monitor.rs @@ -9,21 +9,12 @@ use dispatch2::run_on_main; use objc2::rc::Retained; use objc2::MainThreadMarker; use objc2_app_kit::NSScreen; -use objc2_core_foundation::{ - CFArrayGetCount, CFArrayGetValueAtIndex, CFRetained, CFUUIDGetUUIDBytes, -}; -#[allow(deprecated)] +use objc2_core_foundation::{CFArray, CFRetained}; use objc2_core_graphics::{ CGDirectDisplayID, CGDisplayBounds, CGDisplayCopyAllDisplayModes, CGDisplayCopyDisplayMode, - CGDisplayMode, CGDisplayModeCopyPixelEncoding, CGDisplayModeGetPixelHeight, - CGDisplayModeGetPixelWidth, CGDisplayModeGetRefreshRate, CGDisplayModelNumber, - CGGetActiveDisplayList, CGMainDisplayID, -}; -#[allow(deprecated)] -use objc2_core_video::{ - kCVReturnSuccess, CVDisplayLinkCreateWithCGDisplay, - CVDisplayLinkGetNominalOutputVideoRefreshPeriod, CVTimeFlags, + CGDisplayMode, CGDisplayModelNumber, CGGetActiveDisplayList, CGMainDisplayID, }; +use objc2_core_video::{kCVReturnSuccess, CVDisplayLink, CVTimeFlags}; use objc2_foundation::{ns_string, NSNumber, NSPoint, NSRect}; use super::ffi; @@ -76,7 +67,7 @@ impl VideoModeHandle { unsafe { #[allow(deprecated)] let pixel_encoding = - CGDisplayModeCopyPixelEncoding(Some(&native_mode.0)).unwrap().to_string(); + CGDisplayMode::pixel_encoding(Some(&native_mode.0)).unwrap().to_string(); let bit_depth = if pixel_encoding.eq_ignore_ascii_case(ffi::IO32BitDirectPixels) { 32 } else if pixel_encoding.eq_ignore_ascii_case(ffi::IO16BitDirectPixels) { @@ -89,8 +80,8 @@ impl VideoModeHandle { let mode = VideoMode { size: PhysicalSize::new( - CGDisplayModeGetPixelWidth(Some(&native_mode.0)) as u32, - CGDisplayModeGetPixelHeight(Some(&native_mode.0)) as u32, + CGDisplayMode::pixel_width(Some(&native_mode.0)) as u32, + CGDisplayMode::pixel_height(Some(&native_mode.0)) as u32, ), refresh_rate_millihertz, bit_depth: NonZeroU16::new(bit_depth), @@ -107,9 +98,12 @@ pub struct MonitorHandle(CGDirectDisplayID); impl MonitorHandle { /// Internal comparisons of [`MonitorHandle`]s are done first requesting a UUID for the handle. fn uuid(&self) -> u128 { + // SAFETY: Valid to call. let ptr = unsafe { ffi::CGDisplayCreateUUIDFromDisplayID(self.0) }; + // SAFETY: `CGDisplayCreateUUIDFromDisplayID` is a "create" function, so the pointer has + // +1 retain count. let cf_uuid = unsafe { CFRetained::from_raw(NonNull::new(ptr).unwrap()) }; - u128::from_ne_bytes(unsafe { CFUUIDGetUUIDBytes(&cf_uuid) }.into()) + u128::from_ne_bytes(cf_uuid.uuid_bytes().into()) } pub fn new(id: CGDirectDisplayID) -> Self { @@ -126,38 +120,25 @@ impl MonitorHandle { let refresh_rate_millihertz = self.refresh_rate_millihertz(); let monitor = self.clone(); - unsafe { - let modes = { - let array = CGDisplayCopyAllDisplayModes(self.0, None) - .expect("failed to get list of display modes"); - let array_count = CFArrayGetCount(&array); - let modes: Vec<_> = (0..array_count) - .map(move |i| { - let mode = CFArrayGetValueAtIndex(&array, i) as *mut CGDisplayMode; - CFRetained::retain(NonNull::new(mode).unwrap()) - }) - .collect(); - modes + let array = unsafe { CGDisplayCopyAllDisplayModes(self.0, None) } + .expect("failed to get list of display modes"); + // SAFETY: `CGDisplayCopyAllDisplayModes` is documented to return an array of display modes. + let modes = unsafe { CFRetained::cast_unchecked::>(array) }; + + modes.into_iter().map(move |mode| { + let cg_refresh_rate_hertz = + unsafe { CGDisplayMode::refresh_rate(Some(&mode)) }.round() as i64; + + // CGDisplayModeGetRefreshRate returns 0.0 for any display that + // isn't a CRT + let refresh_rate_millihertz = if cg_refresh_rate_hertz > 0 { + NonZeroU32::new((cg_refresh_rate_hertz * 1000) as u32) + } else { + refresh_rate_millihertz }; - modes.into_iter().map(move |mode| { - let cg_refresh_rate_hertz = CGDisplayModeGetRefreshRate(Some(&mode)).round() as i64; - - // CGDisplayModeGetRefreshRate returns 0.0 for any display that - // isn't a CRT - let refresh_rate_millihertz = if cg_refresh_rate_hertz > 0 { - NonZeroU32::new((cg_refresh_rate_hertz * 1000) as u32) - } else { - refresh_rate_millihertz - }; - - VideoModeHandle::new( - monitor.clone(), - NativeDisplayMode(mode), - refresh_rate_millihertz, - ) - }) - } + VideoModeHandle::new(monitor.clone(), NativeDisplayMode(mode), refresh_rate_millihertz) + }) } pub(crate) fn ns_screen(&self, mtm: MainThreadMarker) -> Option> { @@ -329,21 +310,21 @@ pub(crate) fn flip_window_screen_coordinates(frame: NSRect) -> NSPoint { fn refresh_rate_millihertz(id: CGDirectDisplayID, mode: &NativeDisplayMode) -> Option { unsafe { - let refresh_rate = CGDisplayModeGetRefreshRate(Some(&mode.0)); + let refresh_rate = CGDisplayMode::refresh_rate(Some(&mode.0)); if refresh_rate > 0.0 { return NonZeroU32::new((refresh_rate * 1000.0).round() as u32); } let mut display_link = std::ptr::null_mut(); #[allow(deprecated)] - if CVDisplayLinkCreateWithCGDisplay(id, NonNull::from(&mut display_link)) + if CVDisplayLink::create_with_cg_display(id, NonNull::from(&mut display_link)) != kCVReturnSuccess { return None; } let display_link = CFRetained::from_raw(NonNull::new(display_link).unwrap()); #[allow(deprecated)] - let time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(&display_link); + let time = display_link.nominal_output_video_refresh_period(); // This value is indefinite if an invalid display link was specified if time.flags & CVTimeFlags::IsIndefinite.0 != 0 { diff --git a/src/platform_impl/apple/appkit/observer.rs b/src/platform_impl/apple/appkit/observer.rs index f5e40c1c..ae836208 100644 --- a/src/platform_impl/apple/appkit/observer.rs +++ b/src/platform_impl/apple/appkit/observer.rs @@ -12,10 +12,8 @@ use std::time::Instant; use objc2::MainThreadMarker; use objc2_core_foundation::{ kCFRunLoopCommonModes, kCFRunLoopDefaultMode, CFAbsoluteTimeGetCurrent, CFIndex, CFRetained, - CFRunLoop, CFRunLoopActivity, CFRunLoopAddObserver, CFRunLoopAddTimer, CFRunLoopGetMain, - CFRunLoopObserver, CFRunLoopObserverCallBack, CFRunLoopObserverContext, - CFRunLoopObserverCreate, CFRunLoopPerformBlock, CFRunLoopTimer, CFRunLoopTimerCreate, - CFRunLoopTimerInvalidate, CFRunLoopTimerSetNextFireDate, CFRunLoopWakeUp, + CFRunLoop, CFRunLoopActivity, CFRunLoopObserver, CFRunLoopObserverCallBack, + CFRunLoopObserverContext, CFRunLoopTimer, }; use tracing::error; @@ -95,11 +93,11 @@ impl RunLoop { // SAFETY: We have a MainThreadMarker here, which means we know we're on the main thread, so // scheduling (and scheduling a non-`Send` block) to that thread is allowed. let _ = mtm; - RunLoop(unsafe { CFRunLoopGetMain() }.unwrap()) + RunLoop(CFRunLoop::main().unwrap()) } pub fn wakeup(&self) { - unsafe { CFRunLoopWakeUp(&self.0) } + self.0.wake_up(); } unsafe fn add_observer( @@ -111,9 +109,9 @@ impl RunLoop { context: *mut CFRunLoopObserverContext, ) { let observer = - unsafe { CFRunLoopObserverCreate(None, flags.0, true, priority, handler, context) } + unsafe { CFRunLoopObserver::new(None, flags.0, true, priority, handler, context) } .unwrap(); - unsafe { CFRunLoopAddObserver(&self.0, Some(&observer), kCFRunLoopCommonModes) }; + self.0.add_observer(Some(&observer), unsafe { kCFRunLoopCommonModes }); } /// Submit a closure to run on the main thread as the next step in the run loop, before other @@ -178,7 +176,7 @@ impl RunLoop { let mode = unsafe { kCFRunLoopDefaultMode.unwrap() }; // SAFETY: The runloop is valid, the mode is a `CFStringRef`, and the block is `'static`. - unsafe { CFRunLoopPerformBlock(&self.0, Some(mode), Some(&block)) } + unsafe { self.0.perform_block(Some(mode), Some(&block)) } } } @@ -224,7 +222,7 @@ pub struct EventLoopWaker { impl Drop for EventLoopWaker { fn drop(&mut self) { - unsafe { CFRunLoopTimerInvalidate(&self.timer) }; + self.timer.invalidate(); } } @@ -235,7 +233,7 @@ impl EventLoopWaker { // Create a timer with a 0.1µs interval (1ns does not work) to mimic polling. // It is initially setup with a first fire time really far into the // future, but that gets changed to fire immediately in did_finish_launching - let timer = CFRunLoopTimerCreate( + let timer = CFRunLoopTimer::new( None, f64::MAX, 0.000_000_1, @@ -245,7 +243,7 @@ impl EventLoopWaker { ptr::null_mut(), ) .unwrap(); - CFRunLoopAddTimer(&CFRunLoopGetMain().unwrap(), Some(&timer), kCFRunLoopCommonModes); + CFRunLoop::main().unwrap().add_timer(Some(&timer), kCFRunLoopCommonModes); Self { timer, start_instant: Instant::now(), next_fire_date: None } } } @@ -253,14 +251,14 @@ impl EventLoopWaker { pub fn stop(&mut self) { if self.next_fire_date.is_some() { self.next_fire_date = None; - unsafe { CFRunLoopTimerSetNextFireDate(&self.timer, f64::MAX) }; + self.timer.set_next_fire_date(f64::MAX); } } pub fn start(&mut self) { if self.next_fire_date != Some(self.start_instant) { self.next_fire_date = Some(self.start_instant); - unsafe { CFRunLoopTimerSetNextFireDate(&self.timer, f64::MIN) }; + self.timer.set_next_fire_date(f64::MIN); } } @@ -273,13 +271,11 @@ impl EventLoopWaker { Some(instant) => { if self.next_fire_date != Some(instant) { self.next_fire_date = Some(instant); - unsafe { - let current = CFAbsoluteTimeGetCurrent(); - let duration = instant - now; - let fsecs = duration.subsec_nanos() as f64 / 1_000_000_000.0 - + duration.as_secs() as f64; - CFRunLoopTimerSetNextFireDate(&self.timer, current + fsecs); - } + let current = CFAbsoluteTimeGetCurrent(); + let duration = instant - now; + let fsecs = duration.subsec_nanos() as f64 / 1_000_000_000.0 + + duration.as_secs() as f64; + self.timer.set_next_fire_date(current + fsecs); } }, None => { diff --git a/src/platform_impl/apple/appkit/window_delegate.rs b/src/platform_impl/apple/appkit/window_delegate.rs index 604c6834..14b65191 100644 --- a/src/platform_impl/apple/appkit/window_delegate.rs +++ b/src/platform_impl/apple/appkit/window_delegate.rs @@ -23,8 +23,10 @@ use objc2_app_kit::{ }; use objc2_core_foundation::{CGFloat, CGPoint}; use objc2_core_graphics::{ - CGAcquireDisplayFadeReservation, CGAssociateMouseAndMouseCursorPosition, CGDisplayCapture, - CGDisplayFade, CGDisplayRelease, CGDisplaySetDisplayMode, CGReleaseDisplayFadeReservation, + kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, kCGDisplayFadeReservationInvalidToken, + kCGFloatingWindowLevel, kCGNormalWindowLevel, CGAcquireDisplayFadeReservation, + CGAssociateMouseAndMouseCursorPosition, CGDisplayCapture, CGDisplayFade, CGDisplayRelease, + CGDisplaySetDisplayMode, CGReleaseDisplayFadeReservation, CGRestorePermanentDisplayConfiguration, CGShieldingWindowLevel, CGWarpMouseCursorPosition, }; use objc2_foundation::{ @@ -1500,7 +1502,7 @@ impl WindowDelegate { let display_id = monitor.native_id() as _; - let mut fade_token = ffi::kCGDisplayFadeReservationInvalidToken; + let mut fade_token = kCGDisplayFadeReservationInvalidToken; if matches!(old_fullscreen, Some(Fullscreen::Borderless(_))) { self.ivars().save_presentation_opts.replace(Some(app.presentationOptions())); @@ -1513,8 +1515,8 @@ impl WindowDelegate { CGDisplayFade( fade_token, 0.3, - ffi::kCGDisplayBlendNormal, - ffi::kCGDisplayBlendSolidColor, + kCGDisplayBlendNormal, + kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, @@ -1538,12 +1540,12 @@ impl WindowDelegate { // After the display has been configured, fade back in // asynchronously - if fade_token != ffi::kCGDisplayFadeReservationInvalidToken { + if fade_token != kCGDisplayFadeReservationInvalidToken { CGDisplayFade( fade_token, 0.6, - ffi::kCGDisplayBlendSolidColor, - ffi::kCGDisplayBlendNormal, + kCGDisplayBlendSolidColor, + kCGDisplayBlendNormal, 0.0, 0.0, 0.0, @@ -1560,7 +1562,7 @@ impl WindowDelegate { // Window level must be restored from `CGShieldingWindowLevel() // + 1` back to normal in order for `toggleFullScreen` to do // anything - window.setLevel(ffi::kCGNormalWindowLevel as NSWindowLevel); + window.setLevel(kCGNormalWindowLevel as NSWindowLevel); window.toggleFullScreen(None); } @@ -1629,7 +1631,7 @@ impl WindowDelegate { // Restore the normal window level following the Borderless fullscreen // `CGShieldingWindowLevel() + 1` hack. - self.window().setLevel(ffi::kCGNormalWindowLevel as NSWindowLevel); + self.window().setLevel(kCGNormalWindowLevel as NSWindowLevel); }, _ => {}, }; @@ -1676,10 +1678,19 @@ impl WindowDelegate { #[inline] pub fn set_window_level(&self, level: WindowLevel) { + // Note: There are two different things at play here: + // `CGWindowLevel` and `CGWindowLevelKey`. + // + // It seems like there was a push towards using "key" values instead of the + // raw window level values, and then you were supposed to use + // `CGWindowLevelForKey` to get the actual level. + // + // But the values that `NSWindowLevel` has are compiled in, and as such has + // to remain ABI compatible, so they're safe for us to hardcode as well. let level = match level { - WindowLevel::AlwaysOnTop => ffi::kCGFloatingWindowLevel as NSWindowLevel, - WindowLevel::AlwaysOnBottom => (ffi::kCGNormalWindowLevel - 1) as NSWindowLevel, - WindowLevel::Normal => ffi::kCGNormalWindowLevel as NSWindowLevel, + WindowLevel::AlwaysOnTop => kCGFloatingWindowLevel as NSWindowLevel, + WindowLevel::AlwaysOnBottom => (kCGNormalWindowLevel - 1) as NSWindowLevel, + WindowLevel::Normal => kCGNormalWindowLevel as NSWindowLevel, }; self.window().setLevel(level); } diff --git a/src/platform_impl/apple/event_loop_proxy.rs b/src/platform_impl/apple/event_loop_proxy.rs index 34e06357..409cb6ee 100644 --- a/src/platform_impl/apple/event_loop_proxy.rs +++ b/src/platform_impl/apple/event_loop_proxy.rs @@ -3,9 +3,7 @@ use std::sync::Arc; use objc2::MainThreadMarker; use objc2_core_foundation::{ - kCFRunLoopCommonModes, CFIndex, CFRetained, CFRunLoop, CFRunLoopAddSource, CFRunLoopGetMain, - CFRunLoopSource, CFRunLoopSourceContext, CFRunLoopSourceCreate, CFRunLoopSourceInvalidate, - CFRunLoopSourceSignal, CFRunLoopWakeUp, + kCFRunLoopCommonModes, CFIndex, CFRetained, CFRunLoop, CFRunLoopSource, CFRunLoopSourceContext, }; use crate::event_loop::EventLoopProxyProvider; @@ -90,12 +88,12 @@ impl EventLoopProxy { // Keeping the closure alive beyond this scope is fine, because `F: 'static`. let source = unsafe { let _ = mtm; - CFRunLoopSourceCreate(None, order, &mut context).unwrap() + CFRunLoopSource::new(None, order, &mut context).unwrap() }; // Register the source to be performed on the main thread. - let main_loop = unsafe { CFRunLoopGetMain() }.unwrap(); - unsafe { CFRunLoopAddSource(&main_loop, Some(&source), kCFRunLoopCommonModes) }; + let main_loop = CFRunLoop::main().unwrap(); + unsafe { main_loop.add_source(Some(&source), kCFRunLoopCommonModes) }; Self { source, main_loop } } @@ -106,7 +104,7 @@ impl EventLoopProxy { pub(crate) fn invalidate(&self) { // NOTE: We do NOT fire this on `Drop`, since we want the proxy to be cloneable, such that // we only need to register a single source even if there's multiple proxies in use. - unsafe { CFRunLoopSourceInvalidate(&self.source) }; + self.source.invalidate(); } } @@ -115,12 +113,12 @@ impl EventLoopProxyProvider for EventLoopProxy { // Signal the source, which ends up later invoking `perform` on the main thread. // // Multiple signals in quick succession are automatically coalesced into a single signal. - unsafe { CFRunLoopSourceSignal(&self.source) }; + self.source.signal(); // Let the main thread know there's a new event. // // This is required since we may be (probably are) running on a different thread, and the // main loop may be sleeping (and `CFRunLoopSourceSignal` won't wake it). - unsafe { CFRunLoopWakeUp(&self.main_loop) }; + self.main_loop.wake_up(); } } diff --git a/src/platform_impl/apple/uikit/app_state.rs b/src/platform_impl/apple/uikit/app_state.rs index efdc7c5b..840881f4 100644 --- a/src/platform_impl/apple/uikit/app_state.rs +++ b/src/platform_impl/apple/uikit/app_state.rs @@ -11,9 +11,8 @@ use dispatch2::MainThreadBound; use objc2::rc::Retained; use objc2::MainThreadMarker; use objc2_core_foundation::{ - kCFRunLoopCommonModes, CFAbsoluteTimeGetCurrent, CFRetained, CFRunLoop, CFRunLoopAddTimer, - CFRunLoopGetMain, CFRunLoopTimer, CFRunLoopTimerCreate, CFRunLoopTimerInvalidate, - CFRunLoopTimerSetNextFireDate, CGRect, CGSize, + kCFRunLoopCommonModes, CFAbsoluteTimeGetCurrent, CFRetained, CFRunLoop, CFRunLoopTimer, CGRect, + CGSize, }; use objc2_ui_kit::{UIApplication, UICoordinateSpace, UIView}; @@ -115,7 +114,7 @@ impl AppState { #[inline(never)] #[cold] fn init_guard(guard: &mut RefMut<'static, Option>, mtm: MainThreadMarker) { - let waker = EventLoopWaker::new(unsafe { CFRunLoopGetMain().unwrap() }); + let waker = EventLoopWaker::new(CFRunLoop::main().unwrap()); let event_loop_proxy = Arc::new(EventLoopProxy::new(mtm, move || { get_handler(mtm).handle(|app| app.proxy_wake_up(&ActiveEventLoop { mtm })); })); @@ -305,7 +304,11 @@ pub(crate) fn queue_gl_or_metal_redraw(mtm: MainThreadMarker, window: Retained( + mtm: MainThreadMarker, + app: impl ApplicationHandler, + run: impl FnOnce() -> R, +) -> R { get_handler(mtm).set(Box::new(app), run) } @@ -544,9 +547,7 @@ struct EventLoopWaker { impl Drop for EventLoopWaker { fn drop(&mut self) { - unsafe { - CFRunLoopTimerInvalidate(&self.timer); - } + self.timer.invalidate(); } } @@ -557,7 +558,7 @@ impl EventLoopWaker { // Create a timer with a 0.1µs interval (1ns does not work) to mimic polling. // It is initially setup with a first fire time really far into the // future, but that gets changed to fire immediately in did_finish_launching - let timer = CFRunLoopTimerCreate( + let timer = CFRunLoopTimer::new( None, f64::MAX, 0.000_000_1, @@ -567,18 +568,18 @@ impl EventLoopWaker { ptr::null_mut(), ) .unwrap(); - CFRunLoopAddTimer(&rl, Some(&timer), kCFRunLoopCommonModes); + rl.add_timer(Some(&timer), kCFRunLoopCommonModes); EventLoopWaker { timer } } } fn stop(&mut self) { - unsafe { CFRunLoopTimerSetNextFireDate(&self.timer, f64::MAX) } + self.timer.set_next_fire_date(f64::MAX); } fn start(&mut self) { - unsafe { CFRunLoopTimerSetNextFireDate(&self.timer, f64::MIN) } + self.timer.set_next_fire_date(f64::MIN); } fn start_at(&mut self, instant: Instant) { @@ -586,13 +587,11 @@ impl EventLoopWaker { if now >= instant { self.start(); } else { - unsafe { - let current = CFAbsoluteTimeGetCurrent(); - let duration = instant - now; - let fsecs = - duration.subsec_nanos() as f64 / 1_000_000_000.0 + duration.as_secs() as f64; - CFRunLoopTimerSetNextFireDate(&self.timer, current + fsecs); - } + let current = CFAbsoluteTimeGetCurrent(); + let duration = instant - now; + let fsecs = + duration.subsec_nanos() as f64 / 1_000_000_000.0 + duration.as_secs() as f64; + self.timer.set_next_fire_date(current + fsecs); } } } diff --git a/src/platform_impl/apple/uikit/event_loop.rs b/src/platform_impl/apple/uikit/event_loop.rs index b2e2dcbf..c9f4009a 100644 --- a/src/platform_impl/apple/uikit/event_loop.rs +++ b/src/platform_impl/apple/uikit/event_loop.rs @@ -1,21 +1,19 @@ -use std::ffi::{c_char, c_int, c_void}; -use std::ptr::{self, NonNull}; +use std::ffi::c_void; +use std::ptr; use std::sync::Arc; use objc2::rc::Retained; use objc2::runtime::ProtocolObject; use objc2::{msg_send, ClassType, MainThreadMarker}; use objc2_core_foundation::{ - kCFRunLoopDefaultMode, CFIndex, CFRunLoopActivity, CFRunLoopAddObserver, CFRunLoopGetMain, - CFRunLoopObserver, CFRunLoopObserverCreate, + kCFRunLoopDefaultMode, CFIndex, CFRunLoop, CFRunLoopActivity, CFRunLoopObserver, }; use objc2_foundation::{NSNotificationCenter, NSObjectProtocol}; use objc2_ui_kit::{ UIApplication, UIApplicationDidBecomeActiveNotification, UIApplicationDidEnterBackgroundNotification, UIApplicationDidFinishLaunchingNotification, - UIApplicationDidReceiveMemoryWarningNotification, UIApplicationMain, - UIApplicationWillEnterForegroundNotification, UIApplicationWillResignActiveNotification, - UIApplicationWillTerminateNotification, UIScreen, + UIApplicationDidReceiveMemoryWarningNotification, UIApplicationWillEnterForegroundNotification, + UIApplicationWillResignActiveNotification, UIApplicationWillTerminateNotification, UIScreen, }; use rwh_06::HasDisplayHandle; @@ -253,24 +251,9 @@ impl EventLoop { `EventLoop::run_app` calls `UIApplicationMain` on iOS", ); - extern "C" { - // These functions are in crt_externs.h. - fn _NSGetArgc() -> *mut c_int; - fn _NSGetArgv() -> *mut *mut *mut c_char; - } - - app_state::launch(self.mtm, app, || unsafe { - UIApplicationMain( - *_NSGetArgc(), - NonNull::new(*_NSGetArgv()).unwrap(), - // We intentionally override neither the application nor the delegate, to allow - // the user to do so themselves! - None, - None, - ); - }); - - unreachable!() + // We intentionally override neither the application nor the delegate, + // to allow the user to do so themselves! + app_state::launch(self.mtm, app, || UIApplication::main(None, None, self.mtm)) } pub fn window_target(&self) -> &dyn RootActiveEventLoop { @@ -335,9 +318,9 @@ fn setup_control_flow_observers() { } } - let main_loop = CFRunLoopGetMain().unwrap(); + let main_loop = CFRunLoop::main().unwrap(); - let begin_observer = CFRunLoopObserverCreate( + let begin_observer = CFRunLoopObserver::new( None, CFRunLoopActivity::AfterWaiting.0, true, @@ -346,9 +329,9 @@ fn setup_control_flow_observers() { ptr::null_mut(), ) .unwrap(); - CFRunLoopAddObserver(&main_loop, Some(&begin_observer), kCFRunLoopDefaultMode); + main_loop.add_observer(Some(&begin_observer), kCFRunLoopDefaultMode); - let main_end_observer = CFRunLoopObserverCreate( + let main_end_observer = CFRunLoopObserver::new( None, (CFRunLoopActivity::Exit | CFRunLoopActivity::BeforeWaiting).0, true, @@ -357,9 +340,9 @@ fn setup_control_flow_observers() { ptr::null_mut(), ) .unwrap(); - CFRunLoopAddObserver(&main_loop, Some(&main_end_observer), kCFRunLoopDefaultMode); + main_loop.add_observer(Some(&main_end_observer), kCFRunLoopDefaultMode); - let end_observer = CFRunLoopObserverCreate( + let end_observer = CFRunLoopObserver::new( None, (CFRunLoopActivity::Exit | CFRunLoopActivity::BeforeWaiting).0, true, @@ -368,6 +351,6 @@ fn setup_control_flow_observers() { ptr::null_mut(), ) .unwrap(); - CFRunLoopAddObserver(&main_loop, Some(&end_observer), kCFRunLoopDefaultMode); + main_loop.add_observer(Some(&end_observer), kCFRunLoopDefaultMode); } }