SFML logo
  • Main Page
  • Namespaces
  • Classes
  • Files
  • File List

AppController.mm

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  ::