Line data Source code
1 0 : /* 2 : 3 : OOMacSnowLeopardFullScreenController.m 4 : 5 : 6 : Oolite 7 : Copyright (C) 2004-2013 Giles C Williams and contributors 8 : 9 : This program is free software; you can redistribute it and/or 10 : modify it under the terms of the GNU General Public License 11 : as published by the Free Software Foundation; either version 2 12 : of the License, or (at your option) any later version. 13 : 14 : This program is distributed in the hope that it will be useful, 15 : but WITHOUT ANY WARRANTY; without even the implied warranty of 16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 : GNU General Public License for more details. 18 : 19 : You should have received a copy of the GNU General Public License 20 : along with this program; if not, write to the Free Software 21 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 22 : MA 02110-1301, USA. 23 : 24 : */ 25 : 26 : #import "OOMacSnowLeopardFullScreenController.h" 27 : 28 : #if OOLITE_MAC_OS_X && OOLITE_64_BIT 29 : 30 : 31 : #import <Carbon/Carbon.h> // For SetSystemUIMode() 32 : #import "MyOpenGLView.h" 33 : #import "OOLogging.h" 34 : #import "OOFullScreenWindow.h" 35 : #import "OOCollectionExtractors.h" 36 : 37 : 38 : #define SUPPRESS_BLANKING_WINDOWS ( 1 && OO_DEBUG) 39 : 40 : 41 : @interface OOMacSnowLeopardFullScreenController () 42 : 43 : @property (nonatomic, retain) NSWindow *fullScreenWindow; // Window on main gameplay screen which contains our OpenGL view in full screen mode. 44 : @property (nonatomic, retain) NSArray *blankingWindows; // Plain black windows covering other screens if relevant. 45 : @property (nonatomic, retain) NSWindow *standardWindow; // The main game window, stashed away for use when existing full screen mode. 46 : 47 : @property (nonatomic, retain) NSScreen *gameScreen; 48 : 49 : - (void) beginFullScreenMode; 50 : - (void) endFullScreenMode; 51 : 52 : - (void) screenParametersChanged:(NSNotification *)notification; 53 : 54 : - (void) setUpBlankingWindowsForScreensOtherThan:(NSScreen *)gameScreen; 55 : - (void) removeBlankingWindows; 56 : 57 : @end 58 : 59 : 60 : @implementation OOMacSnowLeopardFullScreenController 61 : 62 : @synthesize fullScreenMode = _fullScreenMode; // Future note: needs to be explicit because property declaration is inherited. 63 : @synthesize fullScreenWindow = _fullScreenWindow; 64 : @synthesize blankingWindows = _blankingWindows; 65 : @synthesize standardWindow = _standardWindow; 66 : @synthesize gameScreen = _gameScreen; 67 : 68 : 69 : - (void) dealloc 70 : { 71 : self.fullScreenMode = NO; 72 : 73 : DESTROY(_fullScreenWindow); 74 : DESTROY(_blankingWindows); 75 : DESTROY(_standardWindow); 76 : DESTROY(_gameScreen); 77 : 78 : [super dealloc]; 79 : } 80 : 81 : 82 : - (void) setFullScreenMode:(BOOL)value 83 : { 84 : if (!value && self.fullScreenMode) 85 : { 86 : [self endFullScreenMode]; 87 : } 88 : else if (value && !self.fullScreenMode) 89 : { 90 : [self beginFullScreenMode]; 91 : } 92 : } 93 : 94 : 95 : - (NSArray *) displayModes 96 : { 97 : NSSize size = self.fullScreenWindow.frame.size; 98 : NSDictionary *fakeMode = [NSDictionary dictionaryWithObjectsAndKeys: 99 : [NSNumber numberWithUnsignedInt:size.width], kOODisplayWidth, 100 : [NSNumber numberWithUnsignedInt:size.height], kOODisplayHeight, 101 : nil]; 102 : return [NSArray arrayWithObject:fakeMode]; 103 : } 104 : 105 : 106 : - (NSUInteger) indexOfCurrentDisplayMode 107 : { 108 : return 0; 109 : } 110 : 111 : 112 : - (BOOL) setDisplayWidth:(NSUInteger)width height:(NSUInteger)height refreshRate:(NSUInteger)refresh 113 : { 114 : return NO; 115 : } 116 : 117 : 118 : - (NSDictionary *) findDisplayModeForWidth:(NSUInteger)width height:(NSUInteger)height refreshRate:(NSUInteger)refresh 119 : { 120 : NSDictionary *fakeMode = [self.displayModes objectAtIndex:0]; 121 : if (width == [fakeMode oo_unsignedIntegerForKey:kOODisplayWidth] && 122 : height == [fakeMode oo_unsignedIntegerForKey:kOODisplayHeight]) 123 : { 124 : return fakeMode; 125 : } 126 : return nil; 127 : } 128 : 129 : 130 : - (void) noteMouseInteractionModeChangedFrom:(OOMouseInteractionMode)oldMode to:(OOMouseInteractionMode)newMode 131 : { 132 : NSAssert(self.fullScreenMode, @"%s called in wrong state.", __FUNCTION__); 133 : 134 : [self.gameView noteMouseInteractionModeChangedFrom:oldMode to:newMode]; 135 : } 136 : 137 : 138 : #pragma mark - Actual full screen mode handling 139 : 140 : - (void) beginFullScreenMode 141 : { 142 : NSAssert(!self.fullScreenMode, @"%s called in wrong state.", __FUNCTION__); 143 : 144 : // Stash the windowed-mode window so we can restore to it later. 145 : self.standardWindow = self.gameView.window; 146 : 147 : /* 148 : Set up a full-screen window. Based on OpenGL Programming Guide for Mac 149 : [developer.apple.com], dated 2012-07-23, "Drawing to the Full Screen". 150 : */ 151 : self.gameScreen = self.gameView.window.screen; 152 : NSRect frame = self.gameScreen.frame; 153 : OOFullScreenWindow *window = [[OOFullScreenWindow alloc] initWithContentRect:frame 154 : styleMask:NSBorderlessWindowMask 155 : backing:NSBackingStoreBuffered 156 : defer:NO]; 157 : if (window == nil) return; 158 : 159 : self.fullScreenWindow = [window autorelease]; 160 : 161 : [window setOpaque:YES]; 162 : [window setMovable:YES]; 163 : window.canBecomeKeyWindow = YES; 164 : window.canBecomeMainWindow = YES; 165 : window.acceptsMouseMovedEvents = YES; 166 : #if !OO_DEBUG 167 : /* 168 : Leaving a full-screen window visible in the background is anti-social, 169 : but convenient in debug builds. 170 : */ 171 : window.hidesOnDeactivate = YES; 172 : #endif 173 : 174 : // TODO: handle screen reconfiguration. 175 : 176 : [self.standardWindow orderOut:nil]; 177 : window.contentView = self.gameView; 178 : 179 : SetSystemUIMode(kUIModeAllSuppressed, kUIOptionDisableMenuBarTransparency); 180 : [self setUpBlankingWindowsForScreensOtherThan:self.gameScreen]; 181 : 182 : [window makeKeyAndOrderFront:self]; 183 : 184 : _fullScreenMode = YES; 185 : 186 : // Subscribe to reconfiguration notifications. 187 : [[NSNotificationCenter defaultCenter] addObserver:self 188 : selector:@selector(screenParametersChanged:) 189 : name:NSApplicationDidChangeScreenParametersNotification 190 : object:NSApp]; 191 : } 192 : 193 : 194 : - (void) endFullScreenMode 195 : { 196 : NSAssert(self.fullScreenMode, @"%s called in wrong state.", __FUNCTION__); 197 : 198 : [self.fullScreenWindow orderOut:nil]; 199 : 200 : [self removeBlankingWindows]; 201 : SetSystemUIMode(kUIModeNormal, 0); 202 : 203 : self.standardWindow.contentView = self.gameView; 204 : [self.standardWindow makeKeyAndOrderFront:nil]; 205 : 206 : self.standardWindow = nil; 207 : self.fullScreenWindow = nil; 208 : self.gameScreen = nil; 209 : 210 : _fullScreenMode = NO; 211 : 212 : // Unsubscribe from notifications. 213 : [[NSNotificationCenter defaultCenter] removeObserver:self 214 : name:NSApplicationDidChangeScreenParametersNotification 215 : object:NSApp]; 216 : } 217 : 218 : 219 : - (void) screenParametersChanged:(NSNotification *)notification 220 : { 221 : NSAssert(self.fullScreenMode, @"%s called in wrong state.", __FUNCTION__); 222 : 223 : [self.fullScreenWindow setFrame:self.gameScreen.frame display:YES]; 224 : [self setUpBlankingWindowsForScreensOtherThan:self.gameScreen]; 225 : } 226 : 227 : 228 : - (void) setUpBlankingWindowsForScreensOtherThan:(NSScreen *)gameScreen 229 : { 230 : #if SUPPRESS_BLANKING_WINDOWS 231 : // Skip blanking windows while debugging. 232 : return; 233 : #endif 234 : 235 : /* 236 : On muliple-screen systems, fill all screens except the game screen 237 : with an all-black window. This behaviour has its critics, but it is 238 : consistent with both traditional Oolite behaviour and Mac OS X 10.7 239 : and later standard behaviour. 240 : */ 241 : 242 : // Remove any existing blanking windows. 243 : [self removeBlankingWindows]; 244 : 245 : NSArray *screens = [NSScreen screens]; 246 : if (screens.count <= 1) 247 : { 248 : // No blanking windows needed on single-screen systems. 249 : return; 250 : } 251 : 252 : NSMutableArray *windows = [NSMutableArray arrayWithCapacity:screens.count - 1]; 253 : for (NSScreen *screen in screens) 254 : { 255 : if ([screen isEqual:gameScreen]) continue; 256 : 257 : NSRect frame = screen.frame; 258 : OOFullScreenWindow *window = [[OOFullScreenWindow alloc] initWithContentRect:frame 259 : styleMask:NSBorderlessWindowMask 260 : backing:NSBackingStoreBuffered 261 : defer:NO]; 262 : 263 : [window setOpaque:YES]; 264 : [window setMovable:YES]; 265 : window.collectionBehavior = NSWindowCollectionBehaviorTransient | NSWindowCollectionBehaviorIgnoresCycle; 266 : window.canBecomeKeyWindow = NO; 267 : window.canBecomeMainWindow = NO; 268 : window.hidesOnDeactivate = YES; 269 : window.backgroundColor = [NSColor blackColor]; 270 : 271 : [windows addObject:window]; 272 : [window orderFront:nil]; 273 : [window release]; 274 : } 275 : 276 : self.blankingWindows = windows; 277 : } 278 : 279 : 280 : - (void) removeBlankingWindows 281 : { 282 : for (NSWindow *window in self.blankingWindows) 283 : { 284 : [window orderOut:nil]; 285 : } 286 : self.blankingWindows = nil; 287 : } 288 : 289 : 290 : - (void) checkWindowVisible:(NSTimer *)timer 291 : { 292 : NSWindow *window = timer.userInfo; 293 : OOLog(@"temp.fullScreen", @"Window %@ is %@ on screen %@", window, window.isVisible ? @"visible" : @"INVISIBLE", window.screen); 294 : } 295 : 296 : @end 297 : 298 : #endif