00001 00002 // 00003 // SFML - Simple and Fast Multimedia Library 00004 // Copyright (C) 2007-2009 Lucas Soltic (ceylow@gmail.com) and Laurent Gomila (laurent.gom@gmail.com) 00005 // 00006 // This software is provided 'as-is', without any express or implied warranty. 00007 // In no event will the authors be held liable for any damages arising from the use of this software. 00008 // 00009 // Permission is granted to anyone to use this software for any purpose, 00010 // including commercial applications, and to alter it and redistribute it freely, 00011 // subject to the following restrictions: 00012 // 00013 // 1. The origin of this software must not be misrepresented; 00014 // you must not claim that you wrote the original software. 00015 // If you use this software in a product, an acknowledgment 00016 // in the product documentation would be appreciated but is not required. 00017 // 00018 // 2. Altered source versions must be plainly marked as such, 00019 // and must not be misrepresented as being the original software. 00020 // 00021 // 3. This notice may not be removed or altered from any source distribution. 00022 // 00024 00025 00027 // Headers 00029 #import <SFML/Window/Cocoa/AppController.h> 00030 #import <SFML/Window/Cocoa/GLKit.h> 00031 #import <SFML/System.hpp> 00032 #import <ApplicationServices/ApplicationServices.h> 00033 #import <iostream> 00034 00035 00036 // AppController singleton object 00037 static AppController *shared = nil; 00038 00039 00040 /* setAppleMenu disappeared from the headers in 10.4 */ 00041 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 00042 @interface NSApplication (SFML) 00043 - (void)setAppleMenu:(NSMenu *)menu; 00044 @end 00045 #endif 00046 00047 #define ENABLE_FADE_OPERATIONS 1 00048 00049 @implementation NSApplication (SFML) 00050 00051 - (void)setRunning:(BOOL)flag 00052 { 00053 // Note: _running is a short, not a BOOL 00054 if (flag) 00055 _running = 1; 00056 else 00057 _running = 0; 00058 } 00059 00060 @end 00061 00062 00063 @implementation AppController 00064 00065 00072 - (id)init 00073 { 00074 self = [super init]; 00075 00076 if (self != nil) { 00077 myOwningEventLoop = NO; 00078 00079 // Save the desktop mode 00080 myDesktopMode = sf::VideoMode::GetDesktopMode(); 00081 myPrevMode = myDesktopMode; 00082 00083 // Make the app autorelease pool 00084 myMainPool = [[NSAutoreleasePool alloc] init]; 00085 00086 // Don't go on if the user handles the app 00087 if (![NSApp isRunning]) 00088 { 00089 // Force our application to appear in the Dock and make it able 00090 // to get focus (even when it's a raw executable) 00091 ProcessSerialNumber psn; 00092 00093 if (!GetCurrentProcess(&psn)) { 00094 TransformProcessType(&psn, kProcessTransformToForegroundApplication); 00095 SetFrontProcess(&psn); 00096 } 00097 00098 // Make the app 00099 [NSApplication sharedApplication]; 00100 00101 NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; 00102 // I want to go back to the desktop mode 00103 // if we've a fullscreen window when hiding 00104 [nc addObserver:self 00105 selector:@selector(applicationWillHide:) 00106 name:NSApplicationWillHideNotification 00107 object:NSApp]; 00108 00109 // And restore de fullscreen mode when unhiding 00110 [nc addObserver:self 00111 selector:@selector(applicationWillUnhide:) 00112 name:NSApplicationWillUnhideNotification 00113 object:NSApp]; 00114 00115 // Go back to desktop mode before exit 00116 [nc addObserver:self 00117 selector:@selector(applicationWillTerminate:) 00118 name:NSApplicationWillTerminateNotification 00119 object:NSApp]; 00120 00121 if ([NSApp mainMenu] == nil) { 00122 [self makeMenuBar]; 00123 } 00124 } 00125 } 00126 00127 return self; 00128 } 00129 00130 00134 - (void)dealloc 00135 { 00136 [[NSNotificationCenter defaultCenter] removeObserver:self]; 00137 [myFullscreenWrapper release]; 00138 [super dealloc]; 00139 } 00140 00141 00145 + (AppController *)sharedController 00146 { 00147 if (nil == shared) 00148 shared = [[AppController alloc] init]; 00149 00150 return shared; 00151 } 00152 00153 00157 - (void)applicationWillHide:(NSNotification *)aNotification 00158 { 00159 if (myFullscreenWrapper) { 00160 myPrevMode = sf::VideoMode::GetDesktopMode(); 00161 00162 CFDictionaryRef displayMode = CGDisplayBestModeForParameters (kCGDirectMainDisplay, 00163 myDesktopMode.BitsPerPixel, 00164 myDesktopMode.Width, 00165 myDesktopMode.Height, 00166 NULL); 00167 00168 // Fade to black screen 00169 [self doFadeOperation:FillScreen time:0.2f sync:true]; 00170 00171 // Make the full screen window unvisible 00172 [[myFullscreenWrapper window] setAlphaValue:0.0f]; 00173 00174 // Switch to the wished display mode 00175 CGDisplaySwitchToMode(kCGDirectMainDisplay, displayMode); 00176 00177 // Fade to normal screen 00178 [self doFadeOperation:CleanScreen time:0.5f sync:false]; 00179 } 00180 } 00181 00182 00186 - (void)applicationWillUnhide:(NSNotification *)aNotification 00187 { 00188 if (myFullscreenWrapper) { 00189 CFDictionaryRef displayMode = CGDisplayBestModeForParameters (kCGDirectMainDisplay, 00190 myPrevMode.BitsPerPixel, 00191 myPrevMode.Width, 00192 myPrevMode.Height, 00193 NULL); 00194 00195 // Fade to a black screen 00196 [self doFadeOperation:FillScreen time:0.5f sync:true]; 00197 [NSMenu setMenuBarVisible:NO]; 00198 00199 // Switch to the wished display mode 00200 CGDisplaySwitchToMode(kCGDirectMainDisplay, displayMode); 00201 00202 // Show the fullscreen window if existing 00203 if (myFullscreenWrapper) 00204 { 00205 [[myFullscreenWrapper window] setAlphaValue:1.0f]; 00206 [[myFullscreenWrapper window] center]; 00207 } 00208 00209 // Fade to normal screen 00210 [self doFadeOperation:CleanScreen time:0.5f sync:false]; 00211 } 00212 } 00213 00214 00215 - (void)applicationWillTerminate:(NSNotification *)aNotification 00216 { 00217 if (myFullscreenWrapper) 00218 [self setFullscreenWindow:nil mode:NULL]; 00219 00220 // FIXME: should I really do this ? what about the user owned windows ? 00221 // And is this really useful as the application is about to exit ? 00222 [NSApp makeWindowsPerform:@selector(close) inOrder:NO]; 00223 } 00224 00228 - (void)makeMenuBar 00229 { 00230 // Source taken from SDL 1.3 00231 00232 NSString *appName = nil; 00233 NSString *title = nil; 00234 NSMenu *appleMenu = nil; 00235 NSMenu *fileMenu = nil; 00236 NSMenu *windowMenu = nil; 00237 NSMenuItem *menuItem = nil; 00238 NSMenuItem *quitMenuItem = nil; 00239 00240 // Determine the application name 00241 appName = [[[NSBundle mainBundle] infoDictionary] objectForKey: @"CFBundleName"]; 00242 00243 if (![appName length]) 00244 appName = [[NSProcessInfo processInfo] processName]; 00245 00246 00247 // Create the main menu bar 00248 [NSApp setMainMenu:[[NSMenu alloc] init]]; 00249 00250 // Create the application menu 00251 appleMenu = [[NSMenu alloc] initWithTitle:@""]; 00252 00253 // Put menu items 00254 // + 'About' menu item 00255 title = [@"About " stringByAppendingString:appName]; 00256 [appleMenu addItemWithTitle:title 00257 action:@selector(orderFrontStandardAboutPanel:) 00258 keyEquivalent:@""]; 00259 00260 [appleMenu addItem:[NSMenuItem separatorItem]]; 00261 00262 // + 'Hide' menu item 00263 title = [@"Hide " stringByAppendingString:appName]; 00264 [appleMenu addItemWithTitle:title 00265 action:@selector(hide:) 00266 keyEquivalent:@"h"]; 00267 00268 // + 'Hide other' menu item 00269 menuItem = reinterpret_cast <NSMenuItem *> ([appleMenu addItemWithTitle:@"Hide Others" 00270 action:@selector(hideOtherApplications:) 00271 keyEquivalent:@"h"]); 00272 [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)]; 00273 00274 // + 'Show all' menu item 00275 [appleMenu addItemWithTitle:@"Show All" 00276 action:@selector(unhideAllApplications:) 00277 keyEquivalent:@""]; 00278 00279 [appleMenu addItem:[NSMenuItem separatorItem]]; 00280 00281 // + 'Quit' menu item 00282 title = [@"Quit " stringByAppendingString:appName]; 00283 quitMenuItem = [[[NSMenuItem alloc] 00284 initWithTitle:title 00285 action:@selector(terminate:) 00286 keyEquivalent:@"q"] autorelease]; 00287 //[quitMenuItem setTarget:self]; 00288 [appleMenu addItem:quitMenuItem]; 00289 00290 // Put the menu into the menubar 00291 menuItem = [[NSMenuItem alloc] 00292 initWithTitle:@"" 00293 action:nil 00294 keyEquivalent:@""]; 00295 [menuItem setSubmenu:appleMenu]; 00296 [[NSApp mainMenu] addItem:menuItem]; 00297 [menuItem release]; 00298 00299 // Tell the application object that this is now the application menu 00300 [NSApp setAppleMenu:appleMenu]; 00301 [appleMenu release]; 00302 00303 // 'File' menu 00304 fileMenu = [[NSMenu alloc] 00305 initWithTitle:@"File"]; 00306 00307 // + 'Close' menu item 00308 menuItem = [[NSMenuItem alloc] 00309 initWithTitle:@"Close" 00310 action:@selector(performClose:) 00311 keyEquivalent:@"w"]; 00312 [fileMenu addItem:menuItem]; 00313 [menuItem release]; 00314 00315 // + 'File' menu item (head) 00316 menuItem = [[NSMenuItem alloc] 00317 initWithTitle:@"File" 00318 action:nil 00319 keyEquivalent:@""]; 00320 [menuItem setSubmenu:fileMenu]; 00321 [[NSApp mainMenu] addItem:menuItem]; 00322 [menuItem release]; 00323 00324 // 'Window' menu 00325 windowMenu = [[NSMenu alloc] 00326 initWithTitle:@"Window"]; 00327 00328 // + 'Minimize' menu item 00329 menuItem = [[NSMenuItem alloc] 00330 initWithTitle:@"Minimize" 00331 action:@selector(performMiniaturize:) 00332 keyEquivalent:@"m"]; 00333 [windowMenu addItem:menuItem]; 00334 [menuItem release]; 00335 00336 // + 'Window' menu item (head) 00337 menuItem = [[NSMenuItem alloc] 00338 initWithTitle:@"Window" 00339 action:nil keyEquivalent:@""]; 00340 [menuItem setSubmenu:windowMenu]; 00341 [[NSApp mainMenu] addItem:menuItem]; 00342 [menuItem release]; 00343 00344 // Tell the application object that this is now the window menu 00345 [NSApp setWindowsMenu:windowMenu]; 00346 [windowMenu release]; 00347 } 00348 00349 00355 - (void)processEvents 00356 { 00357 // Check there is a run loop 00358 if (![NSApp isRunning]) 00359 { 00360 // Get the ownershipt of event handling if not and run 00361 [NSApp finishLaunching]; 00362 [NSApp setRunning:YES]; 00363 myOwningEventLoop = YES; 00364 } 00365 00366 // Clean the autorelease pool 00367 [myMainPool release]; 00368 myMainPool = [[NSAutoreleasePool alloc] init]; 00369 00370 NSEvent *event = nil; 00371 00372 if (myOwningEventLoop) 00373 { 00374 // Minimal event loop 00375 while (nil != (event = [NSApp nextEventMatchingMask:NSAnyEventMask 00376 untilDate:nil 00377 inMode:NSDefaultRunLoopMode 00378 dequeue:YES])) 00379 { 00380 [NSApp sendEvent:event]; 00381 } 00382 } 00383 } 00384 00385 00390 - (void)setFullscreenWindow:(WindowWrapper *)aWrapper mode:(sf::VideoMode *)fullscreenMode 00391 { 00392 // If we have a fullscreen window and want to remove it 00393 if (aWrapper == nil && myFullscreenWrapper) 00394 { 00395 // Get the CoreGraphics display mode according to the desktop mode 00396 CFDictionaryRef displayMode = CGDisplayBestModeForParameters (kCGDirectMainDisplay, 00397 myDesktopMode.BitsPerPixel, 00398 myDesktopMode.Width, 00399 myDesktopMode.Height, 00400 NULL); 00401 00402 #if ENABLE_FADE_OPERATIONS 00403 // Fade to black screen 00404 [self doFadeOperation:FillScreen time:0.2f sync:true]; 00405 #endif 00406 00407 // Switch to the desktop display mode 00408 CGDisplaySwitchToMode(kCGDirectMainDisplay, displayMode); 00409 00410 // Close the window 00411 [[myFullscreenWrapper window] close]; 00412 00413 // Show the menu bar 00414 [NSMenu setMenuBarVisible:YES]; 00415 00416 #if ENABLE_FADE_OPERATIONS 00417 // Fade to normal screen 00418 [self doFadeOperation:CleanScreen time:0.5f sync:true]; 00419 #endif 00420 00421 // Release the saved window wrapper 00422 myFullscreenWrapper = nil; 00423 } 00424 else if (aWrapper) 00425 { 00426 assert(fullscreenMode != NULL); 00427 00428 // Get the CoreGraphics display mode according to the given sf mode 00429 CFDictionaryRef displayMode = CGDisplayBestModeForParameters (kCGDirectMainDisplay, 00430 fullscreenMode->BitsPerPixel, 00431 fullscreenMode->Width, 00432 fullscreenMode->Height, 00433 NULL); 00434 00435 #if ENABLE_FADE_OPERATIONS 00436 // Fade to a black screen 00437 [self doFadeOperation:FillScreen time:0.5f sync:true]; 00438 #endif 00439 00440 if (!myFullscreenWrapper) 00441 { 00442 // Hide the main menu bar 00443 [NSMenu setMenuBarVisible:NO]; 00444 } 00445 00446 if (myPrevMode != *fullscreenMode) 00447 { 00448 // Switch to the wished display mode 00449 CGDisplaySwitchToMode(kCGDirectMainDisplay, displayMode); 00450 } 00451 00452 if (myFullscreenWrapper) 00453 { 00454 [[myFullscreenWrapper window] close]; 00455 } 00456 00457 // Open and center the window 00458 [[aWrapper window] makeKeyAndOrderFront:nil]; 00459 [[aWrapper window] center]; 00460 00461 #if ENABLE_FADE_OPERATIONS 00462 // Fade to normal screen 00463 [self doFadeOperation:CleanScreen time:0.2f sync:false]; 00464 #endif 00465 00466 // Save the fullscreen wrapper 00467 myFullscreenWrapper = aWrapper; 00468 } 00469 else 00470 { 00471 std::cerr << "Inconcistency error for arguments given to -[AppController setFullscreenWindow:mode:]" << std::endl; 00472 } 00473 } 00474 00475 00484 - (void) doFadeOperation:(int)operation time:(float)time sync:(bool)sync 00485 { 00486 static CGDisplayFadeReservationToken prevToken = kCGDisplayFadeReservationInvalidToken; 00487 CGDisplayFadeReservationToken token = prevToken; 00488 00489 CGError result = 0, capture = 0; 00490 00491 if (operation == FillScreen) { 00492 // Get access for the fade operation 00493 result = CGAcquireDisplayFadeReservation((int)(3 + time), &token); 00494 00495 if (!result) { 00496 // Capture display but do not fill the screen with black 00497 // so that we can see the fade operation 00498 capture = CGDisplayCaptureWithOptions(kCGDirectMainDisplay, kCGCaptureNoFill); 00499 00500 if (!capture) { 00501 // Do the increasing fade operation 00502 CGDisplayFade(token, time, 00503 kCGDisplayBlendNormal, 00504 kCGDisplayBlendSolidColor, 00505 0.0f, 0.0f, 0.0f, sync); 00506 00507 // Now, release the non black-filling capture 00508 CGDisplayRelease(kCGDirectMainDisplay); 00509 00510 // And capture with filling 00511 // so that we don't see the switching in the meantime 00512 CGDisplayCaptureWithOptions(kCGDirectMainDisplay, kCGCaptureNoOptions); 00513 } 00514 00515 prevToken = token; 00516 } 00517 } else if (operation == CleanScreen) { 00518 // Get access for the fade operation 00519 if (token == kCGDisplayFadeReservationInvalidToken) 00520 result = CGAcquireDisplayFadeReservation((int)(3 + time), &token); 00521 00522 if (!result) { 00523 if (!capture) { 00524 // Release the black-filling capture 00525 CGDisplayRelease(kCGDirectMainDisplay); 00526 00527 // Capture the display but do not fill with black (still for the fade operation) 00528 CGDisplayCaptureWithOptions(kCGDirectMainDisplay, kCGCaptureNoFill); 00529 00530 // Do the decreasing fading 00531 CGDisplayFade(token, time, 00532 kCGDisplayBlendSolidColor, 00533 kCGDisplayBlendNormal, 00534 0.0f, 0.0f, 0.0f, sync); 00535 00536 // Release the fade operation token 00537 CGReleaseDisplayFadeReservation(token); 00538 00539 // Invalidate the given token 00540 prevToken = kCGDisplayFadeReservationInvalidToken; 00541 } 00542 00543 // Release the captured display 00544 CGDisplayRelease(kCGDirectMainDisplay); 00545 } 00546 } 00547 } 00548 00549 00553 - (const sf::VideoMode&)desktopMode 00554 { 00555 return myDesktopMode; 00556 } 00557 00558 @end 00559
:: Copyright © 2007-2008 Laurent Gomila, all rights reserved :: Documentation generated by doxygen 1.5.2 ::