Commit 4c1322f693bf880f4d1011a2d13c46ff38c6dc81

Sam Lantinga 2013-11-11T02:53:00

Added support for new style fullscreen transitions on Mac OS X

diff --git a/src/video/cocoa/SDL_cocoaevents.m b/src/video/cocoa/SDL_cocoaevents.m
index e6b292b..30a63d8 100644
--- a/src/video/cocoa/SDL_cocoaevents.m
+++ b/src/video/cocoa/SDL_cocoaevents.m
@@ -147,6 +147,7 @@ CreateApplicationMenus(void)
     NSMenu *appleMenu;
     NSMenu *serviceMenu;
     NSMenu *windowMenu;
+    NSMenu *viewMenu;
     NSMenuItem *menuItem;
 
     if (NSApp == nil) {
@@ -220,6 +221,25 @@ CreateApplicationMenus(void)
     /* Tell the application object that this is now the window menu */
     [NSApp setWindowsMenu:windowMenu];
     [windowMenu release];
+
+
+    /* Add the fullscreen view toggle menu option, if supported */
+    if ([NSApp respondsToSelector:@selector(setPresentationOptions:)]) {
+        /* Create the view menu */
+        viewMenu = [[NSMenu alloc] initWithTitle:@"View"];
+
+        /* Add menu items */
+        menuItem = [viewMenu addItemWithTitle:@"Toggle Full Screen" action:@selector(toggleFullScreen:) keyEquivalent:@"f"];
+        [menuItem setKeyEquivalentModifierMask:NSControlKeyMask | NSCommandKeyMask];
+
+        /* Put menu into the menubar */
+        menuItem = [[NSMenuItem alloc] initWithTitle:@"View" action:nil keyEquivalent:@""];
+        [menuItem setSubmenu:viewMenu];
+        [[NSApp mainMenu] addItem:menuItem];
+        [menuItem release];
+
+        [viewMenu release];
+    }
 }
 
 void
diff --git a/src/video/cocoa/SDL_cocoawindow.h b/src/video/cocoa/SDL_cocoawindow.h
index c08c55c..1ec3d3c 100644
--- a/src/video/cocoa/SDL_cocoawindow.h
+++ b/src/video/cocoa/SDL_cocoawindow.h
@@ -32,11 +32,14 @@ typedef struct SDL_WindowData SDL_WindowData;
     BOOL observingVisible;
     BOOL wasCtrlLeft;
     BOOL wasVisible;
+    BOOL isFullscreen;
+    BOOL inFullscreenTransition;
 }
 
 -(void) listen:(SDL_WindowData *) data;
 -(void) pauseVisibleObservation;
 -(void) resumeVisibleObservation;
+-(BOOL) isToggledFullscreen;
 -(void) close;
 
 /* Window delegate functionality */
@@ -48,6 +51,10 @@ typedef struct SDL_WindowData SDL_WindowData;
 -(void) windowDidDeminiaturize:(NSNotification *) aNotification;
 -(void) windowDidBecomeKey:(NSNotification *) aNotification;
 -(void) windowDidResignKey:(NSNotification *) aNotification;
+-(void) windowWillEnterFullScreen:(NSNotification *) aNotification;
+-(void) windowDidEnterFullScreen:(NSNotification *) aNotification;
+-(void) windowWillExitFullScreen:(NSNotification *) aNotification;
+-(void) windowDidExitFullScreen:(NSNotification *) aNotification;
 
 /* Window event handling */
 -(void) mouseDown:(NSEvent *) theEvent;
diff --git a/src/video/cocoa/SDL_cocoawindow.m b/src/video/cocoa/SDL_cocoawindow.m
index 00ee08f..91cc7d1 100644
--- a/src/video/cocoa/SDL_cocoawindow.m
+++ b/src/video/cocoa/SDL_cocoawindow.m
@@ -50,7 +50,8 @@ static void ConvertNSRect(NSRect *r)
     r->origin.y = CGDisplayPixelsHigh(kCGDirectMainDisplay) - r->origin.y - r->size.height;
 }
 
-static void ScheduleContextUpdates(SDL_WindowData *data)
+static void
+ScheduleContextUpdates(SDL_WindowData *data)
 {
     NSMutableArray *contexts = data->nscontexts;
     @synchronized (contexts) {
@@ -60,12 +61,34 @@ static void ScheduleContextUpdates(SDL_WindowData *data)
     }
 }
 
