Refactor macOS to use new objc2 features (#2465)

* Remove UnownedWindow::inner_rect

* Refactor custom view to use much less `unsafe`

The compiler fence is safe to get rid of now since `interpretKeyEvents` takes `&mut self`

* Refactor Window to use much less unsafe

* Refactor NSApplication usage to have much less unsafe

* Remove cocoa dependency

* Enable `deny(unsafe_op_in_unsafe_fn)` on macOS

Also re-enable clippy `let_unit_value` lint

* Remove #[macro_use] on macOS

* Refactor window delegate to use much less unsafe
This commit is contained in:
Mads Marquart 2022-09-08 16:45:29 +02:00 committed by GitHub
parent 05dd31b8ea
commit 340f951d10
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 2756 additions and 2335 deletions

View file

@ -11,16 +11,12 @@ use std::{
sync::mpsc,
};
use cocoa::{
appkit::{NSApp, NSEventModifierFlags, NSEventSubtype, NSEventType::NSApplicationDefined},
base::{id, nil},
foundation::{NSPoint, NSTimeInterval},
};
use objc2::foundation::is_main_thread;
use objc2::rc::{autoreleasepool, Id, Shared};
use objc2::ClassType;
use objc2::{msg_send_id, ClassType};
use raw_window_handle::{AppKitDisplayHandle, RawDisplayHandle};
use super::appkit::{NSApp, NSApplicationActivationPolicy, NSEvent};
use crate::{
event::Event,
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootWindowTarget},
@ -95,15 +91,11 @@ impl<T: 'static> EventLoopWindowTarget<T> {
impl<T> EventLoopWindowTarget<T> {
pub(crate) fn hide_application(&self) {
let cls = objc::runtime::Class::get("NSApplication").unwrap();
let app: cocoa::base::id = unsafe { msg_send![cls, sharedApplication] };
unsafe { msg_send![app, hide: 0] }
NSApp().hide(None)
}
pub(crate) fn hide_other_applications(&self) {
let cls = objc::runtime::Class::get("NSApplication").unwrap();
let app: cocoa::base::id = unsafe { msg_send![cls, sharedApplication] };
unsafe { msg_send![app, hideOtherApplications: 0] }
NSApp().hideOtherApplications(None)
}
}
@ -141,31 +133,29 @@ impl Default for PlatformSpecificEventLoopAttributes {
impl<T> EventLoop<T> {
pub(crate) fn new(attributes: &PlatformSpecificEventLoopAttributes) -> Self {
let delegate = unsafe {
if !is_main_thread() {
panic!("On macOS, `EventLoop` must be created on the main thread!");
}
if !is_main_thread() {
panic!("On macOS, `EventLoop` must be created on the main thread!");
}
// This must be done before `NSApp()` (equivalent to sending
// `sharedApplication`) is called anywhere else, or we'll end up
// with the wrong `NSApplication` class and the wrong thread could
// be marked as main.
let app: id = msg_send![WinitApplication::class(), sharedApplication];
// This must be done before `NSApp()` (equivalent to sending
// `sharedApplication`) is called anywhere else, or we'll end up
// with the wrong `NSApplication` class and the wrong thread could
// be marked as main.
let app: Id<WinitApplication, Shared> =
unsafe { msg_send_id![WinitApplication::class(), sharedApplication] };
use cocoa::appkit::NSApplicationActivationPolicy::*;
let activation_policy = match attributes.activation_policy {
ActivationPolicy::Regular => NSApplicationActivationPolicyRegular,
ActivationPolicy::Accessory => NSApplicationActivationPolicyAccessory,
ActivationPolicy::Prohibited => NSApplicationActivationPolicyProhibited,
};
let delegate = ApplicationDelegate::new(activation_policy, attributes.default_menu);
autoreleasepool(|_| {
let _: () = msg_send![app, setDelegate: &*delegate];
});
delegate
use NSApplicationActivationPolicy::*;
let activation_policy = match attributes.activation_policy {
ActivationPolicy::Regular => NSApplicationActivationPolicyRegular,
ActivationPolicy::Accessory => NSApplicationActivationPolicyAccessory,
ActivationPolicy::Prohibited => NSApplicationActivationPolicyProhibited,
};
let delegate = ApplicationDelegate::new(activation_policy, attributes.default_menu);
autoreleasepool(|_| {
app.setDelegate(&delegate);
});
let panic_info: Rc<PanicInfo> = Default::default();
setup_control_flow_observers(Rc::downgrade(&panic_info));
EventLoop {
@ -208,9 +198,8 @@ impl<T> EventLoop<T> {
self._callback = Some(Rc::clone(&callback));
let exit_code = autoreleasepool(|_| unsafe {
let exit_code = autoreleasepool(|_| {
let app = NSApp();
assert_ne!(app, nil);
// A bit of juggling with the callback references to make sure
// that `self.callback` is the only owner of the callback.
@ -218,7 +207,7 @@ impl<T> EventLoop<T> {
drop(callback);
AppState::set_callback(weak_cb, Rc::clone(&self.window_target));
let _: () = msg_send![app, run];
unsafe { app.run() };
if let Some(panic) = self.panic_info.take() {
drop(self._callback.take());
@ -236,24 +225,6 @@ impl<T> EventLoop<T> {
}
}
#[inline]
pub unsafe fn post_dummy_event(target: id) {
let event_class = class!(NSEvent);
let dummy_event: id = msg_send![
event_class,
otherEventWithType: NSApplicationDefined
location: NSPoint::new(0.0, 0.0)
modifierFlags: NSEventModifierFlags::empty()
timestamp: 0 as NSTimeInterval
windowNumber: 0isize
context: nil
subtype: NSEventSubtype::NSWindowExposedEventType
data1: 0isize
data2: 0isize
];
let _: () = msg_send![target, postEvent: dummy_event, atStart: true];
}
/// Catches panics that happen inside `f` and when a panic
/// happens, stops the `sharedApplication`
#[inline]
@ -272,15 +243,11 @@ pub fn stop_app_on_panic<F: FnOnce() -> R + UnwindSafe, R>(
let panic_info = panic_info.upgrade().unwrap();
panic_info.set_panic(e);
}
unsafe {
let app_class = class!(NSApplication);
let app: id = msg_send![app_class, sharedApplication];
let _: () = msg_send![app, stop: nil];
// Posting a dummy event to get `stop` to take effect immediately.
// See: https://stackoverflow.com/questions/48041279/stopping-the-nsapplication-main-event-loop/48064752#48064752
post_dummy_event(app);
}
let app = NSApp();
app.stop(None);
// Posting a dummy event to get `stop` to take effect immediately.
// See: https://stackoverflow.com/questions/48041279/stopping-the-nsapplication-main-event-loop/48064752#48064752
app.postEvent_atStart(&NSEvent::dummy(), true);
None
}
}