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:
parent
05dd31b8ea
commit
340f951d10
34 changed files with 2756 additions and 2335 deletions
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue