MainControler.m
/* |
File: MainControler.m |
Abstract: A controller object that handles full-screen/window modes |
switching and user interactions. |
Version: 1.1 |
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple |
Inc. ("Apple") in consideration of your agreement to the following |
terms, and your use, installation, modification or redistribution of |
this Apple software constitutes acceptance of these terms. If you do |
not agree with these terms, please do not use, install, modify or |
redistribute this Apple software. |
In consideration of your agreement to abide by the following terms, and |
subject to these terms, Apple grants you a personal, non-exclusive |
license, under Apple's copyrights in this original Apple software (the |
"Apple Software"), to use, reproduce, modify and redistribute the Apple |
Software, with or without modifications, in source and/or binary forms; |
provided that if you redistribute the Apple Software in its entirety and |
without modifications, you must retain this notice and the following |
text and disclaimers in all such redistributions of the Apple Software. |
Neither the name, trademarks, service marks or logos of Apple Inc. may |
be used to endorse or promote products derived from the Apple Software |
without specific prior written permission from Apple. Except as |
expressly stated in this notice, no other rights or licenses, express or |
implied, are granted by Apple herein, including but not limited to any |
patent rights that may be infringed by your derivative works or by other |
works in which the Apple Software may be incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. APPLE |
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION |
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS |
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND |
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL |
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, |
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED |
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), |
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE |
POSSIBILITY OF SUCH DAMAGE. |
Copyright (C) 2010 Apple Inc. All Rights Reserved. |
*/ |
#import "MainController.h" |
#import "MyOpenGLView.h" |
#import "Scene.h" |
@implementation MainController |
- (IBAction) goFullScreen:(id)sender |
{ |
isInFullScreenMode = YES; |
// Pause the non-fullscreen view |
[openGLView stopAnimation]; |
// Mac OS X 10.6 and later offer a simplified mechanism to create full-screen contexts |
#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5 |
NSRect mainDisplayRect, viewRect; |
// Create a screen-sized window on the display you want to take over |
// Note, mainDisplayRect has a non-zero origin if the key window is on a secondary display |
mainDisplayRect = [[NSScreen mainScreen] frame]; |
fullScreenWindow = [[NSWindow alloc] initWithContentRect:mainDisplayRect styleMask:NSBorderlessWindowMask |
backing:NSBackingStoreBuffered defer:YES]; |
// Set the window level to be above the menu bar |
[fullScreenWindow setLevel:NSMainMenuWindowLevel+1]; |
// Perform any other window configuration you desire |
[fullScreenWindow setOpaque:YES]; |
[fullScreenWindow setHidesOnDeactivate:YES]; |
// Create a view with a double-buffered OpenGL context and attach it to the window |
// By specifying the non-fullscreen context as the shareContext, we automatically inherit the OpenGL objects (textures, etc) it has defined |
viewRect = NSMakeRect(0.0, 0.0, mainDisplayRect.size.width, mainDisplayRect.size.height); |
fullScreenView = [[MyOpenGLView alloc] initWithFrame:viewRect shareContext:[openGLView openGLContext]]; |
[fullScreenWindow setContentView:fullScreenView]; |
// Show the window |
[fullScreenWindow makeKeyAndOrderFront:self]; |
// Set the scene with the full-screen viewport and viewing transformation |
[scene setViewportRect:viewRect]; |
// Assign the view's MainController to self |
[fullScreenView setMainController:self]; |
if (!isAnimating) { |
// Mark the view as needing drawing to initalize its contents |
[fullScreenView setNeedsDisplay:YES]; |
} |
else { |
// Start playing the animation |
[fullScreenView startAnimation]; |
} |
#else |
// Mac OS X 10.5 and eariler require additional work to capture the display and set up a special context |
// This demo uses CGL for full-screen rendering on pre-10.6 systems. You may also use NSOpenGL to achieve this. |
CGLPixelFormatObj pixelFormatObj; |
GLint numPixelFormats; |
// Capture the main display |
CGDisplayCapture(kCGDirectMainDisplay); |
// Set up an array of attributes |
CGLPixelFormatAttribute attribs[] = { |
// The full-screen attribute |
kCGLPFAFullScreen, |
// The display mask associated with the captured display |
// We may be on a multi-display system (and each screen may be driven by a different renderer), so we need to specify which screen we want to take over. For this demo, we'll specify the main screen. |
kCGLPFADisplayMask, CGDisplayIDToOpenGLDisplayMask(kCGDirectMainDisplay), |
// Attributes common to full-screen and non-fullscreen |
kCGLPFAAccelerated, |
kCGLPFANoRecovery, |
kCGLPFADoubleBuffer, |
kCGLPFAColorSize, 24, |
kCGLPFADepthSize, 16, |
0 |
}; |
// Create the full-screen context with the attributes listed above |
// By specifying the non-fullscreen context as the shareContext, we automatically inherit the OpenGL objects (textures, etc) it has defined |
CGLChoosePixelFormat(attribs, &pixelFormatObj, &numPixelFormats); |
CGLCreateContext(pixelFormatObj, [[openGLView openGLContext] CGLContextObj], &fullScreenContextObj); |
CGLDestroyPixelFormat(pixelFormatObj); |
if (!fullScreenContextObj) { |
NSLog(@"Failed to create full-screen context"); |
CGReleaseAllDisplays(); |
[self goWindow]; |
return; |
} |
// Set the current context to the one to use for full-screen drawing |
CGLSetCurrentContext(fullScreenContextObj); |
// Attach a full-screen drawable object to the current context |
CGLSetFullScreen(fullScreenContextObj); |
// Lock us to the display's refresh rate |
GLint newSwapInterval = 1; |
CGLSetParameter(fullScreenContextObj, kCGLCPSwapInterval, &newSwapInterval); |
// Tell the scene the dimensions of the area it's going to render to, so it can set up an appropriate viewport and viewing transformation |
[scene setViewportRect:NSMakeRect(0, 0, CGDisplayPixelsWide(kCGDirectMainDisplay), CGDisplayPixelsHigh(kCGDirectMainDisplay))]; |
// Perform the application's main loop until exiting full-screen |
// The shift here is from a model in which we passively receive events handed to us by the AppKit (in window mode) |
// to one in which we are actively driving event processing (in full-screen mode) |
while (isInFullScreenMode) |
{ |
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; |
// Check for and process input events |
NSEvent *event; |
while (event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES]) |
{ |
switch ([event type]) |
{ |
case NSLeftMouseDown: |
[self mouseDown:event]; |
break; |
case NSLeftMouseUp: |
[self mouseUp:event]; |
break; |
case NSLeftMouseDragged: |
[self mouseDragged:event]; |
break; |
case NSKeyDown: |
[self keyDown:event]; |
break; |
default: |
break; |
} |
} |
// Update our animation |
CFAbsoluteTime currentTime = CFAbsoluteTimeGetCurrent(); |
if (isAnimating) { |
[scene advanceTimeBy:(currentTime - renderTime)]; |
} |
renderTime = currentTime; |
// Delegate to the scene object for rendering |
[scene render]; |
CGLFlushDrawable(fullScreenContextObj); |
[pool release]; |
} |
#endif |
} |
- (void) goWindow |
{ |
isInFullScreenMode = NO; |
#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5 |
// Release the screen-sized window and view |
[fullScreenWindow release]; |
[fullScreenView release]; |
#else |
// Set the current context to NULL |
CGLSetCurrentContext(NULL); |
// Clear the drawable object |
CGLClearDrawable(fullScreenContextObj); |
// Destroy the rendering context |
CGLDestroyContext(fullScreenContextObj); |
// Release the displays |
CGReleaseAllDisplays(); |
#endif |
// Switch to the non-fullscreen context |
[[openGLView openGLContext] makeCurrentContext]; |
if (!isAnimating) { |
// Mark the view as needing drawing |
// The animation has advanced while we were in full-screen mode, so its current contents are stale |
[openGLView setNeedsDisplay:YES]; |
} |
else { |
// Continue playing the animation |
[openGLView startAnimation]; |
} |
} |
- (void) awakeFromNib |
{ |
// Allocate the scene object |
scene = [[Scene alloc] init]; |
// Assign the view's MainController to self |
[openGLView setMainController:self]; |
// Activate the display link now |
[openGLView startAnimation]; |
isAnimating = YES; |
} |
- (void) dealloc |
{ |
[scene release]; |
[super dealloc]; |
} |
- (Scene*) scene |
{ |
return scene; |
} |
- (CFAbsoluteTime) renderTime |
{ |
return renderTime; |
} |
- (void) setRenderTime:(CFAbsoluteTime)time |
{ |
renderTime = time; |
} |
- (void) startAnimation |
{ |
if (!isAnimating) |
{ |
if (!isInFullScreenMode) |
[openGLView startAnimation]; |
#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5 |
else |
[fullScreenView startAnimation]; |
#endif |
isAnimating = YES; |
} |
} |
- (void) stopAnimation |
{ |
if (isAnimating) |
{ |
if (!isInFullScreenMode) |
[openGLView stopAnimation]; |
#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5 |
else |
[fullScreenView stopAnimation]; |
#endif |
isAnimating = NO; |
} |
} |
- (void) toggleAnimation |
{ |
if (isAnimating) |
[self stopAnimation]; |
else |
[self startAnimation]; |
} |
- (void) keyDown:(NSEvent *)event |
{ |
unichar c = [[event charactersIgnoringModifiers] characterAtIndex:0]; |
switch (c) { |
// [Esc] exits full-screen mode |
case 27: |
if (isInFullScreenMode) |
[self goWindow]; |
break; |
// [space] toggles rotation of the globe |
case 32: |
[self toggleAnimation]; |
break; |
// [W] toggles wireframe rendering |
case 'w': |
case 'W': |
[scene toggleWireframe]; |
break; |
default: |
break; |
} |
} |
// Holding the mouse button and dragging the mouse changes the "roll" angle (y-axis) and the direction from which sunlight is coming (x-axis). |
- (void)mouseDown:(NSEvent *)theEvent |
{ |
BOOL dragging = YES; |
NSPoint windowPoint; |
NSPoint lastWindowPoint = [theEvent locationInWindow]; |
float dx, dy; |
BOOL wasAnimating = isAnimating; |
if (wasAnimating) { |
[self stopAnimation]; |
} |
while (dragging) |
{ |
theEvent = [[openGLView window] nextEventMatchingMask:NSLeftMouseUpMask | NSLeftMouseDraggedMask]; |
windowPoint = [theEvent locationInWindow]; |
switch ([theEvent type]) |
{ |
case NSLeftMouseUp: |
dragging = NO; |
break; |
case NSLeftMouseDragged: |
dx = windowPoint.x - lastWindowPoint.x; |
dy = windowPoint.y - lastWindowPoint.y; |
[scene setSunAngle:[scene sunAngle] - 1.0 * dx]; |
[scene setRollAngle:[scene rollAngle] - 0.5 * dy]; |
lastWindowPoint = windowPoint; |
if (isInFullScreenMode) { |
#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5 |
[fullScreenView display]; |
#else |
[scene render]; |
CGLFlushDrawable(fullScreenContextObj); |
#endif |
} |
else { |
[openGLView display]; |
} |
break; |
default: |
break; |
} |
} |
if (wasAnimating) { |
[self startAnimation]; |
renderTime = CFAbsoluteTimeGetCurrent(); |
} |
} |
@end |
Copyright © 2010 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2010-12-01