Show the menu bar in borderless fullscreen on macOS (#2053)

* In MacOS, only disable menu bar in exclusive fullscreen

* Save and restore fullscreen options in set_fullscreen

* Don't always cache presentation options when entering exclusive fullscreen

This commit caches presentation options when entering exclusive fullscreen
only if we're coming from borderless fullscreen.

Then, when transitioning from exclusive -> borderless, if no cached presentation
options are present, then the default borderless options are applied.

This fixes the menu bar being unavailable when taking the following path:
[not fullscreen] -> [exclusive fullscreen] -> [borderless fullscreen].

Without this commit, the presentation options from [not fullscreen] were being
cached and then applied to [borderless fullscreen].

* Restore the window level when switching to exclusive fullscreen

The hack of using `CGShieldingWindowLevel() + 1` in borderless fullscreen needs
to be undone when switching from [borderless] -> [exclusive] fullscreen,
otherwise there are menu bar glitches when following a path through
[borderless] -> [exclusive] -> [borderless] modes.

Now, this might appear to conflict with the 'always on top' feature which uses
the 'floating window' level, but this feature appears to be broken anyway when
entering and exiting fullscreen with an always-on-top window. So, rather than
introducing logic to attempt to restore to the 'floating' level here, I think
it's better to do the simple thing for now and then introduce logic for
always-on-top windows when fixing the overall fullscreen behaviour.

* Update the changelog

Co-authored-by: Ehden Sinai <ehdens@gmail.com>
Co-authored-by: Francesca Lovebloom <francesca@brainiumstudios.com>
This commit is contained in:
Ian Hobson 2021-11-19 22:05:36 +01:00 committed by GitHub
parent 3ecbea3c39
commit f2de8475fc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 59 additions and 9 deletions

View file

@ -22,6 +22,7 @@
- On Windows, increase wait timer resolution for more accurate timing when using `WaitUntil`. - On Windows, increase wait timer resolution for more accurate timing when using `WaitUntil`.
- On macOS, fix native file dialogs hanging the event loop. - On macOS, fix native file dialogs hanging the event loop.
- On Wayland, implement a workaround for wrong configure size when using `xdg_decoration` in `kwin_wayland` - On Wayland, implement a workaround for wrong configure size when using `xdg_decoration` in `kwin_wayland`
- On macOS, fix an issue that prevented the menu bar from showing in borderless fullscreen mode.
# 0.25.0 (2021-05-15) # 0.25.0 (2021-05-15)

View file

@ -281,7 +281,10 @@ pub struct SharedState {
is_simple_fullscreen: bool, is_simple_fullscreen: bool,
pub saved_style: Option<NSWindowStyleMask>, pub saved_style: Option<NSWindowStyleMask>,
/// Presentation options saved before entering `set_simple_fullscreen`, and /// Presentation options saved before entering `set_simple_fullscreen`, and
/// restored upon exiting it /// restored upon exiting it. Also used when transitioning from Borderless to
/// Exclusive fullscreen in `set_fullscreen` because we need to disable the menu
/// bar in exclusive fullscreen but want to restore the original options when
/// transitioning back to borderless fullscreen.
save_presentation_opts: Option<NSApplicationPresentationOptions>, save_presentation_opts: Option<NSApplicationPresentationOptions>,
pub saved_desktop_display_mode: Option<(CGDisplay, CGDisplayMode)>, pub saved_desktop_display_mode: Option<(CGDisplay, CGDisplayMode)>,
} }
@ -782,6 +785,15 @@ impl UnownedWindow {
let mut fade_token = ffi::kCGDisplayFadeReservationInvalidToken; let mut fade_token = ffi::kCGDisplayFadeReservationInvalidToken;
if matches!(old_fullscreen, Some(Fullscreen::Borderless(_))) {
unsafe {
let app = NSApp();
trace!("Locked shared state in `set_fullscreen`");
let mut shared_state_lock = self.shared_state.lock().unwrap();
shared_state_lock.save_presentation_opts = Some(app.presentationOptions_());
}
}
unsafe { unsafe {
// Fade to black (and wait for the fade to complete) to hide the // Fade to black (and wait for the fade to complete) to hide the
// flicker from capturing the display and switching display mode // flicker from capturing the display and switching display mode
@ -832,7 +844,6 @@ impl UnownedWindow {
trace!("Locked shared state in `set_fullscreen`"); trace!("Locked shared state in `set_fullscreen`");
let mut shared_state_lock = self.shared_state.lock().unwrap(); let mut shared_state_lock = self.shared_state.lock().unwrap();
shared_state_lock.fullscreen = fullscreen.clone(); shared_state_lock.fullscreen = fullscreen.clone();
trace!("Unlocked shared state in `set_fullscreen`");
INTERRUPT_EVENT_LOOP_EXIT.store(true, Ordering::SeqCst); INTERRUPT_EVENT_LOOP_EXIT.store(true, Ordering::SeqCst);
@ -873,16 +884,42 @@ impl UnownedWindow {
// of the menu bar, and this looks broken, so we must make sure // of the menu bar, and this looks broken, so we must make sure
// that the menu bar is disabled. This is done in the window // that the menu bar is disabled. This is done in the window
// delegate in `window:willUseFullScreenPresentationOptions:`. // delegate in `window:willUseFullScreenPresentationOptions:`.
let app = NSApp();
trace!("Locked shared state in `set_fullscreen`");
shared_state_lock.save_presentation_opts = Some(app.presentationOptions_());
let presentation_options =
NSApplicationPresentationOptions::NSApplicationPresentationFullScreen
| NSApplicationPresentationOptions::NSApplicationPresentationHideDock
| NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar;
app.setPresentationOptions_(presentation_options);
let () = msg_send![*self.ns_window, setLevel: ffi::CGShieldingWindowLevel() + 1]; let () = msg_send![*self.ns_window, setLevel: ffi::CGShieldingWindowLevel() + 1];
}, },
( (
&Some(Fullscreen::Exclusive(RootVideoMode { ref video_mode })), &Some(Fullscreen::Exclusive(RootVideoMode { ref video_mode })),
&Some(Fullscreen::Borderless(_)), &Some(Fullscreen::Borderless(_)),
) => unsafe { ) => unsafe {
let presentation_options =
shared_state_lock.save_presentation_opts.unwrap_or_else(|| {
NSApplicationPresentationOptions::NSApplicationPresentationFullScreen
| NSApplicationPresentationOptions::NSApplicationPresentationAutoHideDock
| NSApplicationPresentationOptions::NSApplicationPresentationAutoHideMenuBar
});
NSApp().setPresentationOptions_(presentation_options);
util::restore_display_mode_async(video_mode.monitor().inner.native_identifier()); util::restore_display_mode_async(video_mode.monitor().inner.native_identifier());
// Restore the normal window level following the Borderless fullscreen
// `CGShieldingWindowLevel() + 1` hack.
let () = msg_send![
*self.ns_window,
setLevel: ffi::NSWindowLevel::NSNormalWindowLevel
];
}, },
_ => INTERRUPT_EVENT_LOOP_EXIT.store(false, Ordering::SeqCst), _ => INTERRUPT_EVENT_LOOP_EXIT.store(false, Ordering::SeqCst),
} };
trace!("Unlocked shared state in `set_fullscreen`");
} }
#[inline] #[inline]