-static int GetHintCtrlClickEmulateRightClick()
+static int
+GetHintCtrlClickEmulateRightClick()
 {
 	const char *hint = SDL_GetHint( SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK );
 	return hint != NULL && *hint != '0';
 }
 
+static unsigned int
+GetWindowStyle(SDL_Window * window)
+{
+    unsigned int style;
+
+    if (window->flags & SDL_WINDOW_FULLSCREEN) {
+        style = NSBorderlessWindowMask;
+    } else {
+        if (window->flags & SDL_WINDOW_BORDERLESS) {
+            style = NSBorderlessWindowMask;
+        } else {
+            style = (NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask);
+        }
+        if (window->flags & SDL_WINDOW_RESIZABLE) {
+            style |= NSResizableWindowMask;
+        }
+    }
+    return style;
+}
+
+
 @implementation Cocoa_WindowListener
 
 - (void)listen:(SDL_WindowData *)data
@@ -78,6 +101,8 @@ static int GetHintCtrlClickEmulateRightClick()
     observingVisible = YES;
     wasCtrlLeft = NO;
     wasVisible = [window isVisible];
+    isFullscreen = NO;
+    inFullscreenTransition = NO;
 
     center = [NSNotificationCenter defaultCenter];
 
@@ -89,6 +114,10 @@ static int GetHintCtrlClickEmulateRightClick()
         [center addObserver:self selector:@selector(windowDidDeminiaturize:) name:NSWindowDidDeminiaturizeNotification object:window];
         [center addObserver:self selector:@selector(windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification object:window];
         [center addObserver:self selector:@selector(windowDidResignKey:) name:NSWindowDidResignKeyNotification object:window];
+        [center addObserver:self selector:@selector(windowWillEnterFullScreen:) name:NSWindowWillEnterFullScreenNotification object:window];
+        [center addObserver:self selector:@selector(windowDidEnterFullScreen:) name:NSWindowDidEnterFullScreenNotification object:window];
+        [center addObserver:self selector:@selector(windowWillExitFullScreen:) name:NSWindowWillExitFullScreenNotification object:window];
+        [center addObserver:self selector:@selector(windowDidExitFullScreen:) name:NSWindowDidExitFullScreenNotification object:window];
     } else {
         [window setDelegate:self];
     }
@@ -152,6 +181,11 @@ static int GetHintCtrlClickEmulateRightClick()
     }
 }
 
+- (BOOL) isToggledFullscreen
+{
+    return isFullscreen;
+}
+
 - (void)close
 {
     NSNotificationCenter *center;
@@ -169,6 +203,10 @@ static int GetHintCtrlClickEmulateRightClick()
         [center removeObserver:self name:NSWindowDidDeminiaturizeNotification object:window];
         [center removeObserver:self name:NSWindowDidBecomeKeyNotification object:window];
         [center removeObserver:self name:NSWindowDidResignKeyNotification object:window];
+        [center removeObserver:self name:NSWindowWillEnterFullScreenNotification object:window];
+        [center removeObserver:self name:NSWindowDidEnterFullScreenNotification object:window];
+        [center removeObserver:self name:NSWindowWillExitFullScreenNotification object:window];
+        [center removeObserver:self name:NSWindowDidExitFullScreenNotification object:window];
     } else {
         [window setDelegate:nil];
     }
@@ -250,8 +288,15 @@ static int GetHintCtrlClickEmulateRightClick()
     y = (int)rect.origin.y;
     w = (int)rect.size.width;
     h = (int)rect.size.height;
-    if (SDL_IsShapedWindow(_data->window))
+
+    if (inFullscreenTransition) {
+        /* We'll take care of this at the end of the transition */
+        return;
+    }
+
+    if (SDL_IsShapedWindow(_data->window)) {
         Cocoa_ResizeWindowShape(_data->window);
+    }
 
     ScheduleContextUpdates(_data);
 
@@ -317,6 +362,46 @@ static int GetHintCtrlClickEmulateRightClick()
     }
 }
 
