Back to index

libsfml  1.6+dfsg2
AppController.mm
Go to the documentation of this file.
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 
00037 /* setAppleMenu disappeared from the headers in 10.4 */
00038 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
00039 @interface NSApplication (SFML)
00040 - (void)setAppleMenu:(NSMenu *)menu;
00041 @end
00042 #endif
00043 
00044 #define ENABLE_FADE_OPERATIONS 1
00045 
00046 @implementation NSApplication (SFML)
00047 
00048 - (void)setRunning:(BOOL)flag
00049 {
00050        // Note: _running is a short, not a BOOL
00051        if (flag)
00052               _running = 1;
00053        else
00054               _running = 0;
00055 }
00056 
00057 @end
00058 
00059 
00060 @implementation sfPrivAppController
00061 
00062 
00069 - (id)init
00070 {
00071        self = [super init];
00072        
00073        if (self != nil) {
00074               myOwningEventLoop = NO;
00075               
00076               // Save the desktop mode
00077               myDesktopMode = sf::VideoMode::GetDesktopMode();
00078               myPrevMode = myDesktopMode;
00079               
00080               // Make the app autorelease pool
00081               myMainPool = [[NSAutoreleasePool alloc] init];
00082               
00083               if (!myMainPool) {
00084                      [self release];
00085                      throw std::bad_alloc();
00086               }
00087               
00088               // Don't go on if the user handles the app
00089               if (![NSApp isRunning])
00090               {
00091                      // Force our application to appear in the Dock and make it able
00092                      // to get focus (even when it's a raw executable)
00093                      ProcessSerialNumber psn;
00094                      
00095                      if (!GetCurrentProcess(&psn)) {
00096                             TransformProcessType(&psn, kProcessTransformToForegroundApplication);
00097                             SetFrontProcess(&psn);
00098                      }
00099                      
00100                      // Make the app
00101                      if (![NSApplication sharedApplication]) {
00102                             [self release];
00103                             throw std::bad_alloc();
00104                      }
00105                      
00106                      [self setNotifications];
00107                      
00108                      if ([NSApp mainMenu] == nil) {
00109                             [self makeMenuBar];
00110                      }
00111               }
00112        }
00113        
00114        return self;
00115 }
00116 
00117 
00121 - (void)dealloc
00122 {
00123        [[NSNotificationCenter defaultCenter] removeObserver:self];
00124        [myMainPool release];
00125        [super dealloc];
00126 }
00127 
00128 
00132 + (sfPrivAppController *)sharedController
00133 {
00134        // AppController singleton object
00135        static sfPrivAppController *shared = [[sfPrivAppController alloc] init];
00136        return shared;
00137 }
00138 
00139 
00143 + (CGDirectDisplayID)primaryScreen
00144 {
00145        static BOOL firstTime = YES;
00146        static CGDirectDisplayID screen = kCGNullDirectDisplay;
00147        
00148        if (firstTime) {
00149               CGDisplayCount numScr;
00150               CGDisplayErr err = CGGetDisplaysWithPoint(CGPointMake(0, 0), 1, &screen, &numScr);
00151               
00152               if (err != kCGErrorSuccess || numScr < 1) {
00153                      std::cerr << "Unable to get primary screen (error code " << err 
00154                      << " ). Using the main screen.";
00155                      screen = CGMainDisplayID();
00156               }
00157               firstTime = NO;
00158        }
00159        
00160        return screen;
00161 }
00162 
00163 
00167 - (void)setNotifications
00168 {
00169        NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
00170        [nc addObserver:self
00171                  selector:@selector(applicationWillDeactivate:)
00172                         name:NSApplicationWillResignActiveNotification
00173                       object:NSApp];
00174        [nc addObserver:self
00175                  selector:@selector(applicationWillActivate:)
00176                         name:NSApplicationWillBecomeActiveNotification
00177                       object:NSApp];
00178 
00179        [nc addObserver:self
00180                  selector:@selector(applicationWillTerminate:)
00181                         name:NSApplicationWillTerminateNotification
00182                       object:NSApp];
00183 }
00184 
00185 
00189 - (void)applicationWillDeactivate:(NSNotification *)aNotification
00190 {
00191        // Note: not using fading because it produces reactivation issues
00192        if (myFullscreenWrapper) {
00193               myPrevMode = sf::VideoMode::GetDesktopMode();
00194               
00195               CFDictionaryRef displayMode = CGDisplayBestModeForParameters ([sfPrivAppController primaryScreen],
00196                                                                                                                          myDesktopMode.BitsPerPixel,
00197                                                                                                                          myDesktopMode.Width,
00198                                                                                                                          myDesktopMode.Height,
00199                                                                                                                          NULL);
00200               
00201               // Make the full screen window unvisible
00202               [[myFullscreenWrapper window] setAlphaValue:0.0f];
00203               
00204               // Switch to the wished display mode
00205               CGDisplaySwitchToMode([sfPrivAppController primaryScreen], displayMode);
00206        }
00207 }
00208 
00209 
00213 - (void)applicationWillActivate:(NSNotification *)aNotification
00214 {
00215        if (myFullscreenWrapper) {
00216               CFDictionaryRef displayMode = CGDisplayBestModeForParameters ([sfPrivAppController primaryScreen],
00217                                                                                                                          myPrevMode.BitsPerPixel,
00218                                                                                                                          myPrevMode.Width,
00219                                                                                                                          myPrevMode.Height,
00220                                                                                                                          NULL);
00221               [NSMenu setMenuBarVisible:NO];
00222               
00223               // Switch to the wished display mode
00224               CGDisplaySwitchToMode([sfPrivAppController primaryScreen], displayMode);
00225               
00226               // Show the fullscreen window if existing
00227               if (myFullscreenWrapper)
00228               {
00229                      [[myFullscreenWrapper window] setAlphaValue:1.0f];
00230                      [[myFullscreenWrapper window] center];
00231                      [[myFullscreenWrapper window] makeKeyAndOrderFront:self];
00232               }
00233        }
00234 }
00235 
00236 
00237 - (void)applicationWillTerminate:(NSNotification *)aNotification
00238 {
00239        if (myFullscreenWrapper)
00240               [self setFullscreenWindow:nil mode:NULL];
00241 }
00242 
00246 - (void)makeMenuBar
00247 {
00248        // Source taken from SDL 1.3
00249        NSString *appName = nil;
00250        NSString *title = nil;
00251        NSMenu *appleMenu = nil;
00252        NSMenu *fileMenu = nil;
00253        NSMenu *windowMenu = nil;
00254        NSMenuItem *menuItem = nil;
00255        NSMenuItem *quitMenuItem = nil;
00256        
00257        // Determine the application name
00258        appName = [[[NSBundle mainBundle] infoDictionary] objectForKey: @"CFBundleName"];
00259        
00260        if (![appName length])
00261               appName = [[NSProcessInfo processInfo] processName];
00262        
00263        
00264        // Create the main menu bar
00265        [NSApp setMainMenu:[[NSMenu alloc] init]];
00266        
00267        // Create the application menu
00268        appleMenu = [[NSMenu alloc] initWithTitle:@""];
00269        
00270        // Put menu items
00271        // + 'About' menu item
00272        title = [@"About " stringByAppendingString:appName];
00273        [appleMenu addItemWithTitle:title
00274                                            action:@selector(orderFrontStandardAboutPanel:)
00275                               keyEquivalent:@""];
00276        
00277        [appleMenu addItem:[NSMenuItem separatorItem]];
00278        
00279        // + 'Hide' menu item
00280        title = [@"Hide " stringByAppendingString:appName];
00281        [appleMenu addItemWithTitle:title
00282                                            action:@selector(hide:)
00283                               keyEquivalent:@"h"];
00284        
00285        // + 'Hide other' menu item
00286        menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others"
00287                                                                                       action:@selector(hideOtherApplications:)
00288                                                                          keyEquivalent:@"h"];
00289        [menuItem setKeyEquivalentModifierMask:NSAlternateKeyMask | NSCommandKeyMask];
00290        
00291        // + 'Show all' menu item
00292        [appleMenu addItemWithTitle:@"Show All"
00293                                            action:@selector(unhideAllApplications:)
00294                               keyEquivalent:@""];
00295        
00296        [appleMenu addItem:[NSMenuItem separatorItem]];
00297        
00298        // + 'Quit' menu item
00299        title = [@"Quit " stringByAppendingString:appName];
00300        quitMenuItem = [[[NSMenuItem alloc]
00301                                     initWithTitle:title 
00302                                     action:@selector(terminate:)
00303                                     keyEquivalent:@"q"] autorelease];
00304        //[quitMenuItem setTarget:self];
00305        [appleMenu addItem:quitMenuItem];
00306        
00307        // Put the menu into the menubar
00308        menuItem = [[NSMenuItem alloc]
00309                             initWithTitle:@""
00310                             action:nil
00311                             keyEquivalent:@""];
00312        [menuItem setSubmenu:appleMenu];
00313        [[NSApp mainMenu] addItem:menuItem];
00314        [menuItem release];
00315        
00316        // Tell the application object that this is now the application menu
00317        [NSApp setAppleMenu:appleMenu];
00318        [appleMenu release];
00319        
00320        // 'File' menu
00321        fileMenu = [[NSMenu alloc]
00322                             initWithTitle:@"File"];
00323        
00324        // + 'Close' menu item
00325        menuItem = [[NSMenuItem alloc]
00326                             initWithTitle:@"Close"
00327                             action:@selector(performClose:)
00328                             keyEquivalent:@"w"];
00329        [fileMenu addItem:menuItem];
00330        [menuItem release];
00331        
00332        // + 'File' menu item (head)
00333        menuItem = [[NSMenuItem alloc]
00334                             initWithTitle:@"File"
00335                             action:nil
00336                             keyEquivalent:@""];
00337        [menuItem setSubmenu:fileMenu];
00338        [[NSApp mainMenu] addItem:menuItem];
00339        [menuItem release];
00340        
00341        // 'Window' menu
00342        windowMenu = [[NSMenu alloc]
00343                               initWithTitle:@"Window"];
00344        
00345        // + 'Minimize' menu item
00346        menuItem = [[NSMenuItem alloc]
00347                             initWithTitle:@"Minimize"
00348                             action:@selector(performMiniaturize:)
00349                             keyEquivalent:@"m"];
00350        [windowMenu addItem:menuItem];
00351        [menuItem release];
00352        
00353        // + 'Window' menu item (head)
00354        menuItem = [[NSMenuItem alloc]
00355                             initWithTitle:@"Window"
00356                             action:nil keyEquivalent:@""];
00357        [menuItem setSubmenu:windowMenu];
00358        [[NSApp mainMenu] addItem:menuItem];
00359        [menuItem release];
00360        
00361        // Tell the application object that this is now the window menu
00362        [NSApp setWindowsMenu:windowMenu];
00363        [windowMenu release];
00364 }
00365 
00366 
00372 - (void)processEvents
00373 {
00374        // Check there is a run loop
00375        if (![NSApp isRunning])
00376        {
00377               // Get the ownershipt of event handling if not and run
00378               [NSApp finishLaunching];
00379               [NSApp setRunning:YES];
00380               myOwningEventLoop = YES;
00381        }
00382        
00383        NSEvent *event = nil;
00384        
00385        if (myOwningEventLoop)
00386        {
00387               // Clean the autorelease pool
00388               [myMainPool release];
00389               myMainPool = [[NSAutoreleasePool alloc] init];
00390               
00391               // Minimal event loop
00392               while (nil != (event = [NSApp nextEventMatchingMask:NSAnyEventMask
00393                                                                                       untilDate:nil
00394                                                                                             inMode:NSDefaultRunLoopMode
00395                                                                                            dequeue:YES]))
00396               {
00397                      [NSApp sendEvent:event];
00398               }
00399        }
00400 }
00401 
00402 
00407 - (void)setFullscreenWindow:(sfPrivWindow *)aWindow mode:(sf::VideoMode *)fullscreenMode
00408 {
00409        // If we have a fullscreen window and want to remove it
00410        if (aWindow == nil && myFullscreenWrapper)
00411        {
00412               // Get the CoreGraphics display mode according to the desktop mode
00413               CFDictionaryRef displayMode = CGDisplayBestModeForParameters ([sfPrivAppController primaryScreen],
00414                                                                                                                          myDesktopMode.BitsPerPixel,
00415                                                                                                                          myDesktopMode.Width,
00416                                                                                                                          myDesktopMode.Height,
00417                                                                                                                          NULL);
00418               
00419 #if ENABLE_FADE_OPERATIONS
00420               // Fade to black screen
00421               [self doFadeOperation:FillScreen time:0.2f sync:true];
00422 #endif
00423               
00424               // Switch to the desktop display mode
00425               CGDisplaySwitchToMode([sfPrivAppController primaryScreen], displayMode);
00426               
00427               // Close the window
00428               [[myFullscreenWrapper window] close];
00429               
00430               // Show the menu bar
00431               [NSMenu setMenuBarVisible:YES];
00432               
00433 #if ENABLE_FADE_OPERATIONS
00434               // Fade to normal screen
00435               [self doFadeOperation:CleanScreen time:0.5f sync:true];
00436 #endif
00437               
00438               // Release the saved window wrapper
00439               myFullscreenWrapper = nil;
00440        }
00441        else if (aWindow)
00442        {
00443               // else if we want to SET fullscreen
00444               assert(fullscreenMode != NULL);
00445               
00446               // Get the CoreGraphics display mode according to the given sf mode
00447               CFDictionaryRef displayMode = CGDisplayBestModeForParameters ([sfPrivAppController primaryScreen],
00448                                                                                                                          fullscreenMode->BitsPerPixel,
00449                                                                                                                          fullscreenMode->Width,
00450                                                                                                                          fullscreenMode->Height,
00451                                                                                                                          NULL);
00452               
00453 #if ENABLE_FADE_OPERATIONS
00454               // Fade to a black screen
00455               [self doFadeOperation:FillScreen time:0.5f sync:true];
00456 #endif
00457               
00458               if (!myFullscreenWrapper)
00459               {
00460                      // Hide the main menu bar
00461                      [NSMenu setMenuBarVisible:NO];
00462               }
00463               
00464               if (myPrevMode != *fullscreenMode)
00465               {
00466                      // Switch to the wished display mode
00467                      myPrevMode = *fullscreenMode;
00468                      CGDisplaySwitchToMode([sfPrivAppController primaryScreen], displayMode);
00469               }
00470               
00471               if (myFullscreenWrapper)
00472               {
00473                      [[myFullscreenWrapper window] close];
00474               }
00475               
00476               // Open and center the window
00477               [[aWindow window] makeKeyAndOrderFront:nil];
00478               [[aWindow window] center];
00479               
00480 #if ENABLE_FADE_OPERATIONS
00481               // Fade to normal screen
00482               [self doFadeOperation:CleanScreen time:0.2f sync:false];
00483 #endif
00484               
00485               // Save the fullscreen wrapper
00486               myFullscreenWrapper = aWindow;
00487        }
00488        else
00489        {
00490               std::cerr << "Inconcistency error for arguments given to -[sfPrivAppController setFullscreenWindow:mode:]" << std::endl;
00491        }
00492 }
00493 
00494 
00503 - (void) doFadeOperation:(int)operation time:(float)time sync:(bool)sync
00504 {
00505        static CGDisplayFadeReservationToken prevToken = kCGDisplayFadeReservationInvalidToken;
00506        CGDisplayFadeReservationToken token = prevToken;
00507        
00508        CGError result = 0, capture = 0;
00509        
00510        if (operation == FillScreen) {
00511               // Get access for the fade operation
00512               result = CGAcquireDisplayFadeReservation((int)(3 + time), &token);
00513               
00514               if (!result) {
00515                      // Capture display but do not fill the screen with black
00516                      // so that we can see the fade operation
00517                      capture = CGDisplayCaptureWithOptions([sfPrivAppController primaryScreen], kCGCaptureNoFill);
00518                      
00519                      if (!capture) {
00520                             // Do the increasing fade operation
00521                             CGDisplayFade(token, time,
00522                                                    kCGDisplayBlendNormal,
00523                                                    kCGDisplayBlendSolidColor,
00524                                                    0.0f, 0.0f, 0.0f, sync);
00525                             
00526                             // Now, release the non black-filling capture
00527                             CGDisplayRelease([sfPrivAppController primaryScreen]);
00528                             
00529                             // And capture with filling
00530                             // so that we don't see the switching in the meantime
00531                             CGDisplayCaptureWithOptions([sfPrivAppController primaryScreen], kCGCaptureNoOptions);
00532                      }
00533                      
00534                      prevToken = token;
00535               }
00536        } else if (operation == CleanScreen) {
00537               // Get access for the fade operation
00538               if (token == kCGDisplayFadeReservationInvalidToken)
00539                      result = CGAcquireDisplayFadeReservation((int)(3 + time), &token);
00540               
00541               if (!result) {
00542                      if (!capture) {
00543                             // Release the black-filling capture
00544                             CGDisplayRelease([sfPrivAppController primaryScreen]);
00545                             
00546                             // Capture the display but do not fill with black (still for the fade operation)
00547                             CGDisplayCaptureWithOptions([sfPrivAppController primaryScreen], kCGCaptureNoFill);
00548                             
00549                             // Do the decreasing fading
00550                             CGDisplayFade(token, time,
00551                                                    kCGDisplayBlendSolidColor,
00552                                                    kCGDisplayBlendNormal,
00553                                                    0.0f, 0.0f, 0.0f, sync);
00554                             
00555                             // Release the fade operation token
00556                             CGReleaseDisplayFadeReservation(token);
00557                             
00558                             // Invalidate the given token
00559                             prevToken = kCGDisplayFadeReservationInvalidToken;
00560                      }
00561                      
00562                      // Release the captured display
00563                      CGDisplayRelease([sfPrivAppController primaryScreen]);
00564               }
00565        }
00566 }
00567 
00568 
00572 - (const sf::VideoMode&)desktopMode
00573 {
00574        return myDesktopMode;
00575 }
00576 
00577 @end
00578