From 4c1322f693bf880f4d1011a2d13c46ff38c6dc81 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 11 Nov 2013 02:53:00 -0800 Subject: [PATCH] Added support for new style fullscreen transitions on Mac OS X --- src/video/cocoa/SDL_cocoaevents.m | 20 ++++ src/video/cocoa/SDL_cocoawindow.h | 7 ++ src/video/cocoa/SDL_cocoawindow.m | 165 +++++++++++++++++++++++++----- 3 files changed, 166 insertions(+), 26 deletions(-) diff --git a/src/video/cocoa/SDL_cocoaevents.m b/src/video/cocoa/SDL_cocoaevents.m index e6b292b0bda57..30a63d8f74992 100644 --- a/src/video/cocoa/SDL_cocoaevents.m +++ b/src/video/cocoa/SDL_cocoaevents.m @@ -147,6 +147,7 @@ - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filenam NSMenu *appleMenu; NSMenu *serviceMenu; NSMenu *windowMenu; + NSMenu *viewMenu; NSMenuItem *menuItem; if (NSApp == nil) { @@ -220,6 +221,25 @@ - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filenam /* 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 c08c55c11b8f9..1ec3d3c5440bf 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 00ee08f3250c7..91cc7d15e299f 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 @@ - (void)listen:(SDL_WindowData *)data observingVisible = YES; wasCtrlLeft = NO; wasVisible = [window isVisible]; + isFullscreen = NO; + inFullscreenTransition = NO; center = [NSNotificationCenter defaultCenter]; @@ -89,6 +114,10 @@ - (void)listen:(SDL_WindowData *)data [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 @@ -(void) resumeVisibleObservation } } +- (BOOL) isToggledFullscreen +{ + return isFullscreen; +} + - (void)close { NSNotificationCenter *center; @@ -169,6 +203,10 @@ - (void)close [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 @@ - (void)windowDidResize:(NSNotification *)aNotification 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 @@ - (void)windowDidResignKey:(NSNotification *)aNotification } } +- (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 @@ - (void)resetCursorRects } @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 @@ - (void)resetCursorRects 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 @@ - (void)resetCursorRects [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 @@ - (void)resetCursorRects } 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]; }