From 530a681e482e7af8f5ca8d77714cac22864e6258 Mon Sep 17 00:00:00 2001 From: Jason Felds Date: Mon, 2 May 2011 21:07:31 -0400 Subject: [PATCH] Guess I'm the line ending guardian. --- .../LowLevelWindow/LowLevelWindow_MacOSX.mm | 1268 ++++++++--------- 1 file changed, 634 insertions(+), 634 deletions(-) diff --git a/src/arch/LowLevelWindow/LowLevelWindow_MacOSX.mm b/src/arch/LowLevelWindow/LowLevelWindow_MacOSX.mm index c596739d23..063bb84cad 100644 --- a/src/arch/LowLevelWindow/LowLevelWindow_MacOSX.mm +++ b/src/arch/LowLevelWindow/LowLevelWindow_MacOSX.mm @@ -1,634 +1,634 @@ -#import "global.h" -#import "LowLevelWindow_MacOSX.h" -#import "DisplayResolutions.h" -#import "RageUtil.h" -#import "RageThreads.h" -#import "RageDisplay_Legacy_Helpers.h" -#import "arch/ArchHooks/ArchHooks.h" - -#import -#import -#import -#import - -// Bad header! -extern "C" { -#include -} - - -static const unsigned int g_iStyleMask = NSTitledWindowMask | NSClosableWindowMask | - NSMiniaturizableWindowMask | NSResizableWindowMask; -static bool g_bResized; -static int g_iWidth; -static int g_iHeight; -static RageMutex g_ResizeLock( "Window resize lock." ); - -// Simple helper class -class AutoreleasePool -{ - AutoreleasePool( const AutoreleasePool& ); - AutoreleasePool &operator=( const AutoreleasePool& ); - NSAutoreleasePool *m_Pool; - -public: - AutoreleasePool() { m_Pool = [[NSAutoreleasePool alloc] init]; } - ~AutoreleasePool() { [m_Pool release]; } -}; - -#define POOL AutoreleasePool UNIQUE_NAME(pool) - -// Window delegate class -@interface SMWindowDelegate : NSObject -{ - @public - NSWindow *m_Window; -} -- (void) windowDidBecomeKey:(NSNotification *)aNotification; -- (void) windowDidResignKey:(NSNotification *)aNotification; -- (void) windowWillClose:(NSNotification *)aNotification; -- (void) windowDidResize:(NSNotification *)aNotification; -// XXX maybe use whichever screen contains the window? Hard for me to test though. -//- (void) windowDidChangeScreen:(NSNotification *)aNotification; - -// Helper methods to perform actions on the main thread. -- (void) setupWindow; -- (void) closeWindow; -- (void) setParams:(NSValue *)params; -@end - -@implementation SMWindowDelegate - -- (void) windowDidBecomeKey:(NSNotification *)aNotification -{ - HOOKS->SetHasFocus( true ); -} - -- (void) windowDidResignKey:(NSNotification *)aNotification -{ - HOOKS->SetHasFocus( false ); -} - -- (void) windowWillClose:(NSNotification *)aNotification -{ - ArchHooks::SetUserQuit(); -} - -- (void) windowDidResize:(NSNotification *)aNotification -{ - id window = [aNotification object]; - NSSize size = [NSWindow contentRectForFrameRect:[window frame] styleMask:g_iStyleMask].size; - - LockMut( g_ResizeLock ); - g_bResized = true; - g_iWidth = int( size.width ); - g_iHeight = int( size.height ); -} - -- (void) setupWindow -{ - NSRect rect = NSMakeRect( 0, 0, 0, 0 ); - m_Window = [[NSWindow alloc] initWithContentRect:rect - styleMask:g_iStyleMask - backing:NSBackingStoreBuffered - defer:YES]; - - [m_Window setExcludedFromWindowsMenu:YES]; - [m_Window useOptimizedDrawing:YES]; - [m_Window setReleasedWhenClosed:NO]; - [m_Window setDelegate:self]; -} - -- (void) closeWindow -{ - [m_Window setDelegate:nil]; - [m_Window close]; - [m_Window release]; -} - -- (void) setParams:(NSValue *)params -{ - const VideoModeParams &p = *(const VideoModeParams *)[params pointerValue]; - NSRect contentRect = { { 0, 0 }, { p.width, p.height } }; - - [m_Window setContentSize:contentRect.size]; - [m_Window setTitle:[NSString stringWithUTF8String:p.sWindowTitle.c_str()]]; - [m_Window center]; - [m_Window makeKeyAndOrderFront:nil]; -} -@end - -enum GLContextType -{ - WINDOWED, - FULL_SCREEN, - PIXEL_BUFFER -}; - -static NSOpenGLContext *CreateOGLContext( GLContextType type, int iColorSize, int iAlphaSize, int iDepthSize, NSOpenGLContext *share, bool &bShared ) -{ - NSOpenGLPixelFormatAttribute attrs[] = { - NSOpenGLPFANoRecovery, // so we can share with the full screen context - NSOpenGLPFAAccelerated, - NSOpenGLPFAMinimumPolicy, - NSOpenGLPFAColorSize, NSOpenGLPixelFormatAttribute(iColorSize), - NSOpenGLPFAAlphaSize, NSOpenGLPixelFormatAttribute(iAlphaSize), - NSOpenGLPFADepthSize, NSOpenGLPixelFormatAttribute(iDepthSize), -/* - NSOpenGLPFAMultisample, - NSOpenGLPFASampleBuffers, (NSOpenGLPixelFormatAttribute)1, - NSOpenGLPFASamples, (NSOpenGLPixelFormatAttribute)4, -*/ - NSOpenGLPixelFormatAttribute(0), // 9 - NSOpenGLPixelFormatAttribute(0), // 10 - NSOpenGLPixelFormatAttribute(0), // 11 - NSOpenGLPixelFormatAttribute(0), // 12 - NSOpenGLPixelFormatAttribute(0) // Must be at the end. - }; - const int n = 9; // The first noncommon index. - - switch( type ) - { - case WINDOWED: - attrs[n+0] = NSOpenGLPFAWindow; - attrs[n+1] = NSOpenGLPFADoubleBuffer; - break; - case FULL_SCREEN: - attrs[n+0] = NSOpenGLPFAFullScreen; - attrs[n+1] = NSOpenGLPFADoubleBuffer; - attrs[n+2] = NSOpenGLPFAScreenMask; - attrs[n+3] = NSOpenGLPixelFormatAttribute( CGDisplayIDToOpenGLDisplayMask(kCGDirectMainDisplay) ); - break; - case PIXEL_BUFFER: - attrs[n+0] = NSOpenGLPFAOffScreen; - attrs[n+1] = NSOpenGLPFAPixelBuffer; - break; - } - - NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs]; - - if( !pixelFormat ) - return nil; - - NSOpenGLContext *context = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:share]; - - bShared = share && context; - if( !context ) - context = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil]; - - [pixelFormat release]; - - return context; -} - - -class RenderTarget_MacOSX : public RenderTarget -{ -public: - RenderTarget_MacOSX( id shareContext ); - ~RenderTarget_MacOSX(); - void Create( const RenderTargetParam ¶m, int &iTextureWidthOut, int &iTextureHeightOut ); - unsigned GetTexture() const { return m_iTexHandle; } - void StartRenderingTo(); - void FinishRenderingTo(); - -private: - NSOpenGLContext *m_ShareContext, *m_OldContext, *m_PBufferContext; - GLuint m_iTexHandle; - int m_iWidth, m_iHeight; -}; - -RenderTarget_MacOSX::RenderTarget_MacOSX( id shareContext ) -{ - m_ShareContext = shareContext; - m_OldContext = nil; - m_PBufferContext = nil; - m_iTexHandle = 0; - m_iWidth = 0; - m_iHeight = 0; -} - -RenderTarget_MacOSX::~RenderTarget_MacOSX() -{ - POOL; - [m_PBufferContext release]; - if( m_iTexHandle ) - glDeleteTextures( 1, &m_iTexHandle ); -} - -void RenderTarget_MacOSX::Create( const RenderTargetParam ¶m, int &iTextureWidthOut, int &iTextureHeightOut ) -{ - POOL; - m_iWidth = param.iWidth; - m_iHeight = param.iHeight; - - // PBuffer needs to be a power of 2. - int iTextureWidth = power_of_two( param.iWidth ); - int iTextureHeight = power_of_two( param.iHeight ); - - // Create the PBuffer. - unsigned long format = param.bWithAlpha? GL_RGBA:GL_RGB; - NSOpenGLPixelBuffer *PBuffer = [[NSOpenGLPixelBuffer alloc] initWithTextureTarget:GL_TEXTURE_2D - textureInternalFormat:format - textureMaxMipMapLevel:0 // No idea. - pixelsWide:iTextureWidth - pixelsHigh:iTextureHeight]; - DEBUG_ASSERT( PBuffer ); - - // Create an OGL context. - bool bShared = false; - m_PBufferContext = CreateOGLContext( PIXEL_BUFFER, 24, param.bWithAlpha? 8:0, param.bWithDepthBuffer? 16:0, m_ShareContext, bShared ); - DEBUG_ASSERT( m_PBufferContext ); - DEBUG_ASSERT( bShared ); - [m_PBufferContext setPixelBuffer:PBuffer cubeMapFace:0 mipMapLevel:0 - currentVirtualScreen:[m_ShareContext currentVirtualScreen]]; - [PBuffer release]; // XXX: Hopefully this is retained by the PBufferContext. - - glGenTextures( 1, &m_iTexHandle ); - glBindTexture( GL_TEXTURE_2D, m_iTexHandle ); - - while( glGetError() != GL_NO_ERROR ) - ; - - iTextureWidthOut = iTextureWidth; - iTextureHeightOut = iTextureHeight; - - glTexImage2D( GL_TEXTURE_2D, 0, param.bWithAlpha? GL_RGBA8:GL_RGB8, - iTextureWidth, iTextureHeight, 0, param.bWithAlpha? GL_RGBA:GL_RGB, - GL_UNSIGNED_BYTE, NULL ); - GLenum error = glGetError(); - ASSERT_M( error == GL_NO_ERROR, RageDisplay_Legacy_Helpers::GLToString(error) ); - - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); -} - -void RenderTarget_MacOSX::StartRenderingTo() -{ - DEBUG_ASSERT( !m_OldContext ); - m_OldContext = [NSOpenGLContext currentContext]; - [m_PBufferContext makeCurrentContext]; - glViewport( 0, 0, m_iWidth, m_iHeight ); -} - -void RenderTarget_MacOSX::FinishRenderingTo() -{ - DEBUG_ASSERT( m_OldContext ); - glBindTexture( GL_TEXTURE_2D, m_iTexHandle ); - - while( glGetError() != GL_NO_ERROR ) - ; - - glCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 0, 0, m_iWidth, m_iHeight ); - - GLenum error = glGetError(); - ASSERT_M( error == GL_NO_ERROR, RageDisplay_Legacy_Helpers::GLToString(error) ); - - glBindTexture( GL_TEXTURE_2D, 0 ); - - [m_OldContext makeCurrentContext]; - m_OldContext = nil; -} - - -LowLevelWindow_MacOSX::LowLevelWindow_MacOSX() : m_Context(nil), m_BGContext(nil), m_CurrentDisplayMode(NULL), m_DisplayID(0) -{ - POOL; - m_WindowDelegate = [[SMWindowDelegate alloc] init]; - [m_WindowDelegate performSelectorOnMainThread:@selector(setupWindow) withObject:nil waitUntilDone:YES]; - - m_CurrentParams.windowed = true; // We are essentially windowed to begin with. - SetActualParamsFromMode( CGDisplayCurrentMode(kCGDirectMainDisplay) ); - HOOKS->SetHasFocus( [NSApp isActive] ); -} - -LowLevelWindow_MacOSX::~LowLevelWindow_MacOSX() -{ - POOL; - ShutDownFullScreen(); - - [m_Context clearDrawable]; - [m_Context release]; - [m_BGContext clearDrawable]; - [m_BGContext release]; - [m_WindowDelegate performSelectorOnMainThread:@selector(closeWindow) withObject:nil waitUntilDone:YES]; - [m_WindowDelegate release]; -} - -void *LowLevelWindow_MacOSX::GetProcAddress( RString s ) -{ - // http://developer.apple.com/qa/qa2001/qa1188.html - // Both functions mentioned in there are deprecated in 10.4. - const RString& symbolName( '_' + s ); - const uint32_t count = _dyld_image_count(); - NSSymbol symbol = NULL; - const uint32_t options = NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR; - - for( uint32_t i = 0; i < count && !symbol; ++i ) - symbol = NSLookupSymbolInImage( _dyld_get_image_header(i), symbolName, options ); - return symbol ? NSAddressOfSymbol( symbol ) : NULL; -} - -RString LowLevelWindow_MacOSX::TryVideoMode( const VideoModeParams& p, bool& newDeviceOut ) -{ - // Always set these params. - m_CurrentParams.bSmoothLines = p.bSmoothLines; - m_CurrentParams.bTrilinearFiltering = p.bTrilinearFiltering; - m_CurrentParams.bAnisotropicFiltering = p.bAnisotropicFiltering; - m_CurrentParams.interlaced = p.interlaced; - m_CurrentParams.PAL = p.PAL; - m_CurrentParams.fDisplayAspectRatio = p.fDisplayAspectRatio; - -#define X(x) p.x != m_CurrentParams.x - const bool bRebuildContext = X(bpp) || X(windowed) || !m_Context; - const bool bChangeMode = X(width) || X(height) || X(rate) || bRebuildContext; - const bool bChangeVsync = X(vsync) || bRebuildContext; -#undef X - - if( !bChangeMode && !bChangeVsync ) - return RString(); - - POOL; - newDeviceOut = false; - - ASSERT( p.bpp == 16 || p.bpp == 32 ); - - // If we don't have focus, we cannot be full screen. - if( p.windowed || !HOOKS->AppHasFocus() ) - { - if( bRebuildContext ) - { - bool bShared; - NSOpenGLContext *newContext = CreateOGLContext( WINDOWED, p.bpp == 16? 16:24, p.bpp == 16? 1:8, 16, m_Context, bShared ); - - if( !newContext ) - return "Failed to create OGL context."; - ShutDownFullScreen(); - [m_Context release]; - m_Context = newContext; - newDeviceOut = !bShared; - m_CurrentParams.bpp = p.bpp; - [m_BGContext release]; - m_BGContext = nil; - m_BGContext = CreateOGLContext( WINDOWED, p.bpp == 16? 16:24, p.bpp == 16? 1:8, 16, m_Context, bShared ); - - if( m_BGContext && !bShared ) - { - [m_BGContext release]; - m_BGContext = nil; - } - } - - [m_WindowDelegate performSelectorOnMainThread:@selector(setParams:) withObject:[NSValue valueWithPointer:&p] waitUntilDone:YES]; - [m_Context setView:[((SMWindowDelegate *)m_WindowDelegate)->m_Window contentView]]; - [m_Context update]; - [m_Context makeCurrentContext]; - m_CurrentParams.windowed = true; - SetActualParamsFromMode( CGDisplayCurrentMode(kCGDirectMainDisplay) ); - m_CurrentParams.vsync = p.vsync; // hack - - return RString(); - } - if( bChangeMode ) - { - int result = ChangeDisplayMode( p ); - - if( result ) - return ssprintf( "Failed to switch to full screen:%d x %d @ %d. Error %d.", - p.width, p.height, p.rate, result ); - } - - if( bRebuildContext ) - { - bool bShared; - NSOpenGLContext *newContext = CreateOGLContext( FULL_SCREEN, p.bpp == 16? 16:24, p.bpp == 16? 1:8, 16, m_Context, bShared ); - - if( !newContext ) - return "Failed to create full screen OGL context."; - [m_Context clearDrawable]; - [m_Context release]; - m_Context = newContext; - newDeviceOut = !bShared; - m_CurrentParams.bpp = p.bpp; - [m_BGContext release]; - m_BGContext = CreateOGLContext( FULL_SCREEN, p.bpp == 16? 16:24, p.bpp == 16? 1:8, 16, m_Context, bShared ); - - if( m_BGContext && !bShared ) - { - [m_BGContext release]; - m_BGContext = nil; - } - } - - [m_Context setFullScreen]; - [m_Context update]; - [m_Context makeCurrentContext]; - - if( bChangeVsync ) - { - long swap = p.vsync ? 1 : 0; - - [m_Context setValues:&swap forParameter:NSOpenGLCPSwapInterval]; - m_CurrentParams.vsync = p.vsync; - } - - return RString(); -} - -void LowLevelWindow_MacOSX::ShutDownFullScreen() -{ - if( m_CurrentParams.windowed ) - return; - ASSERT( m_CurrentDisplayMode ); - - // Clear the front and back framebuffers before switching out of FullScreen mode. - // (This is not strictly necessary, but avoids an untidy flash of garbage.) - glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); - glClear( GL_COLOR_BUFFER_BIT ); - [m_Context flushBuffer]; - glClear( GL_COLOR_BUFFER_BIT ); - [m_Context flushBuffer]; - - [NSOpenGLContext clearCurrentContext]; - [m_Context clearDrawable]; - [m_BGContext clearDrawable]; - - CGDisplayErr err = CGDisplaySwitchToMode( kCGDirectMainDisplay, m_CurrentDisplayMode ); - - ASSERT( err == kCGErrorSuccess ); - CGDisplayShowCursor( kCGDirectMainDisplay ); - err = CGDisplayRelease( m_DisplayID ); - ASSERT( err == kCGErrorSuccess ); - SetActualParamsFromMode( m_CurrentDisplayMode ); - // We don't own this so we cannot release it. - m_CurrentDisplayMode = NULL; - m_CurrentParams.windowed = true; -} - -int LowLevelWindow_MacOSX::ChangeDisplayMode( const VideoModeParams& p ) -{ - CFDictionaryRef mode = NULL; - CFDictionaryRef newMode; - CGDisplayErr err; - - if( !m_CurrentDisplayMode ) - { - m_DisplayID = CGMainDisplayID(); - if( (err = CGDisplayCapture(m_DisplayID)) != kCGErrorSuccess ) - return err; - // Only hide the first time we go to full screen. - CGDisplayHideCursor( kCGDirectMainDisplay ); - 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( !m_CurrentDisplayMode ) - m_CurrentDisplayMode = mode; - m_CurrentParams.windowed = false; - SetActualParamsFromMode( newMode ); - - return 0; -} - -void LowLevelWindow_MacOSX::SetActualParamsFromMode( CFDictionaryRef mode ) -{ - SInt32 rate; - bool ret = CFNumberGetValue( (CFNumberRef)CFDictionaryGetValue(mode, CFSTR("RefreshRate")), - kCFNumberSInt32Type, &rate ); - - if( !ret || rate == 0) - rate = 60; - m_CurrentParams.rate = rate; - - if( !m_CurrentParams.windowed ) - { - long swap; - - m_CurrentParams.width = CGDisplayPixelsWide( kCGDirectMainDisplay ); - m_CurrentParams.height = CGDisplayPixelsHigh( kCGDirectMainDisplay ); - CGLGetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval, &swap ); - m_CurrentParams.vsync = swap != 0; - } - else - { - NSSize size = [[((SMWindowDelegate *)m_WindowDelegate)->m_Window contentView] frame].size; - - m_CurrentParams.width = int(size.width); - m_CurrentParams.height = int(size.height); - } - - m_CurrentParams.bpp = CGDisplayBitsPerPixel( kCGDirectMainDisplay ); -} - -static int GetIntValue( CFTypeRef r ) -{ - int ret; - - if( !r || CFGetTypeID(r) != CFNumberGetTypeID() || !CFNumberGetValue(CFNumberRef(r), kCFNumberIntType, &ret) ) - return 0; - return ret; -} - -static bool GetBoolValue( CFTypeRef r ) -{ - return r && CFGetTypeID( r ) == CFBooleanGetTypeID() && CFBooleanGetValue( CFBooleanRef(r) ); -} - -void LowLevelWindow_MacOSX::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 ); - int width = GetIntValue( CFDictionaryGetValue(dict, kCGDisplayWidth) ); - int height = GetIntValue( CFDictionaryGetValue(dict, kCGDisplayHeight) ); - CFTypeRef safe = CFDictionaryGetValue( dict, kCGDisplayModeIsSafeForHardware ); - bool stretched = GetBoolValue( CFDictionaryGetValue(dict, kCGDisplayModeIsStretched) ); - - if( !width || !height ) - continue; - if( safe && !GetBoolValue( safe ) ) - continue; - DisplayResolution res = { width, height, stretched }; - dr.insert( res ); - } - // Do not release modes! We don't own them here. -} - -void LowLevelWindow_MacOSX::SwapBuffers() -{ - CGLFlushDrawable( CGLGetCurrentContext() ); -} - -void LowLevelWindow_MacOSX::Update() -{ - // Keep the system from sleeping or the screen saver from activating. - UpdateSystemActivity( IdleActivity ); - - LockMutex lock( g_ResizeLock ); - if( likely(!g_bResized) ) - return; - g_bResized = false; - if( m_CurrentParams.width == g_iWidth && m_CurrentParams.height == g_iHeight ) - return; - m_CurrentParams.width = g_iWidth; - m_CurrentParams.height = g_iHeight; - lock.Unlock(); // Unlock before calling ResolutionChanged(). - [m_Context update]; - DISPLAY->ResolutionChanged(); -} - -RenderTarget *LowLevelWindow_MacOSX::CreateRenderTarget() -{ - return new RenderTarget_MacOSX( m_Context ); -} - -void LowLevelWindow_MacOSX::BeginConcurrentRendering() -{ - if( m_CurrentParams.windowed ) - [m_BGContext setView:[((SMWindowDelegate *)m_WindowDelegate)->m_Window contentView]]; - else - [m_BGContext setFullScreen]; - [m_BGContext makeCurrentContext]; -} - -/* - * (c) 2005-2006, 2008 Steve Checkoway - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, and/or sell copies of the Software, and to permit persons to - * whom the Software is furnished to do so, provided that the above - * copyright notice(s) and this permission notice appear in all copies of - * the Software and that both the above copyright notice(s) and this - * permission notice appear in supporting documentation. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF - * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS - * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT - * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS - * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ +#import "global.h" +#import "LowLevelWindow_MacOSX.h" +#import "DisplayResolutions.h" +#import "RageUtil.h" +#import "RageThreads.h" +#import "RageDisplay_Legacy_Helpers.h" +#import "arch/ArchHooks/ArchHooks.h" + +#import +#import +#import +#import + +// Bad header! +extern "C" { +#include +} + + +static const unsigned int g_iStyleMask = NSTitledWindowMask | NSClosableWindowMask | + NSMiniaturizableWindowMask | NSResizableWindowMask; +static bool g_bResized; +static int g_iWidth; +static int g_iHeight; +static RageMutex g_ResizeLock( "Window resize lock." ); + +// Simple helper class +class AutoreleasePool +{ + AutoreleasePool( const AutoreleasePool& ); + AutoreleasePool &operator=( const AutoreleasePool& ); + NSAutoreleasePool *m_Pool; + +public: + AutoreleasePool() { m_Pool = [[NSAutoreleasePool alloc] init]; } + ~AutoreleasePool() { [m_Pool release]; } +}; + +#define POOL AutoreleasePool UNIQUE_NAME(pool) + +// Window delegate class +@interface SMWindowDelegate : NSObject +{ + @public + NSWindow *m_Window; +} +- (void) windowDidBecomeKey:(NSNotification *)aNotification; +- (void) windowDidResignKey:(NSNotification *)aNotification; +- (void) windowWillClose:(NSNotification *)aNotification; +- (void) windowDidResize:(NSNotification *)aNotification; +// XXX maybe use whichever screen contains the window? Hard for me to test though. +//- (void) windowDidChangeScreen:(NSNotification *)aNotification; + +// Helper methods to perform actions on the main thread. +- (void) setupWindow; +- (void) closeWindow; +- (void) setParams:(NSValue *)params; +@end + +@implementation SMWindowDelegate + +- (void) windowDidBecomeKey:(NSNotification *)aNotification +{ + HOOKS->SetHasFocus( true ); +} + +- (void) windowDidResignKey:(NSNotification *)aNotification +{ + HOOKS->SetHasFocus( false ); +} + +- (void) windowWillClose:(NSNotification *)aNotification +{ + ArchHooks::SetUserQuit(); +} + +- (void) windowDidResize:(NSNotification *)aNotification +{ + id window = [aNotification object]; + NSSize size = [NSWindow contentRectForFrameRect:[window frame] styleMask:g_iStyleMask].size; + + LockMut( g_ResizeLock ); + g_bResized = true; + g_iWidth = int( size.width ); + g_iHeight = int( size.height ); +} + +- (void) setupWindow +{ + NSRect rect = NSMakeRect( 0, 0, 0, 0 ); + m_Window = [[NSWindow alloc] initWithContentRect:rect + styleMask:g_iStyleMask + backing:NSBackingStoreBuffered + defer:YES]; + + [m_Window setExcludedFromWindowsMenu:YES]; + [m_Window useOptimizedDrawing:YES]; + [m_Window setReleasedWhenClosed:NO]; + [m_Window setDelegate:self]; +} + +- (void) closeWindow +{ + [m_Window setDelegate:nil]; + [m_Window close]; + [m_Window release]; +} + +- (void) setParams:(NSValue *)params +{ + const VideoModeParams &p = *(const VideoModeParams *)[params pointerValue]; + NSRect contentRect = { { 0, 0 }, { p.width, p.height } }; + + [m_Window setContentSize:contentRect.size]; + [m_Window setTitle:[NSString stringWithUTF8String:p.sWindowTitle.c_str()]]; + [m_Window center]; + [m_Window makeKeyAndOrderFront:nil]; +} +@end + +enum GLContextType +{ + WINDOWED, + FULL_SCREEN, + PIXEL_BUFFER +}; + +static NSOpenGLContext *CreateOGLContext( GLContextType type, int iColorSize, int iAlphaSize, int iDepthSize, NSOpenGLContext *share, bool &bShared ) +{ + NSOpenGLPixelFormatAttribute attrs[] = { + NSOpenGLPFANoRecovery, // so we can share with the full screen context + NSOpenGLPFAAccelerated, + NSOpenGLPFAMinimumPolicy, + NSOpenGLPFAColorSize, NSOpenGLPixelFormatAttribute(iColorSize), + NSOpenGLPFAAlphaSize, NSOpenGLPixelFormatAttribute(iAlphaSize), + NSOpenGLPFADepthSize, NSOpenGLPixelFormatAttribute(iDepthSize), +/* + NSOpenGLPFAMultisample, + NSOpenGLPFASampleBuffers, (NSOpenGLPixelFormatAttribute)1, + NSOpenGLPFASamples, (NSOpenGLPixelFormatAttribute)4, +*/ + NSOpenGLPixelFormatAttribute(0), // 9 + NSOpenGLPixelFormatAttribute(0), // 10 + NSOpenGLPixelFormatAttribute(0), // 11 + NSOpenGLPixelFormatAttribute(0), // 12 + NSOpenGLPixelFormatAttribute(0) // Must be at the end. + }; + const int n = 9; // The first noncommon index. + + switch( type ) + { + case WINDOWED: + attrs[n+0] = NSOpenGLPFAWindow; + attrs[n+1] = NSOpenGLPFADoubleBuffer; + break; + case FULL_SCREEN: + attrs[n+0] = NSOpenGLPFAFullScreen; + attrs[n+1] = NSOpenGLPFADoubleBuffer; + attrs[n+2] = NSOpenGLPFAScreenMask; + attrs[n+3] = NSOpenGLPixelFormatAttribute( CGDisplayIDToOpenGLDisplayMask(kCGDirectMainDisplay) ); + break; + case PIXEL_BUFFER: + attrs[n+0] = NSOpenGLPFAOffScreen; + attrs[n+1] = NSOpenGLPFAPixelBuffer; + break; + } + + NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs]; + + if( !pixelFormat ) + return nil; + + NSOpenGLContext *context = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:share]; + + bShared = share && context; + if( !context ) + context = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil]; + + [pixelFormat release]; + + return context; +} + + +class RenderTarget_MacOSX : public RenderTarget +{ +public: + RenderTarget_MacOSX( id shareContext ); + ~RenderTarget_MacOSX(); + void Create( const RenderTargetParam ¶m, int &iTextureWidthOut, int &iTextureHeightOut ); + unsigned GetTexture() const { return m_iTexHandle; } + void StartRenderingTo(); + void FinishRenderingTo(); + +private: + NSOpenGLContext *m_ShareContext, *m_OldContext, *m_PBufferContext; + GLuint m_iTexHandle; + int m_iWidth, m_iHeight; +}; + +RenderTarget_MacOSX::RenderTarget_MacOSX( id shareContext ) +{ + m_ShareContext = shareContext; + m_OldContext = nil; + m_PBufferContext = nil; + m_iTexHandle = 0; + m_iWidth = 0; + m_iHeight = 0; +} + +RenderTarget_MacOSX::~RenderTarget_MacOSX() +{ + POOL; + [m_PBufferContext release]; + if( m_iTexHandle ) + glDeleteTextures( 1, &m_iTexHandle ); +} + +void RenderTarget_MacOSX::Create( const RenderTargetParam ¶m, int &iTextureWidthOut, int &iTextureHeightOut ) +{ + POOL; + m_iWidth = param.iWidth; + m_iHeight = param.iHeight; + + // PBuffer needs to be a power of 2. + int iTextureWidth = power_of_two( param.iWidth ); + int iTextureHeight = power_of_two( param.iHeight ); + + // Create the PBuffer. + unsigned long format = param.bWithAlpha? GL_RGBA:GL_RGB; + NSOpenGLPixelBuffer *PBuffer = [[NSOpenGLPixelBuffer alloc] initWithTextureTarget:GL_TEXTURE_2D + textureInternalFormat:format + textureMaxMipMapLevel:0 // No idea. + pixelsWide:iTextureWidth + pixelsHigh:iTextureHeight]; + DEBUG_ASSERT( PBuffer ); + + // Create an OGL context. + bool bShared = false; + m_PBufferContext = CreateOGLContext( PIXEL_BUFFER, 24, param.bWithAlpha? 8:0, param.bWithDepthBuffer? 16:0, m_ShareContext, bShared ); + DEBUG_ASSERT( m_PBufferContext ); + DEBUG_ASSERT( bShared ); + [m_PBufferContext setPixelBuffer:PBuffer cubeMapFace:0 mipMapLevel:0 + currentVirtualScreen:[m_ShareContext currentVirtualScreen]]; + [PBuffer release]; // XXX: Hopefully this is retained by the PBufferContext. + + glGenTextures( 1, &m_iTexHandle ); + glBindTexture( GL_TEXTURE_2D, m_iTexHandle ); + + while( glGetError() != GL_NO_ERROR ) + ; + + iTextureWidthOut = iTextureWidth; + iTextureHeightOut = iTextureHeight; + + glTexImage2D( GL_TEXTURE_2D, 0, param.bWithAlpha? GL_RGBA8:GL_RGB8, + iTextureWidth, iTextureHeight, 0, param.bWithAlpha? GL_RGBA:GL_RGB, + GL_UNSIGNED_BYTE, NULL ); + GLenum error = glGetError(); + ASSERT_M( error == GL_NO_ERROR, RageDisplay_Legacy_Helpers::GLToString(error) ); + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); +} + +void RenderTarget_MacOSX::StartRenderingTo() +{ + DEBUG_ASSERT( !m_OldContext ); + m_OldContext = [NSOpenGLContext currentContext]; + [m_PBufferContext makeCurrentContext]; + glViewport( 0, 0, m_iWidth, m_iHeight ); +} + +void RenderTarget_MacOSX::FinishRenderingTo() +{ + DEBUG_ASSERT( m_OldContext ); + glBindTexture( GL_TEXTURE_2D, m_iTexHandle ); + + while( glGetError() != GL_NO_ERROR ) + ; + + glCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 0, 0, m_iWidth, m_iHeight ); + + GLenum error = glGetError(); + ASSERT_M( error == GL_NO_ERROR, RageDisplay_Legacy_Helpers::GLToString(error) ); + + glBindTexture( GL_TEXTURE_2D, 0 ); + + [m_OldContext makeCurrentContext]; + m_OldContext = nil; +} + + +LowLevelWindow_MacOSX::LowLevelWindow_MacOSX() : m_Context(nil), m_BGContext(nil), m_CurrentDisplayMode(NULL), m_DisplayID(0) +{ + POOL; + m_WindowDelegate = [[SMWindowDelegate alloc] init]; + [m_WindowDelegate performSelectorOnMainThread:@selector(setupWindow) withObject:nil waitUntilDone:YES]; + + m_CurrentParams.windowed = true; // We are essentially windowed to begin with. + SetActualParamsFromMode( CGDisplayCurrentMode(kCGDirectMainDisplay) ); + HOOKS->SetHasFocus( [NSApp isActive] ); +} + +LowLevelWindow_MacOSX::~LowLevelWindow_MacOSX() +{ + POOL; + ShutDownFullScreen(); + + [m_Context clearDrawable]; + [m_Context release]; + [m_BGContext clearDrawable]; + [m_BGContext release]; + [m_WindowDelegate performSelectorOnMainThread:@selector(closeWindow) withObject:nil waitUntilDone:YES]; + [m_WindowDelegate release]; +} + +void *LowLevelWindow_MacOSX::GetProcAddress( RString s ) +{ + // http://developer.apple.com/qa/qa2001/qa1188.html + // Both functions mentioned in there are deprecated in 10.4. + const RString& symbolName( '_' + s ); + const uint32_t count = _dyld_image_count(); + NSSymbol symbol = NULL; + const uint32_t options = NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR; + + for( uint32_t i = 0; i < count && !symbol; ++i ) + symbol = NSLookupSymbolInImage( _dyld_get_image_header(i), symbolName, options ); + return symbol ? NSAddressOfSymbol( symbol ) : NULL; +} + +RString LowLevelWindow_MacOSX::TryVideoMode( const VideoModeParams& p, bool& newDeviceOut ) +{ + // Always set these params. + m_CurrentParams.bSmoothLines = p.bSmoothLines; + m_CurrentParams.bTrilinearFiltering = p.bTrilinearFiltering; + m_CurrentParams.bAnisotropicFiltering = p.bAnisotropicFiltering; + m_CurrentParams.interlaced = p.interlaced; + m_CurrentParams.PAL = p.PAL; + m_CurrentParams.fDisplayAspectRatio = p.fDisplayAspectRatio; + +#define X(x) p.x != m_CurrentParams.x + const bool bRebuildContext = X(bpp) || X(windowed) || !m_Context; + const bool bChangeMode = X(width) || X(height) || X(rate) || bRebuildContext; + const bool bChangeVsync = X(vsync) || bRebuildContext; +#undef X + + if( !bChangeMode && !bChangeVsync ) + return RString(); + + POOL; + newDeviceOut = false; + + ASSERT( p.bpp == 16 || p.bpp == 32 ); + + // If we don't have focus, we cannot be full screen. + if( p.windowed || !HOOKS->AppHasFocus() ) + { + if( bRebuildContext ) + { + bool bShared; + NSOpenGLContext *newContext = CreateOGLContext( WINDOWED, p.bpp == 16? 16:24, p.bpp == 16? 1:8, 16, m_Context, bShared ); + + if( !newContext ) + return "Failed to create OGL context."; + ShutDownFullScreen(); + [m_Context release]; + m_Context = newContext; + newDeviceOut = !bShared; + m_CurrentParams.bpp = p.bpp; + [m_BGContext release]; + m_BGContext = nil; + m_BGContext = CreateOGLContext( WINDOWED, p.bpp == 16? 16:24, p.bpp == 16? 1:8, 16, m_Context, bShared ); + + if( m_BGContext && !bShared ) + { + [m_BGContext release]; + m_BGContext = nil; + } + } + + [m_WindowDelegate performSelectorOnMainThread:@selector(setParams:) withObject:[NSValue valueWithPointer:&p] waitUntilDone:YES]; + [m_Context setView:[((SMWindowDelegate *)m_WindowDelegate)->m_Window contentView]]; + [m_Context update]; + [m_Context makeCurrentContext]; + m_CurrentParams.windowed = true; + SetActualParamsFromMode( CGDisplayCurrentMode(kCGDirectMainDisplay) ); + m_CurrentParams.vsync = p.vsync; // hack + + return RString(); + } + if( bChangeMode ) + { + int result = ChangeDisplayMode( p ); + + if( result ) + return ssprintf( "Failed to switch to full screen:%d x %d @ %d. Error %d.", + p.width, p.height, p.rate, result ); + } + + if( bRebuildContext ) + { + bool bShared; + NSOpenGLContext *newContext = CreateOGLContext( FULL_SCREEN, p.bpp == 16? 16:24, p.bpp == 16? 1:8, 16, m_Context, bShared ); + + if( !newContext ) + return "Failed to create full screen OGL context."; + [m_Context clearDrawable]; + [m_Context release]; + m_Context = newContext; + newDeviceOut = !bShared; + m_CurrentParams.bpp = p.bpp; + [m_BGContext release]; + m_BGContext = CreateOGLContext( FULL_SCREEN, p.bpp == 16? 16:24, p.bpp == 16? 1:8, 16, m_Context, bShared ); + + if( m_BGContext && !bShared ) + { + [m_BGContext release]; + m_BGContext = nil; + } + } + + [m_Context setFullScreen]; + [m_Context update]; + [m_Context makeCurrentContext]; + + if( bChangeVsync ) + { + long swap = p.vsync ? 1 : 0; + + [m_Context setValues:&swap forParameter:NSOpenGLCPSwapInterval]; + m_CurrentParams.vsync = p.vsync; + } + + return RString(); +} + +void LowLevelWindow_MacOSX::ShutDownFullScreen() +{ + if( m_CurrentParams.windowed ) + return; + ASSERT( m_CurrentDisplayMode ); + + // Clear the front and back framebuffers before switching out of FullScreen mode. + // (This is not strictly necessary, but avoids an untidy flash of garbage.) + glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); + glClear( GL_COLOR_BUFFER_BIT ); + [m_Context flushBuffer]; + glClear( GL_COLOR_BUFFER_BIT ); + [m_Context flushBuffer]; + + [NSOpenGLContext clearCurrentContext]; + [m_Context clearDrawable]; + [m_BGContext clearDrawable]; + + CGDisplayErr err = CGDisplaySwitchToMode( kCGDirectMainDisplay, m_CurrentDisplayMode ); + + ASSERT( err == kCGErrorSuccess ); + CGDisplayShowCursor( kCGDirectMainDisplay ); + err = CGDisplayRelease( m_DisplayID ); + ASSERT( err == kCGErrorSuccess ); + SetActualParamsFromMode( m_CurrentDisplayMode ); + // We don't own this so we cannot release it. + m_CurrentDisplayMode = NULL; + m_CurrentParams.windowed = true; +} + +int LowLevelWindow_MacOSX::ChangeDisplayMode( const VideoModeParams& p ) +{ + CFDictionaryRef mode = NULL; + CFDictionaryRef newMode; + CGDisplayErr err; + + if( !m_CurrentDisplayMode ) + { + m_DisplayID = CGMainDisplayID(); + if( (err = CGDisplayCapture(m_DisplayID)) != kCGErrorSuccess ) + return err; + // Only hide the first time we go to full screen. + CGDisplayHideCursor( kCGDirectMainDisplay ); + 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( !m_CurrentDisplayMode ) + m_CurrentDisplayMode = mode; + m_CurrentParams.windowed = false; + SetActualParamsFromMode( newMode ); + + return 0; +} + +void LowLevelWindow_MacOSX::SetActualParamsFromMode( CFDictionaryRef mode ) +{ + SInt32 rate; + bool ret = CFNumberGetValue( (CFNumberRef)CFDictionaryGetValue(mode, CFSTR("RefreshRate")), + kCFNumberSInt32Type, &rate ); + + if( !ret || rate == 0) + rate = 60; + m_CurrentParams.rate = rate; + + if( !m_CurrentParams.windowed ) + { + long swap; + + m_CurrentParams.width = CGDisplayPixelsWide( kCGDirectMainDisplay ); + m_CurrentParams.height = CGDisplayPixelsHigh( kCGDirectMainDisplay ); + CGLGetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval, &swap ); + m_CurrentParams.vsync = swap != 0; + } + else + { + NSSize size = [[((SMWindowDelegate *)m_WindowDelegate)->m_Window contentView] frame].size; + + m_CurrentParams.width = int(size.width); + m_CurrentParams.height = int(size.height); + } + + m_CurrentParams.bpp = CGDisplayBitsPerPixel( kCGDirectMainDisplay ); +} + +static int GetIntValue( CFTypeRef r ) +{ + int ret; + + if( !r || CFGetTypeID(r) != CFNumberGetTypeID() || !CFNumberGetValue(CFNumberRef(r), kCFNumberIntType, &ret) ) + return 0; + return ret; +} + +static bool GetBoolValue( CFTypeRef r ) +{ + return r && CFGetTypeID( r ) == CFBooleanGetTypeID() && CFBooleanGetValue( CFBooleanRef(r) ); +} + +void LowLevelWindow_MacOSX::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 ); + int width = GetIntValue( CFDictionaryGetValue(dict, kCGDisplayWidth) ); + int height = GetIntValue( CFDictionaryGetValue(dict, kCGDisplayHeight) ); + CFTypeRef safe = CFDictionaryGetValue( dict, kCGDisplayModeIsSafeForHardware ); + bool stretched = GetBoolValue( CFDictionaryGetValue(dict, kCGDisplayModeIsStretched) ); + + if( !width || !height ) + continue; + if( safe && !GetBoolValue( safe ) ) + continue; + DisplayResolution res = { width, height, stretched }; + dr.insert( res ); + } + // Do not release modes! We don't own them here. +} + +void LowLevelWindow_MacOSX::SwapBuffers() +{ + CGLFlushDrawable( CGLGetCurrentContext() ); +} + +void LowLevelWindow_MacOSX::Update() +{ + // Keep the system from sleeping or the screen saver from activating. + UpdateSystemActivity( IdleActivity ); + + LockMutex lock( g_ResizeLock ); + if( likely(!g_bResized) ) + return; + g_bResized = false; + if( m_CurrentParams.width == g_iWidth && m_CurrentParams.height == g_iHeight ) + return; + m_CurrentParams.width = g_iWidth; + m_CurrentParams.height = g_iHeight; + lock.Unlock(); // Unlock before calling ResolutionChanged(). + [m_Context update]; + DISPLAY->ResolutionChanged(); +} + +RenderTarget *LowLevelWindow_MacOSX::CreateRenderTarget() +{ + return new RenderTarget_MacOSX( m_Context ); +} + +void LowLevelWindow_MacOSX::BeginConcurrentRendering() +{ + if( m_CurrentParams.windowed ) + [m_BGContext setView:[((SMWindowDelegate *)m_WindowDelegate)->m_Window contentView]]; + else + [m_BGContext setFullScreen]; + [m_BGContext makeCurrentContext]; +} + +/* + * (c) 2005-2006, 2008 Steve Checkoway + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons to + * whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF + * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS + * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT + * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */