https://github.com/libsdl-org/SDL/commit/b4562c024323bf01dffb497fc6dc9db3eca29233
From b4562c024323bf01dffb497fc6dc9db3eca29233 Mon Sep 17 00:00:00 2001
From: Frank Praznik <[EMAIL REDACTED]>
Date: Sat, 11 Jan 2025 14:07:05 -0500
Subject: [PATCH] cocoa: Add a hint to control menu visibility in fullscreen
spaces windows
Adds SDL_HINT_VIDEO_MAC_FULLSCREEN_MENU_VISIBILITY to control whether or not the menu can be accessed when the cursor is moved to the top of the screen when a window is in fullscreen spaces mode.
The three values are true, false, and 'auto' (default), with auto resulting in a hidden menu if fullscreen was toggled programmatically, and the menu being accessible if fullscreen was toggled via the button on the window title bar, so the user has an easy way back out of fullscreen if the client app/game doesn't have a readily available option to toggle it.
---
include/SDL3/SDL_hints.h | 17 +++++++
src/video/cocoa/SDL_cocoavideo.m | 1 +
src/video/cocoa/SDL_cocoawindow.h | 3 ++
src/video/cocoa/SDL_cocoawindow.m | 78 ++++++++++++++++++++++++-------
4 files changed, 82 insertions(+), 17 deletions(-)
diff --git a/include/SDL3/SDL_hints.h b/include/SDL3/SDL_hints.h
index 622297c042812..de5b5b407a377 100644
--- a/include/SDL3/SDL_hints.h
+++ b/include/SDL3/SDL_hints.h
@@ -3330,6 +3330,23 @@ extern "C" {
*/
#define SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES "SDL_VIDEO_MAC_FULLSCREEN_SPACES"
+/**
+ * A variable that specifies the menu visibility when a window is fullscreen in Spaces on macOS.
+ *
+ * The variable can be set to the following values:
+ *
+ * - "0": The menu will be hidden when the window is in a fullscreen space, and not accessible by moving the mouse to the top of the screen.
+ * - "1": The menu will be accessible when the window is in a fullscreen space.
+ * - "auto": The menu will be hidden if fullscreen mode was toggled on programmatically via `SDL_SetWindowFullscreen()`,
+ * and accessible if fullscreen was entered via the "fullscreen" button on the window
+ * title bar. (default)
+ *
+ * This hint can be set anytime.
+ *
+ * \since This hint is available since SDL 3.1.9.
+ */
+#define SDL_HINT_VIDEO_MAC_FULLSCREEN_MENU_VISIBILITY "SDL_VIDEO_MAC_FULLSCREEN_MENU_VISIBILITY"
+
/**
* A variable controlling whether fullscreen windows are minimized when they
* lose focus.
diff --git a/src/video/cocoa/SDL_cocoavideo.m b/src/video/cocoa/SDL_cocoavideo.m
index 6f25de1e6e1fb..7d595e1366ada 100644
--- a/src/video/cocoa/SDL_cocoavideo.m
+++ b/src/video/cocoa/SDL_cocoavideo.m
@@ -218,6 +218,7 @@ static bool Cocoa_VideoInit(SDL_VideoDevice *_this)
data.allow_spaces = SDL_GetHintBoolean(SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES, true);
data.trackpad_is_touch_only = SDL_GetHintBoolean(SDL_HINT_TRACKPAD_IS_TOUCH_ONLY, false);
+ SDL_AddHintCallback(SDL_HINT_VIDEO_MAC_FULLSCREEN_MENU_VISIBILITY, Cocoa_MenuVisibilityCallback, NULL);
data.swaplock = SDL_CreateMutex();
if (!data.swaplock) {
diff --git a/src/video/cocoa/SDL_cocoawindow.h b/src/video/cocoa/SDL_cocoawindow.h
index fa46cbb37066f..af567b8af2899 100644
--- a/src/video/cocoa/SDL_cocoawindow.h
+++ b/src/video/cocoa/SDL_cocoawindow.h
@@ -142,6 +142,7 @@ typedef enum
@property(nonatomic) NSView *sdlContentView;
@property(nonatomic) NSMutableArray *nscontexts;
@property(nonatomic) BOOL in_blocking_transition;
+@property(nonatomic) BOOL fullscreen_space_requested;
@property(nonatomic) BOOL was_zoomed;
@property(nonatomic) NSInteger window_number;
@property(nonatomic) NSInteger flash_request;
@@ -192,4 +193,6 @@ extern bool Cocoa_SetWindowModal(SDL_VideoDevice *_this, SDL_Window *window, boo
extern bool Cocoa_SetWindowParent(SDL_VideoDevice *_this, SDL_Window *window, SDL_Window *parent);
extern bool Cocoa_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window);
+extern void Cocoa_MenuVisibilityCallback(void *userdata, const char *name, const char *oldValue, const char *newValue);
+
#endif // SDL_cocoawindow_h_
diff --git a/src/video/cocoa/SDL_cocoawindow.m b/src/video/cocoa/SDL_cocoawindow.m
index 9ae7b16350612..01fdc7b422df4 100644
--- a/src/video/cocoa/SDL_cocoawindow.m
+++ b/src/video/cocoa/SDL_cocoawindow.m
@@ -404,6 +404,61 @@ static CGFloat SqDistanceToRect(const NSPoint *point, const NSRect *rect)
return screen;
}
+bool Cocoa_IsWindowInFullscreenSpace(SDL_Window *window)
+{
+ @autoreleasepool {
+ SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->internal;
+
+ if ([data.listener isInFullscreenSpace]) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
+
+typedef enum CocoaMenuVisibility
+{
+ COCOA_MENU_VISIBILITY_AUTO = 0,
+ COCOA_MENU_VISIBILITY_NEVER,
+ COCOA_MENU_VISIBILITY_ALWAYS
+} CocoaMenuVisibility;
+
+static CocoaMenuVisibility menu_visibility_hint = COCOA_MENU_VISIBILITY_AUTO;
+
+static void Cocoa_ToggleFullscreenSpaceMenuVisibility(SDL_Window *window)
+{
+ if (window && Cocoa_IsWindowInFullscreenSpace(window)) {
+ SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->internal;
+
+ // 'Auto' sets the menu to visible if fullscreen wasn't explicitly entered via SDL_SetWindowFullscreen().
+ if ((menu_visibility_hint == COCOA_MENU_VISIBILITY_AUTO && !data.fullscreen_space_requested) ||
+ menu_visibility_hint == COCOA_MENU_VISIBILITY_ALWAYS) {
+ [NSMenu setMenuBarVisible:YES];
+ } else {
+ [NSMenu setMenuBarVisible:NO];
+ }
+ }
+}
+
+void Cocoa_MenuVisibilityCallback(void *userdata, const char *name, const char *oldValue, const char *newValue)
+{
+ if (newValue) {
+ if (*newValue == '0' || SDL_strcasecmp(newValue, "false") == 0) {
+ menu_visibility_hint = COCOA_MENU_VISIBILITY_NEVER;
+ } else if (*newValue == '1' || SDL_strcasecmp(newValue, "true") == 0) {
+ menu_visibility_hint = COCOA_MENU_VISIBILITY_ALWAYS;
+ } else {
+ menu_visibility_hint = COCOA_MENU_VISIBILITY_AUTO;
+ }
+ } else {
+ menu_visibility_hint = COCOA_MENU_VISIBILITY_AUTO;
+ }
+
+ // Update the current menu visibility.
+ Cocoa_ToggleFullscreenSpaceMenuVisibility(SDL_GetKeyboardFocus());
+}
+
static NSScreen *ScreenForRect(const NSRect *rect)
{
NSPoint center = NSMakePoint(NSMidX(*rect), NSMidY(*rect));
@@ -1195,7 +1250,7 @@ - (void)windowDidBecomeKey:(NSNotification *)aNotification
Cocoa_CheckClipboardUpdate(_data.videodata);
if (isFullscreenSpace && !window->fullscreen_exclusive) {
- [NSMenu setMenuBarVisible:NO];
+ Cocoa_ToggleFullscreenSpaceMenuVisibility(window);
}
{
const unsigned int newflags = [NSEvent modifierFlags] & NSEventModifierFlagCapsLock;
@@ -1307,9 +1362,7 @@ - (void)windowDidEnterFullScreen:(NSNotification *)aNotification
if ([self windowOperationIsPending:PENDING_OPERATION_LEAVE_FULLSCREEN]) {
[self setFullscreenSpace:NO];
} else {
- if (window->fullscreen_exclusive) {
- [NSMenu setMenuBarVisible:NO];
- }
+ Cocoa_ToggleFullscreenSpaceMenuVisibility(window);
/* Don't recurse back into UpdateFullscreenMode() if this was hit in
* a blocking transition, as the caller is already waiting in
@@ -1380,6 +1433,7 @@ - (void)windowDidExitFullScreen:(NSNotification *)aNotification
NSWindow *nswindow = _data.nswindow;
inFullscreenTransition = NO;
+ _data.fullscreen_space_requested = NO;
/* As of macOS 10.15, the window decorations can go missing sometimes after
certain fullscreen-desktop->exlusive-fullscreen->windowed mode flows
@@ -3030,25 +3084,15 @@ void Cocoa_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window)
}
}
-bool Cocoa_IsWindowInFullscreenSpace(SDL_Window *window)
-{
- @autoreleasepool {
- SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->internal;
-
- if ([data.listener isInFullscreenSpace]) {
- return true;
- } else {
- return false;
- }
- }
-}
-
bool Cocoa_SetWindowFullscreenSpace(SDL_Window *window, bool state, bool blocking)
{
@autoreleasepool {
bool succeeded = false;
SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->internal;
+ if (state) {
+ data.fullscreen_space_requested = YES;
+ }
data.in_blocking_transition = blocking;
if ([data.listener setFullscreenSpace:(state ? YES : NO)]) {
if (blocking) {