diff --git a/CHANGELOG.md b/CHANGELOG.md index ddb3d629..f7210605 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Unreleased +- On macOS, fix error when `set_fullscreen` is called during fullscreen transition. - On all platforms except mobile and WASM, implement `Window::set_minimized`. - On X11, fix `CursorEntered` event being generated for non-winit windows. - On macOS, fix crash when starting maximized without decorations. diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index 404eafa5..ee95f17a 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -243,6 +243,13 @@ lazy_static! { pub struct SharedState { pub resizable: bool, pub fullscreen: Option, + // This is true between windowWillEnterFullScreen and windowDidEnterFullScreen + // or windowWillExitFullScreen and windowDidExitFullScreen. + // We must not toggle fullscreen when this is true. + pub in_fullscreen_transition: bool, + // If it is attempted to toggle fullscreen when in_fullscreen_transition is true, + // Set target_fullscreen and do after fullscreen transition is end. + pub target_fullscreen: Option>, pub maximized: bool, pub standard_frame: Option, is_simple_fullscreen: bool, @@ -661,11 +668,18 @@ impl UnownedWindow { #[inline] pub fn set_fullscreen(&self, fullscreen: Option) { trace!("Locked shared state in `set_fullscreen`"); - let shared_state_lock = self.shared_state.lock().unwrap(); + let mut shared_state_lock = self.shared_state.lock().unwrap(); if shared_state_lock.is_simple_fullscreen { trace!("Unlocked shared state in `set_fullscreen`"); return; } + if shared_state_lock.in_fullscreen_transition { + // We can't set fullscreen here. + // Set fullscreen after transition. + shared_state_lock.target_fullscreen = Some(fullscreen); + trace!("Unlocked shared state in `set_fullscreen`"); + return; + } let old_fullscreen = shared_state_lock.fullscreen.clone(); if fullscreen == old_fullscreen { trace!("Unlocked shared state in `set_fullscreen`"); diff --git a/src/platform_impl/macos/window_delegate.rs b/src/platform_impl/macos/window_delegate.rs index cb1bf3c2..ecdd4eef 100644 --- a/src/platform_impl/macos/window_delegate.rs +++ b/src/platform_impl/macos/window_delegate.rs @@ -195,6 +195,10 @@ lazy_static! { sel!(windowDidExitFullScreen:), window_did_exit_fullscreen as extern "C" fn(&Object, Sel, id), ); + decl.add_method( + sel!(windowWillExitFullScreen:), + window_will_exit_fullscreen as extern "C" fn(&Object, Sel, id), + ); decl.add_method( sel!(windowDidFailToEnterFullScreen:), window_did_fail_to_enter_fullscreen as extern "C" fn(&Object, Sel, id), @@ -419,13 +423,27 @@ extern "C" fn window_will_enter_fullscreen(this: &Object, _: Sel, _: id) { shared_state.fullscreen = Some(Fullscreen::Borderless(window.current_monitor())) } } - + shared_state.in_fullscreen_transition = true; trace!("Unlocked shared state in `window_will_enter_fullscreen`"); }) }); trace!("Completed `windowWillEnterFullscreen:`"); } +/// Invoked when before exit fullscreen +extern "C" fn window_will_exit_fullscreen(this: &Object, _: Sel, _: id) { + trace!("Triggered `windowWillExitFullScreen:`"); + with_state(this, |state| { + state.with_window(|window| { + trace!("Locked shared state in `window_will_exit_fullscreen`"); + let mut shared_state = window.shared_state.lock().unwrap(); + shared_state.in_fullscreen_transition = true; + trace!("Unlocked shared state in `window_will_exit_fullscreen`"); + }); + }); + trace!("Completed `windowWillExitFullScreen:`"); +} + extern "C" fn window_will_use_fullscreen_presentation_options( _this: &Object, _: Sel, @@ -451,6 +469,17 @@ extern "C" fn window_did_enter_fullscreen(this: &Object, _: Sel, _: id) { trace!("Triggered `windowDidEnterFullscreen:`"); with_state(this, |state| { state.initial_fullscreen = false; + state.with_window(|window| { + trace!("Locked shared state in `window_did_enter_fullscreen`"); + let mut shared_state = window.shared_state.lock().unwrap(); + shared_state.in_fullscreen_transition = false; + let target_fullscreen = shared_state.target_fullscreen.take(); + trace!("Unlocked shared state in `window_did_enter_fullscreen`"); + drop(shared_state); + if let Some(target_fullscreen) = target_fullscreen { + window.set_fullscreen(target_fullscreen); + } + }); }); trace!("Completed `windowDidEnterFullscreen:`"); } @@ -461,6 +490,15 @@ extern "C" fn window_did_exit_fullscreen(this: &Object, _: Sel, _: id) { with_state(this, |state| { state.with_window(|window| { window.restore_state_from_fullscreen(); + trace!("Locked shared state in `window_did_exit_fullscreen`"); + let mut shared_state = window.shared_state.lock().unwrap(); + shared_state.in_fullscreen_transition = false; + let target_fullscreen = shared_state.target_fullscreen.take(); + trace!("Unlocked shared state in `window_did_exit_fullscreen`"); + drop(shared_state); + if let Some(target_fullscreen) = target_fullscreen { + window.set_fullscreen(target_fullscreen); + } }) }); trace!("Completed `windowDidExitFullscreen:`"); @@ -485,6 +523,13 @@ extern "C" fn window_did_exit_fullscreen(this: &Object, _: Sel, _: id) { extern "C" fn window_did_fail_to_enter_fullscreen(this: &Object, _: Sel, _: id) { trace!("Triggered `windowDidFailToEnterFullscreen:`"); with_state(this, |state| { + state.with_window(|window| { + trace!("Locked shared state in `window_did_fail_to_enter_fullscreen`"); + let mut shared_state = window.shared_state.lock().unwrap(); + shared_state.in_fullscreen_transition = false; + shared_state.target_fullscreen = None; + trace!("Unlocked shared state in `window_did_fail_to_enter_fullscreen`"); + }); if state.initial_fullscreen { let _: () = unsafe { msg_send![*state.ns_window,