From 28a5feef28ba143a043aa1944ad05d615c63a82d Mon Sep 17 00:00:00 2001 From: Aleksi Juvani <3168386+aleksijuvani@users.noreply.github.com> Date: Mon, 16 Sep 2019 02:59:37 +0300 Subject: [PATCH] Fix freeze upon exiting exclusive fullscreen on macOS 10.15 (#1127) --- CHANGELOG.md | 1 + src/platform_impl/macos/util/async.rs | 15 +++++ src/platform_impl/macos/window.rs | 84 ++++++++++++--------------- 3 files changed, 53 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de34b407..24b6f47e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - On macOS, differentiate between `CursorIcon::Grab` and `CursorIcon::Grabbing`. - On Wayland, fix event processing sometimes stalling when using OpenGL with vsync. - Officially remove the Emscripten backend +- On macOS 10.15, fix freeze upon exiting exclusive fullscreen mode. # 0.20.0 Alpha 3 (2019-08-14) diff --git a/src/platform_impl/macos/util/async.rs b/src/platform_impl/macos/util/async.rs index d7fe9592..ad1a891b 100644 --- a/src/platform_impl/macos/util/async.rs +++ b/src/platform_impl/macos/util/async.rs @@ -231,6 +231,21 @@ pub unsafe fn toggle_full_screen_async( ); } +extern "C" fn restore_display_mode_callback(screen: *mut c_void) { + unsafe { + let screen = Box::from_raw(screen as *mut u32); + ffi::CGRestorePermanentDisplayConfiguration(); + assert_eq!(ffi::CGDisplayRelease(*screen), ffi::kCGErrorSuccess); + } +} +pub unsafe fn restore_display_mode_async(ns_screen: u32) { + dispatch_async_f( + dispatch_get_main_queue(), + Box::into_raw(Box::new(ns_screen)) as *mut _, + restore_display_mode_callback, + ); +} + struct SetMaximizedData { ns_window: id, is_zoomed: bool, diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index 75adf8a3..4c9e3f9b 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -617,25 +617,6 @@ impl UnownedWindow { self.set_maximized(maximized); } - fn restore_display_mode(&self) { - trace!("Locked shared state in `restore_display_mode`"); - let shared_state_lock = self.shared_state.lock().unwrap(); - - if let Some(Fullscreen::Exclusive(RootVideoMode { ref video_mode })) = - shared_state_lock.fullscreen - { - unsafe { - ffi::CGRestorePermanentDisplayConfiguration(); - assert_eq!( - ffi::CGDisplayRelease(video_mode.monitor().inner.native_identifier()), - ffi::kCGErrorSuccess - ); - } - } - - trace!("Unlocked shared state in `restore_display_mode`"); - } - #[inline] pub fn set_maximized(&self, maximized: bool) { let is_zoomed = self.is_zoomed(); @@ -763,7 +744,39 @@ impl UnownedWindow { } } + trace!("Locked shared state in `set_fullscreen`"); + let mut shared_state_lock = self.shared_state.lock().unwrap(); + shared_state_lock.fullscreen = fullscreen.clone(); + trace!("Unlocked shared state in `set_fullscreen`"); + match (&old_fullscreen, &fullscreen) { + (&None, &Some(_)) => unsafe { + util::toggle_full_screen_async( + *self.ns_window, + *self.ns_view, + old_fullscreen.is_none(), + Arc::downgrade(&self.shared_state), + ); + }, + (&Some(Fullscreen::Borderless(_)), &None) => unsafe { + // State is restored by `window_did_exit_fullscreen` + util::toggle_full_screen_async( + *self.ns_window, + *self.ns_view, + old_fullscreen.is_none(), + Arc::downgrade(&self.shared_state), + ); + }, + (&Some(Fullscreen::Exclusive(RootVideoMode { ref video_mode })), &None) => unsafe { + util::restore_display_mode_async(video_mode.monitor().inner.native_identifier()); + // Rest of the state is restored by `window_did_exit_fullscreen` + util::toggle_full_screen_async( + *self.ns_window, + *self.ns_view, + old_fullscreen.is_none(), + Arc::downgrade(&self.shared_state), + ); + }, (&Some(Fullscreen::Borderless(_)), &Some(Fullscreen::Exclusive(_))) => unsafe { // If we're already in fullscreen mode, calling // `CGDisplayCapture` will place the shielding window on top of @@ -775,37 +788,14 @@ impl UnownedWindow { // delegate in `window:willUseFullScreenPresentationOptions:`. msg_send![*self.ns_window, setLevel: ffi::CGShieldingWindowLevel() + 1]; }, - (&Some(Fullscreen::Exclusive(_)), &None) => unsafe { - self.restore_display_mode(); - - util::toggle_full_screen_async( - *self.ns_window, - *self.ns_view, - old_fullscreen.is_none(), - Arc::downgrade(&self.shared_state), - ); - }, - (&Some(Fullscreen::Exclusive(_)), &Some(Fullscreen::Borderless(_))) => { - self.restore_display_mode(); - } - (&None, &Some(Fullscreen::Exclusive(_))) - | (&None, &Some(Fullscreen::Borderless(_))) - | (&Some(Fullscreen::Borderless(_)), &None) => unsafe { - // Wish it were this simple for all cases - util::toggle_full_screen_async( - *self.ns_window, - *self.ns_view, - old_fullscreen.is_none(), - Arc::downgrade(&self.shared_state), - ); + ( + &Some(Fullscreen::Exclusive(RootVideoMode { ref video_mode })), + &Some(Fullscreen::Borderless(_)), + ) => unsafe { + util::restore_display_mode_async(video_mode.monitor().inner.native_identifier()); }, _ => (), } - - trace!("Locked shared state in `set_fullscreen`"); - let mut shared_state_lock = self.shared_state.lock().unwrap(); - shared_state_lock.fullscreen = fullscreen.clone(); - trace!("Unlocked shared state in `set_fullscreen`"); } #[inline]