View file

@ -482,10 +482,10 @@ extern "C" fn window_will_exit_fullscreen(this: &Object, _: Sel, _: id) {
} }
extern "C" fn window_will_use_fullscreen_presentation_options( extern "C" fn window_will_use_fullscreen_presentation_options(
_this: &Object, this: &Object,
_: Sel, _: Sel,
_: id, _: id,
_proposed_options: NSUInteger, proposed_options: NSUInteger,
) -> NSUInteger { ) -> NSUInteger {
// Generally, games will want to disable the menu bar and the dock. Ideally, // Generally, games will want to disable the menu bar and the dock. Ideally,
// this would be configurable by the user. Unfortunately because of our // this would be configurable by the user. Unfortunately because of our
@ -495,10 +495,22 @@ extern "C" fn window_will_use_fullscreen_presentation_options(
// still want to make this configurable for borderless fullscreen. Right now // still want to make this configurable for borderless fullscreen. Right now
// we don't, for consistency. If we do, it should be documented that the // we don't, for consistency. If we do, it should be documented that the
// user-provided options are ignored in exclusive fullscreen. // user-provided options are ignored in exclusive fullscreen.
(NSApplicationPresentationOptions::NSApplicationPresentationFullScreen let mut options: NSUInteger = proposed_options;
| NSApplicationPresentationOptions::NSApplicationPresentationHideDock with_state(this, |state| {
| NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar) state.with_window(|window| {
.bits() trace!("Locked shared state in `window_will_use_fullscreen_presentation_options`");
let shared_state = window.shared_state.lock().unwrap();
if let Some(Fullscreen::Exclusive(_)) = shared_state.fullscreen {
options = (NSApplicationPresentationOptions::NSApplicationPresentationFullScreen
| NSApplicationPresentationOptions::NSApplicationPresentationHideDock
| NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar)
.bits();
}
trace!("Unlocked shared state in `window_will_use_fullscreen_presentation_options`");
})
});
options
} }
/// Invoked when entered fullscreen /// Invoked when entered fullscreen