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.
This commit is contained in:
Mads Marquart 2025-04-20 07:36:49 +02:00 committed by GitHub
parent ed4d70fdd4
commit 24e2c6914a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 125 additions and 217 deletions

View file

@ -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;

View file

@ -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::*;

View file

@ -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::<CFArray<CGDisplayMode>>(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<Retained<NSScreen>> {
@ -329,21 +310,21 @@ pub(crate) fn flip_window_screen_coordinates(frame: NSRect) -> NSPoint {
fn refresh_rate_millihertz(id: CGDirectDisplayID, mode: &NativeDisplayMode) -> Option<NonZeroU32> {
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 {

View file

@ -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 => {

View file

@ -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);
}

View file

@ -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();
}
}

View file

@ -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<AppState>>, 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<W
}
}
pub(crate) fn launch(mtm: MainThreadMarker, app: impl ApplicationHandler, run: impl FnOnce()) {
pub(crate) fn launch<R>(
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);
}
}
}

View file

@ -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);
}
}