Make this usable. Some issues still exist. E.g., full screen does not work yet.

This commit is contained in:
Steve Checkoway
2006-01-14 05:49:26 +00:00
parent fac7b375c2
commit 7aa6310339
2 changed files with 144 additions and 276 deletions
@@ -8,6 +8,8 @@
typedef void *id; typedef void *id;
#endif #endif
typedef const struct __CFDictionary *CFDictionaryRef;
class LowLevelWindow_Cocoa : public LowLevelWindow class LowLevelWindow_Cocoa : public LowLevelWindow
{ {
VideoModeParams mCurrentParams; VideoModeParams mCurrentParams;
@@ -15,6 +17,7 @@ class LowLevelWindow_Cocoa : public LowLevelWindow
id mView; id mView;
id mFullScreenContext; id mFullScreenContext;
bool mSharingContexts; bool mSharingContexts;
CFDictionaryRef mCurrentDisplayMode;
public: public:
LowLevelWindow_Cocoa(); LowLevelWindow_Cocoa();
@@ -25,6 +28,9 @@ public:
void SwapBuffers(); void SwapBuffers();
VideoModeParams GetActualVideoModeParams() const { return mCurrentParams; } VideoModeParams GetActualVideoModeParams() const { return mCurrentParams; }
private:
void ShutDownFullScreen();
int ChangeDisplayMode( const VideoModeParams& p );
}; };
#ifdef ARCH_LOW_LEVEL_WINDOW #ifdef ARCH_LOW_LEVEL_WINDOW
@@ -7,7 +7,6 @@
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#import <mach-o/dyld.h> #import <mach-o/dyld.h>
static const int g_iStyleMask = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask; static const int g_iStyleMask = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask;
// Simple helper class // Simple helper class
@@ -41,7 +40,7 @@ static NSOpenGLPixelFormat *CreatePixelFormat( int bbp, bool windowed )
} }
LowLevelWindow_Cocoa::LowLevelWindow_Cocoa() : mView(nil), mFullScreenContext(nil) LowLevelWindow_Cocoa::LowLevelWindow_Cocoa() : mView(nil), mFullScreenContext(nil), mCurrentDisplayMode(NULL)
{ {
POOL; POOL;
NSRect rect = { {0, 0}, {0, 0} }; NSRect rect = { {0, 0}, {0, 0} };
@@ -51,6 +50,7 @@ LowLevelWindow_Cocoa::LowLevelWindow_Cocoa() : mView(nil), mFullScreenContext(ni
defer:YES]; defer:YES];
[mWindow setExcludedFromWindowsMenu:YES]; [mWindow setExcludedFromWindowsMenu:YES];
[mWindow useOptimizedDrawing:YES]; [mWindow useOptimizedDrawing:YES];
mCurrentParams.windowed = true; // We are essentially windowed to begin with.
} }
LowLevelWindow_Cocoa::~LowLevelWindow_Cocoa() LowLevelWindow_Cocoa::~LowLevelWindow_Cocoa()
@@ -74,6 +74,7 @@ LowLevelWindow_Cocoa::~LowLevelWindow_Cocoa()
void *LowLevelWindow_Cocoa::GetProcAddress( CString s ) void *LowLevelWindow_Cocoa::GetProcAddress( CString s )
{ {
CHECKPOINT;
// http://developer.apple.com/qa/qa2001/qa1188.html // http://developer.apple.com/qa/qa2001/qa1188.html
const CString& symbolName( '_' + s ); const CString& symbolName( '_' + s );
NSSymbol symbol = NULL; NSSymbol symbol = NULL;
@@ -85,294 +86,155 @@ void *LowLevelWindow_Cocoa::GetProcAddress( CString s )
CString LowLevelWindow_Cocoa::TryVideoMode( const VideoModeParams& p, bool& newDeviceOut ) CString LowLevelWindow_Cocoa::TryVideoMode( const VideoModeParams& p, bool& newDeviceOut )
{ {
CHECKPOINT;
POOL; POOL;
newDeviceOut = false; newDeviceOut = false;
NSRect contentRect = { { 0, 0 }, { p.width, p.height } }; 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) ); // Change the window and the title
if( changedWindowed )
{
if( p.windowed )
{
// No need to reset the size if it changes here.
[mWindow setContentSize:contentRect.size]; [mWindow setContentSize:contentRect.size];
[mWindow setTitle:[NSString stringWithUTF8String:p.sWindowTitle.c_str()]]; [mWindow setTitle:[NSString stringWithUTF8String:p.sWindowTitle.c_str()]];
if( changedView ) mCurrentParams.width = p.width;
mCurrentParams.height = p.height;
mCurrentParams.sWindowTitle = p.sWindowTitle;
ASSERT( p.bpp == 16 || p.bpp == 32 );
if( p.bpp != mCurrentParams.bpp || !mView )
{ {
// Create view.
NSOpenGLPixelFormat *pixelFormat = CreatePixelFormat( p.bpp, true ); NSOpenGLPixelFormat *pixelFormat = CreatePixelFormat( p.bpp, true );
if( pixelFormat == nil ) if( pixelFormat == nil )
return "Failed to set Pixel Format"; return "Failed to set the windowed pixel format.";
id nextView = [[NSOpenGLView alloc] initWithFrame:contentRect pixelFormat:pixelFormat]; id nextView = [[NSOpenGLView alloc] initWithFrame:contentRect pixelFormat:pixelFormat];
if( nextView == nil ) if( nextView == nil )
return "Failed to create OGL context."; return "Failed to create windowed OGL context.";
mView = nextView;
[mWindow setContentView:mView]; // This retains the view and releases the old one.
[mView release];
newDeviceOut = true; newDeviceOut = true;
} mView = nextView;
// Shut down full screen. Nothing else should fail. [mWindow setContentView:mView];
if( mFullScreenContext )
{
[mFullScreenContext clearDrawable];
CGDisplayErr err = CGReleaseAllDisplays();
ASSERT( err = kCGErrorSuccess ); // We need to recreate the full screen context as well.
if( changedView )
{
[mFullScreenContext release]; [mFullScreenContext release];
mFullScreenContext = nil; mFullScreenContext = nil;
mCurrentParams.bpp = p.bpp;
} }
if( p.windowed )
{
id context = [mView openGLContext];
ShutDownFullScreen();
[mWindow center];
[mWindow makeKeyAndOrderFront:nil];
[context update];
[context makeCurrentContext];
// Copy the rest of the state
mCurrentParams = p;
newDeviceOut = !mSharingContexts; newDeviceOut = !mSharingContexts;
}
// Set the current context
[[mView openGLContext] makeCurrentContext];
[mWindow center];
[mWindow performSelectorOnMainThread:@selector(makeKeyAndOrderFront:)
withObject:nil waitUntilDone:YES];
return CString(); return CString();
} }
// Okay, we're going full screen here. int result = ChangeDisplayMode( p );
// If the bpp has changed (or nothing has been created) we need to recreate everything.
id nextView = changedView ? nil : mView; if( result )
if( changedView )
{ {
NSOpenGLPixelFormat *pixelFormat = CreatePixelFormat( p.bpp, true ); return ssprintf( "Failed to switch to full screen:%d x %d @ %d. Error %d.",
p.width, p.height, p.rate, result );
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( mFullScreenContext == nil )
if( newContext == nil )
{ {
NSOpenGLPixelFormat *pixelFormat = CreatePixelFormat( p.bpp, false ); NSOpenGLPixelFormat *pixelFormat = CreatePixelFormat( p.bpp, false );
if( pixelFormat == nil ) 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." ); ShutDownFullScreen(); // If this fails, we need to leave full screen.
newContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil]; return "Failed to set full screen pixel format.";
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 mFullScreenContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat
shareContext:[mView openGLContext]]; shareContext:[mView openGLContext]];
if( !mFullScreenContext ) if( !(mSharingContexts = mFullScreenContext != nil) )
{ {
LOG->Warn( "Failed to share openGL contexts." ); LOG->Warn( "Failed to share openGL contexts." );
mFullScreenContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil]; mFullScreenContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil];
ASSERT( mFullScreenContext ); if( !mFullScreenContext )
newDeviceOut = true;
}
}
else if( changedSize )
{ {
[mFullScreenContext update]; ShutDownFullScreen(); // Same here.
return "Failed to create full screen openGL context.";
}
}
} }
if( mCurrentParams.windowed )
{
CGCaptureAllDisplays(); // Do not notify other apps of any changes.
[mFullScreenContext setFullScreen]; [mFullScreenContext setFullScreen];
[mFullScreenContext update];
[mFullScreenContext makeCurrentContext]; [mFullScreenContext makeCurrentContext];
} // Copy the rest of the state
mCurrentParams = p;
newDeviceOut = !mSharingContexts;
return CString();
} }
mCurrentParams = p; void LowLevelWindow_Cocoa::ShutDownFullScreen()
return ""; {
if( mCurrentParams.windowed )
return;
ASSERT( mCurrentDisplayMode );
[mFullScreenContext clearDrawable];
CGDisplayErr err = CGDisplaySwitchToMode( kCGDirectMainDisplay, mCurrentDisplayMode );
ASSERT( err == kCGErrorSuccess );
err = CGReleaseAllDisplays();
ASSERT( err == kCGErrorSuccess );
// We don't own this so we cannot release it.
mCurrentDisplayMode = NULL;
mCurrentParams.windowed = true;
}
int LowLevelWindow_Cocoa::ChangeDisplayMode( const VideoModeParams& p )
{
CFDictionaryRef mode = NULL;
CFDictionaryRef newMode;
CGDisplayErr err;
#define X(x) p.x == mCurrentParams.x
if( !mCurrentParams.windowed && X(bpp) && X(width) && X(height) && X(rate) )
return 0;
#undef X
if( mCurrentParams.windowed )
{
err = CGCaptureAllDisplays();
if( err != kCGErrorSuccess )
return int(err);
mode = CGDisplayCurrentMode( kCGDirectMainDisplay );
}
if( p.rate == REFRESH_DEFAULT )
newMode = CGDisplayBestModeForParameters( kCGDirectMainDisplay, p.bpp, p.width, p.height, NULL );
else
newMode = CGDisplayBestModeForParametersAndRefreshRate( kCGDirectMainDisplay, p.bpp,
p.width, p.height, p.rate, NULL );
err = CGDisplaySwitchToMode( kCGDirectMainDisplay, newMode );
if( err != kCGErrorSuccess )
return err; // We don't own mode, don't release it.
if( mCurrentParams.windowed )
{
mCurrentDisplayMode = mode;
mCurrentParams.windowed = false;
}
mCurrentParams.bpp = p.bpp;
mCurrentParams.width = p.width;
mCurrentParams.height = p.height;
mCurrentParams.rate = p.rate;
return 0;
} }
#endif
void LowLevelWindow_Cocoa::GetDisplayResolutions( DisplayResolutions &dr ) const void LowLevelWindow_Cocoa::GetDisplayResolutions( DisplayResolutions &dr ) const
{ {
@@ -401,6 +263,6 @@ void LowLevelWindow_Cocoa::GetDisplayResolutions( DisplayResolutions &dr ) const
void LowLevelWindow_Cocoa::SwapBuffers() void LowLevelWindow_Cocoa::SwapBuffers()
{ {
// XXX I'm not sure if this is needed yet. // XXX I'm not sure if this is needed yet.
//POOL; POOL;
[[NSOpenGLContext currentContext] flushBuffer]; [[NSOpenGLContext currentContext] flushBuffer];
} }