+- (void)windowWillEnterFullScreen:(NSNotification *)aNotification
+{
+    SDL_Window *window = _data->window;
+    NSWindow *nswindow = _data->nswindow;
+
+    if (!(window->flags & SDL_WINDOW_RESIZABLE)) {
+        if ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) {
+            [nswindow setStyleMask:(NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask|NSResizableWindowMask)];
+        } else {
+            [nswindow setStyleMask:NSBorderlessWindowMask];
+        }
+    }
+    isFullscreen = YES;
+    inFullscreenTransition = YES;
+}
+
+- (void)windowDidEnterFullScreen:(NSNotification *)aNotification
+{
+    inFullscreenTransition = NO;
+    [self windowDidResize:aNotification];
+}
+
+- (void)windowWillExitFullScreen:(NSNotification *)aNotification
+{
+    inFullscreenTransition = YES;
+}
+
+- (void)windowDidExitFullScreen:(NSNotification *)aNotification
+{
+    SDL_Window *window = _data->window;
+    NSWindow *nswindow = _data->nswindow;
+
+    if (!(window->flags & SDL_WINDOW_RESIZABLE)) {
+        [nswindow setStyleMask:GetWindowStyle(window)];
+    }
+    isFullscreen = NO;
+    inFullscreenTransition = NO;
+    [self windowDidResize:aNotification];
+}
+
 /* We'll respond to key events by doing nothing so we don't beep.
  * We could handle key messages here, but we lose some in the NSApp dispatch,
  * where they get converted to action messages, etc.
@@ -606,26 +691,6 @@ static int GetHintCtrlClickEmulateRightClick()
 }
 @end
 
-static unsigned int
-GetWindowStyle(SDL_Window * window)
-{
-    unsigned int style;
-
-    if (window->flags & SDL_WINDOW_FULLSCREEN) {
-        style = NSBorderlessWindowMask;
-    } else {
-        if (window->flags & SDL_WINDOW_BORDERLESS) {
-            style = NSBorderlessWindowMask;
-        } else {
-            style = (NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask);
-        }
-        if (window->flags & SDL_WINDOW_RESIZABLE) {
-            style |= NSResizableWindowMask;
-        }
-    }
-    return style;
-}
-
 static int
 SetupWindowData(_THIS, SDL_Window * window, NSWindow *nswindow, SDL_bool created)
 {
@@ -758,6 +823,7 @@ Cocoa_CreateWindow(_THIS, SDL_Window * window)
         return -1;
     }
     [nswindow setBackgroundColor:[NSColor blackColor]];
+    [nswindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
 
     /* Create a default view for this window */
     rect = [nswindow contentRectForFrameRect:[nswindow frame]];
@@ -1020,10 +1086,45 @@ Cocoa_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered)
     [pool release];
 }
 
-void
-Cocoa_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
+static SDL_bool
+Cocoa_CanToggleFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
+{
+    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
+    NSWindow *nswindow = data->nswindow;
+
+    if (![nswindow respondsToSelector: @selector(toggleFullScreen:)]) {
+        return SDL_FALSE;
+    }
+
+    /* We can enter new style fullscreen mode for "fullscreen desktop" */
+    if ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) {
+        return SDL_TRUE;
+    }
+
+    /* We can always leave new style fullscreen mode */
+    if (!fullscreen && [data->listener isToggledFullscreen]) {
+        return SDL_TRUE;
+    }
+
+    /* Requesting a mode switched fullscreen mode */
+    return SDL_FALSE;
+}
+
+static void
+Cocoa_SetWindowFullscreen_NewStyle(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
+{
+    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
+    NSWindow *nswindow = data->nswindow;
+ 
+    if (fullscreen != [data->listener isToggledFullscreen]) {
+        [nswindow toggleFullScreen:nil];
+    }
+    ScheduleContextUpdates(data);
+}
+
+static void
+Cocoa_SetWindowFullscreen_OldStyle(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
 {
-    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
     NSWindow *nswindow = data->nswindow;
     NSRect rect;
@@ -1097,6 +1198,18 @@ Cocoa_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display
     }
 
     ScheduleContextUpdates(data);
+}
+
+void
+Cocoa_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
+{
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+    if (Cocoa_CanToggleFullscreen(_this, window, display, fullscreen)) {
+        Cocoa_SetWindowFullscreen_NewStyle(_this, window, display, fullscreen);
+    } else {
+        Cocoa_SetWindowFullscreen_OldStyle(_this, window, display, fullscreen);
+    }
 
     [pool release];
 }