diff --git a/stepmania/src/arch/LowLevelWindow/LowLevelWindow_Cocoa.h b/stepmania/src/arch/LowLevelWindow/LowLevelWindow_Cocoa.h index c3cd2d47da..11f8cc7334 100644 --- a/stepmania/src/arch/LowLevelWindow/LowLevelWindow_Cocoa.h +++ b/stepmania/src/arch/LowLevelWindow/LowLevelWindow_Cocoa.h @@ -2,6 +2,7 @@ #define LOW_LEVEL_WINDOW_COCOA_H #include "LowLevelWindow.h" +#include "RageDisplay.h" #ifndef __OBJC__ typedef void *id; @@ -9,25 +10,32 @@ typedef void *id; class LowLevelWindow_Cocoa : public LowLevelWindow { - RageDisplay::VideoModeParams mCurrentParams; + VideoModeParams mCurrentParams; id mWindow; + id mView; id mFullScreenContext; + bool mSharingContexts; public: LowLevelWindow_Cocoa(); ~LowLevelWindow_Cocoa(); void *GetProcAddress( CString s ); - CString TryVideoMode( RageDisplay::VideoModeParams p, bool& newDeviceOut ); + CString TryVideoMode( const VideoModeParams& p, bool& newDeviceOut ); + void GetDisplayResolutions( DisplayResolutions &dr ) const; void SwapBuffers(); - void Update(); - RageDisplay::VideoModeParams GetVideoModeParams() const { return mCurrentParams; } + VideoModeParams GetActualVideoModeParams() const { return mCurrentParams; } }; +#ifdef ARCH_LOW_LEVEL_WINDOW +#error "More than one LowLevelWindow selected!" +#endif +#define ARCH_LOW_LEVEL_WINDOW LowLevelWindow_Cocoa + #endif /* - * (c) 2005 Steve Checkoway + * (c) 2005-2006 Steve Checkoway * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a diff --git a/stepmania/src/arch/LowLevelWindow/LowLevelWindow_Cocoa.mm b/stepmania/src/arch/LowLevelWindow/LowLevelWindow_Cocoa.mm index e62c99e679..7ecd5a9084 100644 --- a/stepmania/src/arch/LowLevelWindow/LowLevelWindow_Cocoa.mm +++ b/stepmania/src/arch/LowLevelWindow/LowLevelWindow_Cocoa.mm @@ -1,61 +1,75 @@ #import "global.h" #import "LowLevelWindow_Cocoa.h" +#import "DisplayResolutions.h" +#import "RageLog.h" +#import "RageUtil.h" + #import #import -@interface SMView : NSOpenGLView -{ -} -- (id) initWithFrame:(NSRect)frameRect colorSize:(int)size; -@end -@implementation SMView -- (id) initWithFrame:(NSRect)frameRect colorSize:(int)size +static const int g_iStyleMask = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask; + +// Simple helper class +class AutoreleasePool { - NSOpenGLPixelFormatAttribute attrs[] = { + NSAutoreleasePool *mPool; + +public: + AutoreleasePool() { mPool = [[NSAutoreleasePool alloc] init]; } + ~AutoreleasePool() { [mPool release]; } +}; + +// These can nest. +#define POOL2(x) AutoreleasePool pool ## x +#define POOL1(x) POOL2(x) +#define POOL POOL1(__LINE__) + +static NSOpenGLPixelFormat *CreatePixelFormat( int bbp, bool windowed ) +{ + NSOpenGLPixelFormatAttribute attrs[] = { NSOpenGLPFANoRecovery, // so we can share with the full screen context NSOpenGLPFADoubleBuffer, NSOpenGLPFAAccelerated, NSOpenGLPFADepthSize, NSOpenGLPixelFormatAttribute(16), - NSOpenGLPFAColorSize, NSOpenGLPixelFormatAttribute(size), + NSOpenGLPFAColorSize, NSOpenGLPixelFormatAttribute(bbp == 16 ? 16 : 24), + windowed ? NSOpenGLPixelFormatAttribute(0) : NSOpenGLPFAFullScreen, NSOpenGLPixelFormatAttribute(0) }; - NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs]; - [super initWithFrame:frameRect pixelFormat:pixelFormat]; - return self; + return [[[NSOpenGLPixelFormat alloc] initWithAttributes:attrs] autorelease]; } -// Put this here because I always forget about it and it might be needed later. -- (void) dealloc -{ - [super dealloc]; -} -@end - -LowLevelWindow_Cocoa::LowLevelWindow_Cocoa() +LowLevelWindow_Cocoa::LowLevelWindow_Cocoa() : mView(nil), mFullScreenContext(nil) { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSRect rect = { 0, 0, 0, 0 }; - int mask = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask; + POOL; + NSRect rect = { {0, 0}, {0, 0} }; mWindow = [[NSWindow alloc] initWithContentRect:rect - styleMask:mask + styleMask:g_iStyleMask backing:NSBackingStoreNonretained defer:YES]; - [pool release]; + [mWindow setExcludedFromWindowsMenu:YES]; + [mWindow useOptimizedDrawing:YES]; } LowLevelWindow_Cocoa::~LowLevelWindow_Cocoa() { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - + POOL; if( mWindow ) { [mWindow orderOut:nil]; [mWindow release]; } - [pool release]; + if( mFullScreenContext ) + { + if( !mCurrentParams.windowed ) + { + [mFullScreenContext clearDrawable]; // Exit full screen. + CGReleaseAllDisplays(); + } + [mFullScreenContext release]; + } } void *LowLevelWindow_Cocoa::GetProcAddress( CString s ) @@ -69,20 +83,324 @@ void *LowLevelWindow_Cocoa::GetProcAddress( CString s ) return symbol ? NSAddressOfSymbol( symbol ) : NULL; } -CString LowLevelWindow_Cocoa::TryVideoMode( RageDisplay::VideoModeParams p, bool& newDeviceOut ) +CString LowLevelWindow_Cocoa::TryVideoMode( const VideoModeParams& p, bool& newDeviceOut ) { - return "XXX"; + POOL; + + newDeviceOut = false; + + NSRect contentRect = { { 0, 0 }, { p.width, p.height } }; + const bool changedWindowed = p.windowed != mCurrentParams.windowed; + const bool changedView = p.bpp != mCurrentParams.bpp || !mView; + + LOG->Trace( "changedWindowed = %d; changedView = %d;", int(changedWindowed), int(changedView) ); + + if( changedWindowed ) + { + if( p.windowed ) + { + // No need to reset the size if it changes here. + [mWindow setContentSize:contentRect.size]; + [mWindow setTitle:[NSString stringWithUTF8String:p.sWindowTitle.c_str()]]; + + if( changedView ) + { + // Create view. + NSOpenGLPixelFormat *pixelFormat = CreatePixelFormat( p.bpp, true ); + + if( pixelFormat == nil ) + return "Failed to set Pixel Format"; + id nextView = [[NSOpenGLView alloc] initWithFrame:contentRect pixelFormat:pixelFormat]; + if( nextView == nil ) + return "Failed to create OGL context."; + mView = nextView; + [mWindow setContentView:mView]; // This retains the view and releases the old one. + [mView release]; + newDeviceOut = true; + } + // Shut down full screen. Nothing else should fail. + if( mFullScreenContext ) + { + [mFullScreenContext clearDrawable]; + CGDisplayErr err = CGReleaseAllDisplays(); + + ASSERT( err = kCGErrorSuccess ); + if( changedView ) + { + [mFullScreenContext release]; + mFullScreenContext = nil; + } + newDeviceOut = !mSharingContexts; + } + // Set the current context + [[mView openGLContext] makeCurrentContext]; + [mWindow center]; + [mWindow performSelectorOnMainThread:@selector(makeKeyAndOrderFront:) + withObject:nil waitUntilDone:YES]; + return CString(); + } + // Okay, we're going full screen here. + // If the bpp has changed (or nothing has been created) we need to recreate everything. + id nextView = changedView ? nil : mView; + if( changedView ) + { + NSOpenGLPixelFormat *pixelFormat = CreatePixelFormat( p.bpp, true ); + + if( pixelFormat == nil ) + return "Failed to set pixel format."; + nextView = [[NSOpenGLView alloc] initWithFrame:contentRect pixelFormat:pixelFormat]; + if( nextView == nil ) + return "Failed to create OGL context."; + newDeviceOut = true; + // We need to recreate the full screen context. + [mFullScreenContext release]; + mFullScreenContext = nil; + + // Autorelease nextView in case we fail later on. + [nextView autorelease]; + } + id newContext = mFullScreenContext; + + if( newContext == nil ) + { + NSOpenGLPixelFormat *pixelFormat = CreatePixelFormat( p.bpp, false ); + + if( pixelFormat == nil ) + return "Failed to set pixel format."; + + newContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat + shareContext:[nextView openGLContext]]; + if( !(mSharingContexts = newContext != nil) ) + { + LOG->Warn( "Failed to share openGL contexts." ); + newContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil]; + if( !mFullScreenContext ) + return "Failed to create full screen openGL context."; + newDeviceOut = true; + } + // Autorelease to aid cleanup in case of failure + [newContext autorelease]; + } + // Now we need to actually go full screen. + CGDisplayErr err = CGCaptureAllDisplays(); + + if( err != kCGErrorSuccess ) + return ssprintf( "Failed to capture all displays: %d.", int(err) ); + // We have the display, change the screen size if possible. + CFDictionaryRef params; + + if( p.rate == REFRESH_DEFAULT ) + params = CGDisplayBestModeForParameters( kCGDirectMainDisplay, p.bpp, p.width, p.height, NULL ); + else + params = CGDisplayBestModeForParametersAndRefreshRate( kCGDirectMainDisplay, p.bpp, + p.width, p.height, p.rate, NULL ); + err = CGDisplaySwitchToMode( kCGDirectMainDisplay, params ); + // XXX the docs don't show releasing params... + + if( err != kCGErrorSuccess ) + { + CGDisplayErr e = CGReleaseAllDisplays(); + + ASSERT( e == kCGErrorSuccess ); + return ssprintf( "Failed to switch mode: %d.", int(err) ); + } + + // We've succeeded! + mView = nextView; + [mWindow setContentView:mView]; + if( !mFullScreenContext ) + mFullScreenContext = [newContext retain]; // We autoreleased above so retain here. + [mFullScreenContext setFullScreen]; + [mFullScreenContext makeCurrentContext]; + return CString(); + } + // We're not changing windowed here + if( p.windowed ) + { + if( changedView ) + { + // Create view. + NSOpenGLPixelFormat *pixelFormat = CreatePixelFormat( p.bpp, true ); + + if( pixelFormat == nil ) + return "Failed to set Pixel Format"; + id nextView = [[NSOpenGLView alloc] initWithFrame:contentRect pixelFormat:pixelFormat]; + if( nextView == nil ) + return "Failed to create OGL context."; + mView = [nextView autorelease]; + newDeviceOut = true; + [mFullScreenContext release]; + mFullScreenContext = nil; + } + [mWindow setContentSize:contentRect.size]; + [mWindow setTitle:[NSString stringWithUTF8String:p.sWindowTitle.c_str()]]; + [mWindow setContentView:mView]; // This retains the view and releases the old one. + [[mView openGLContext] makeCurrentContext]; + [mWindow center]; + [mWindow performSelectorOnMainThread:@selector(makeKeyAndOrderFront:) + withObject:nil waitUntilDone:YES]; + return CString(); + } + // Just full screen left here. + // If the bpp has changed (or nothing has been created) we need to recreate everything. + id nextView = changedView ? nil : mView; + if( changedView ) + { + NSOpenGLPixelFormat *pixelFormat = CreatePixelFormat( p.bpp, true ); + + if( pixelFormat == nil ) + return "Failed to set pixel format."; + nextView = [[NSOpenGLView alloc] initWithFrame:contentRect pixelFormat:pixelFormat]; + if( nextView == nil ) + return "Failed to create OGL context."; + newDeviceOut = true; + // We need to recreate the full screen context. + [mFullScreenContext release]; + mFullScreenContext = nil; + + // Autorelease nextView in case we fail later on. + [nextView autorelease]; + } + id newContext = mFullScreenContext; + + if( newContext == nil ) + { + NSOpenGLPixelFormat *pixelFormat = CreatePixelFormat( p.bpp, false ); + + if( pixelFormat == nil ) + return "Failed to set pixel format."; + + newContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat + shareContext:[nextView openGLContext]]; + if( !(mSharingContexts = newContext != nil) ) + { + LOG->Warn( "Failed to share openGL contexts." ); + newContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil]; + if( !mFullScreenContext ) + return "Failed to create full screen openGL context."; + newDeviceOut = true; + } + // Autorelease to aid cleanup in case of failure + [newContext autorelease]; + } + CFDictionaryRef params; + + if( p.rate == REFRESH_DEFAULT ) + params = CGDisplayBestModeForParameters( kCGDirectMainDisplay, p.bpp, p.width, p.height, NULL ); + else + params = CGDisplayBestModeForParametersAndRefreshRate( kCGDirectMainDisplay, p.bpp, + p.width, p.height, p.rate, NULL ); + CGDisplayErr err = CGDisplaySwitchToMode( kCGDirectMainDisplay, params ); + // XXX the docs don't show releasing params... + + if( err != kCGErrorSuccess ) + return ssprintf( "Failed to switch mode: %d.", int(err) ); + + // We've succeeded! + mView = nextView; + [mWindow setContentSize:contentRect.size]; + [mWindow setContentView:mView]; + if( !mFullScreenContext ) + { + mFullScreenContext = [newContext retain]; // We autoreleased above so retain here. + [mFullScreenContext setFullScreen]; + [mFullScreenContext makeCurrentContext]; + } + else + { + [mFullScreenContext update]; + } + return CString(); +} +#if 0 + + if( changedBpp ) + { + mView = nil; // Create a new one, setContentView: will release it + if( mFullScreenContext ) + { + [mFullScreenContext clearDrawable]; + [mFullScreenContext release]; + mFullScreenContext = nil; + CGReleaseAllDisplays(); + } + } + + + if( p.windowed ) + { + if( !mCurrentParams.windowed ) + { + [mFullScreenContext clearDrawable]; + CGReleaseAllDisplays(); + } + [[mView openGLContext] makeCurrentContext]; + [mWindow setTitle:[NSString stringWithUTF8String:p.sWindowTitle.c_str()]]; + [mWindow center]; + [mWindow performSelectorOnMainThread:@selector(makeKeyAndOrderFront:) + withObject:nil waitUntilDone:YES]; + } + else + { + if( !mFullScreenContext ) + { + NSOpenGLPixelFormat *pixelFormat = CreatePixelFormat( p.bpp, false ); + + ASSERT( pixelFormat ); + mFullScreenContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat + shareContext:[mView openGLContext]]; + if( !mFullScreenContext ) + { + LOG->Warn( "Failed to share openGL contexts." ); + mFullScreenContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil]; + ASSERT( mFullScreenContext ); + newDeviceOut = true; + } + } + else if( changedSize ) + { + [mFullScreenContext update]; + } + if( mCurrentParams.windowed ) + { + CGCaptureAllDisplays(); // Do not notify other apps of any changes. + [mFullScreenContext setFullScreen]; + [mFullScreenContext makeCurrentContext]; + } + } + + mCurrentParams = p; + return ""; +} +#endif + +void LowLevelWindow_Cocoa::GetDisplayResolutions( DisplayResolutions &dr ) const +{ + CFArrayRef modes = CGDisplayAvailableModes( kCGDirectMainDisplay ); + ASSERT( modes ); + const CFIndex count = CFArrayGetCount( modes ); + + for( CFIndex i = 0; i < count; ++i ) + { + CFDictionaryRef dict = (CFDictionaryRef)CFArrayGetValueAtIndex( modes, i ); + CFNumberRef num = (CFNumberRef)CFDictionaryGetValue( dict, CFSTR("Width") ); + SInt32 width, height; + + if( !num || !CFNumberGetValue(num, kCFNumberSInt32Type, &width) ) + continue; + num = (CFNumberRef)CFDictionaryGetValue( dict, CFSTR("Height") ); + if( !num || !CFNumberGetValue(num, kCFNumberSInt32Type, &height) ) + continue; + + DisplayResolution res = { width, height }; + dr.s.insert( res ); + } + // Do not release modes! We don't own it here. } void LowLevelWindow_Cocoa::SwapBuffers() { // XXX I'm not sure if this is needed yet. - //NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - + //POOL; [[NSOpenGLContext currentContext] flushBuffer]; - //[pool release]; -} - -void LowLevelWindow_Cocoa::Update() -{ } diff --git a/stepmania/src/arch/LowLevelWindow/Selector_LowLevelWindow.h b/stepmania/src/arch/LowLevelWindow/Selector_LowLevelWindow.h index 1e16e8c43c..c3dad82224 100644 --- a/stepmania/src/arch/LowLevelWindow/Selector_LowLevelWindow.h +++ b/stepmania/src/arch/LowLevelWindow/Selector_LowLevelWindow.h @@ -6,6 +6,8 @@ #include "LowLevelWindow_Win32.h" #elif defined(HAVE_X11) #include "LowLevelWindow_X11.h" +#elif defined(MACOSX) +#include "LowLevelWindow_Cocoa.h" #elif defined(HAVE_SDL) #include "LowLevelWindow_SDL.h" #else