diff --git a/src/changelog/unreleased.md b/src/changelog/unreleased.md index f3a0f6d2c3..6b0849c091 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -39,3 +39,7 @@ The migration guide could reference other migration examples in the current changelog entry. ## Unreleased + +### Fixed + +- On macOS, fix panic on exit when dropping windows outside the event loop. diff --git a/src/platform_impl/macos/app_delegate.rs b/src/platform_impl/macos/app_delegate.rs index ffb25abcaf..984f4d099e 100644 --- a/src/platform_impl/macos/app_delegate.rs +++ b/src/platform_impl/macos/app_delegate.rs @@ -52,6 +52,8 @@ pub(super) struct State { wait_timeout: Cell>, pending_events: RefCell>, pending_redraw: RefCell>, + // NOTE: This is strongly referenced by our `NSWindowDelegate` and our `NSView` subclass, and + // as such should be careful to not add fields that, in turn, strongly reference those. } declare_class!( @@ -71,7 +73,7 @@ declare_class!( unsafe impl NSObjectProtocol for ApplicationDelegate {} unsafe impl NSApplicationDelegate for ApplicationDelegate { - // Note: This will, globally, only be run once, no matter how many + // NOTE: This will, globally, only be run once, no matter how many // `EventLoop`s the user creates. #[method(applicationDidFinishLaunching:)] fn did_finish_launching(&self, _sender: Option<&AnyObject>) { @@ -106,7 +108,7 @@ declare_class!( // In this case we still want to consider Winit's `EventLoop` to be "running", // so we call `start_running()` above. if self.ivars().stop_on_launch.get() { - // Note: the original idea had been to only stop the underlying `RunLoop` + // NOTE: the original idea had been to only stop the underlying `RunLoop` // for the app but that didn't work as expected (`-[NSApplication run]` // effectively ignored the attempt to stop the RunLoop and re-started it). // @@ -188,7 +190,7 @@ impl ApplicationDelegate { /// Clears the `running` state and resets the `control_flow` state when an `EventLoop` exits. /// - /// Note: that if the `NSApplication` has been launched then that state is preserved, + /// NOTE: that if the `NSApplication` has been launched then that state is preserved, /// and we won't need to re-launch the app if subsequent EventLoops are run. pub fn internal_exit(&self) { self.handle_event(Event::LoopExiting); diff --git a/src/platform_impl/macos/event_loop.rs b/src/platform_impl/macos/event_loop.rs index d3c5b362ea..b5a3580970 100644 --- a/src/platform_impl/macos/event_loop.rs +++ b/src/platform_impl/macos/event_loop.rs @@ -78,6 +78,10 @@ impl ActiveEventLoop { RootWindowTarget { p, _marker: PhantomData } } + pub(super) fn app_delegate(&self) -> &ApplicationDelegate { + &self.delegate + } + pub fn create_custom_cursor(&self, source: CustomCursorSource) -> RootCustomCursor { RootCustomCursor { inner: CustomCursor::new(source.inner) } } @@ -133,9 +137,7 @@ impl ActiveEventLoop { pub(crate) fn owned_display_handle(&self) -> OwnedDisplayHandle { OwnedDisplayHandle } -} -impl ActiveEventLoop { pub(crate) fn hide_application(&self) { NSApplication::sharedApplication(self.mtm).hide(None) } diff --git a/src/platform_impl/macos/view.rs b/src/platform_impl/macos/view.rs index c80b44262d..677d461ab1 100644 --- a/src/platform_impl/macos/view.rs +++ b/src/platform_impl/macos/view.rs @@ -110,8 +110,11 @@ fn get_left_modifier_code(key: &Key) -> KeyCode { } } -#[derive(Debug, Default)] +#[derive(Debug)] pub struct ViewState { + /// Strong reference to the global application state. + app_delegate: Id, + cursor_state: RefCell, ime_position: Cell, ime_size: Cell, @@ -204,8 +207,7 @@ declare_class!( // It's a workaround for https://github.com/rust-windowing/winit/issues/2640, don't replace with `self.window_id()`. if let Some(window) = self.ivars()._ns_window.load() { - let app_delegate = ApplicationDelegate::get(MainThreadMarker::from(self)); - app_delegate.handle_redraw(window.id()); + self.ivars().app_delegate.handle_redraw(window.id()); } #[allow(clippy::let_unit_value)] @@ -773,16 +775,28 @@ declare_class!( impl WinitView { pub(super) fn new( + app_delegate: &ApplicationDelegate, window: &WinitWindow, accepts_first_mouse: bool, option_as_alt: OptionAsAlt, ) -> Id { let mtm = MainThreadMarker::from(window); let this = mtm.alloc().set_ivars(ViewState { + app_delegate: app_delegate.retain(), + cursor_state: Default::default(), + ime_position: Default::default(), + ime_size: Default::default(), + modifiers: Default::default(), + phys_modifiers: Default::default(), + tracking_rect: Default::default(), + ime_state: Default::default(), + input_source: Default::default(), + ime_allowed: Default::default(), + forward_key_to_app: Default::default(), + marked_text: Default::default(), accepts_first_mouse, _ns_window: WeakId::new(&window.retain()), option_as_alt: Cell::new(option_as_alt), - ..Default::default() }); let this: Id = unsafe { msg_send_id![super(this), init] }; @@ -818,13 +832,11 @@ impl WinitView { } fn queue_event(&self, event: WindowEvent) { - let app_delegate = ApplicationDelegate::get(MainThreadMarker::from(self)); - app_delegate.queue_window_event(self.window().id(), event); + self.ivars().app_delegate.queue_window_event(self.window().id(), event); } fn queue_device_event(&self, event: DeviceEvent) { - let app_delegate = ApplicationDelegate::get(MainThreadMarker::from(self)); - app_delegate.queue_device_event(event); + self.ivars().app_delegate.queue_device_event(event); } fn scale_factor(&self) -> f64 { diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index 5d20b4e8a3..d19ea9171f 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -28,7 +28,9 @@ impl Window { attributes: WindowAttributes, ) -> Result { let mtm = window_target.mtm; - let delegate = autoreleasepool(|_| WindowDelegate::new(attributes, mtm))?; + let delegate = autoreleasepool(|_| { + WindowDelegate::new(window_target.app_delegate(), attributes, mtm) + })?; Ok(Window { window: MainThreadBound::new(delegate.window().retain(), mtm), delegate: MainThreadBound::new(delegate, mtm), diff --git a/src/platform_impl/macos/window_delegate.rs b/src/platform_impl/macos/window_delegate.rs index b9e51a01ab..4607ba2603 100644 --- a/src/platform_impl/macos/window_delegate.rs +++ b/src/platform_impl/macos/window_delegate.rs @@ -74,6 +74,9 @@ impl Default for PlatformSpecificWindowAttributes { #[derive(Debug)] pub(crate) struct State { + /// Strong reference to the global application state. + app_delegate: Id, + window: Id, current_theme: Cell>, @@ -442,7 +445,11 @@ declare_class!( } ); -fn new_window(attrs: &WindowAttributes, mtm: MainThreadMarker) -> Option> { +fn new_window( + app_delegate: &ApplicationDelegate, + attrs: &WindowAttributes, + mtm: MainThreadMarker, +) -> Option> { autoreleasepool(|_| { let screen = match attrs.fullscreen.clone().map(Into::into) { Some(Fullscreen::Borderless(Some(monitor))) @@ -579,6 +586,7 @@ fn new_window(attrs: &WindowAttributes, mtm: MainThreadMarker) -> Option Option Result, RootOsError> { - let window = new_window(&attrs, mtm) + pub(super) fn new( + app_delegate: &ApplicationDelegate, + attrs: WindowAttributes, + mtm: MainThreadMarker, + ) -> Result, RootOsError> { + let window = new_window(app_delegate, &attrs, mtm) .ok_or_else(|| os_error!(OsError::CreationError("couldn't create `NSWindow`")))?; #[cfg(feature = "rwh_06")] @@ -660,6 +672,7 @@ impl WindowDelegate { }; let delegate = mtm.alloc().set_ivars(State { + app_delegate: app_delegate.retain(), window: window.retain(), current_theme: Cell::new(current_theme), previous_position: Cell::new(None), @@ -756,8 +769,7 @@ impl WindowDelegate { } pub(crate) fn queue_event(&self, event: WindowEvent) { - let app_delegate = ApplicationDelegate::get(MainThreadMarker::from(self)); - app_delegate.queue_window_event(self.window().id(), event); + self.ivars().app_delegate.queue_window_event(self.window().id(), event); } fn queue_static_scale_factor_changed_event(&self) { @@ -770,8 +782,7 @@ impl WindowDelegate { let content_size = self.window().contentRectForFrameRect(self.window().frame()).size; let content_size = LogicalSize::new(content_size.width, content_size.height); - let app_delegate = ApplicationDelegate::get(MainThreadMarker::from(self)); - app_delegate.queue_static_scale_factor_changed_event( + self.ivars().app_delegate.queue_static_scale_factor_changed_event( self.window().retain(), content_size.to_physical(scale_factor), scale_factor, @@ -833,8 +844,7 @@ impl WindowDelegate { } pub fn request_redraw(&self) { - let app_delegate = ApplicationDelegate::get(MainThreadMarker::from(self)); - app_delegate.queue_redraw(self.window().id()); + self.ivars().app_delegate.queue_redraw(self.window().id()); } #[inline]