/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2013 - Raw Material Software Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ class OpenGLContext::NativeContext { public: NativeContext (Component& component, const OpenGLPixelFormat& pixFormat, void* contextToShare, bool /*useMultisampling*/, OpenGLVersion version) : lastSwapTime (0), minSwapTimeMs (0), underrunCounter (0) { (void) version; NSOpenGLPixelFormatAttribute attribs[] = { #if JUCE_OPENGL3 NSOpenGLPFAOpenGLProfile, version >= openGL3_2 ? NSOpenGLProfileVersion3_2Core : NSOpenGLProfileVersionLegacy, #endif NSOpenGLPFADoubleBuffer, NSOpenGLPFAClosestPolicy, NSOpenGLPFANoRecovery, NSOpenGLPFAColorSize, (NSOpenGLPixelFormatAttribute) (pixFormat.redBits + pixFormat.greenBits + pixFormat.blueBits), NSOpenGLPFAAlphaSize, (NSOpenGLPixelFormatAttribute) pixFormat.alphaBits, NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute) pixFormat.depthBufferBits, NSOpenGLPFAStencilSize, (NSOpenGLPixelFormatAttribute) pixFormat.stencilBufferBits, NSOpenGLPFAAccumSize, (NSOpenGLPixelFormatAttribute) (pixFormat.accumulationBufferRedBits + pixFormat.accumulationBufferGreenBits + pixFormat.accumulationBufferBlueBits + pixFormat.accumulationBufferAlphaBits), pixFormat.multisamplingLevel > 0 ? NSOpenGLPFASamples : (NSOpenGLPixelFormatAttribute) 0, (NSOpenGLPixelFormatAttribute) pixFormat.multisamplingLevel, 0 }; NSOpenGLPixelFormat* format = [[NSOpenGLPixelFormat alloc] initWithAttributes: attribs]; static MouseForwardingNSOpenGLViewClass cls; view = [cls.createInstance() initWithFrame: NSMakeRect (0, 0, 100.0f, 100.0f) pixelFormat: format]; #if defined (MAC_OS_X_VERSION_10_7) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7) if ([view respondsToSelector: @selector (setWantsBestResolutionOpenGLSurface:)]) [view setWantsBestResolutionOpenGLSurface: YES]; #endif [[NSNotificationCenter defaultCenter] addObserver: view selector: @selector (_surfaceNeedsUpdate:) name: NSViewGlobalFrameDidChangeNotification object: view]; renderContext = [[[NSOpenGLContext alloc] initWithFormat: format shareContext: (NSOpenGLContext*) contextToShare] autorelease]; [view setOpenGLContext: renderContext]; [format release]; viewAttachment = NSViewComponent::attachViewToComponent (component, view); } ~NativeContext() { [[NSNotificationCenter defaultCenter] removeObserver: view]; [renderContext clearDrawable]; [renderContext setView: nil]; [view setOpenGLContext: nil]; renderContext = nil; } void initialiseOnRenderThread (OpenGLContext&) {} void shutdownOnRenderThread() { deactivateCurrentContext(); } bool createdOk() const noexcept { return getRawContext() != nullptr; } void* getRawContext() const noexcept { return static_cast (renderContext); } GLuint getFrameBufferID() const noexcept { return 0; } bool makeActive() const noexcept { jassert (renderContext != nil); if ([renderContext view] != view) [renderContext setView: view]; if (NSOpenGLContext* context = [view openGLContext]) { [context makeCurrentContext]; return true; } return false; } bool isActive() const noexcept { return [NSOpenGLContext currentContext] == renderContext; } static void deactivateCurrentContext() { [NSOpenGLContext clearCurrentContext]; } struct Locker { Locker (NativeContext& nc) : cglContext ((CGLContextObj) [nc.renderContext CGLContextObj]) { CGLLockContext (cglContext); } ~Locker() { CGLUnlockContext (cglContext); } private: CGLContextObj cglContext; }; void swapBuffers() { [renderContext flushBuffer]; sleepIfRenderingTooFast(); } void updateWindowPosition (const Rectangle&) {} bool setSwapInterval (int numFramesPerSwap) { minSwapTimeMs = (numFramesPerSwap * 1000) / 60; [renderContext setValues: (const GLint*) &numFramesPerSwap forParameter: NSOpenGLCPSwapInterval]; return true; } int getSwapInterval() const { GLint numFrames = 0; [renderContext getValues: &numFrames forParameter: NSOpenGLCPSwapInterval]; return numFrames; } void sleepIfRenderingTooFast() { // When our window is entirely occluded by other windows, the system // fails to correctly implement the swap interval time, so the render // loop spins at full speed, burning CPU. This hack detects when things // are going too fast and slows things down if necessary. if (minSwapTimeMs > 0) { const double now = Time::getMillisecondCounterHiRes(); const int elapsed = (int) (now - lastSwapTime); lastSwapTime = now; if (isPositiveAndBelow (elapsed, minSwapTimeMs - 3)) { if (underrunCounter > 3) Thread::sleep (minSwapTimeMs - elapsed); else ++underrunCounter; } else { underrunCounter = 0; } } } NSOpenGLContext* renderContext; NSOpenGLView* view; ReferenceCountedObjectPtr viewAttachment; double lastSwapTime; int minSwapTimeMs, underrunCounter; //============================================================================== struct MouseForwardingNSOpenGLViewClass : public ObjCClass { MouseForwardingNSOpenGLViewClass() : ObjCClass ("JUCEGLView_") { addMethod (@selector (rightMouseDown:), rightMouseDown, "v@:@"); addMethod (@selector (rightMouseUp:), rightMouseUp, "v@:@"); addMethod (@selector (acceptsFirstMouse:), acceptsFirstMouse, "v@:@"); registerClass(); } private: static void rightMouseDown (id self, SEL, NSEvent* ev) { [[(NSOpenGLView*) self superview] rightMouseDown: ev]; } static void rightMouseUp (id self, SEL, NSEvent* ev) { [[(NSOpenGLView*) self superview] rightMouseUp: ev]; } static BOOL acceptsFirstMouse (id, SEL, NSEvent*) { return YES; } }; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeContext) }; //============================================================================== bool OpenGLHelpers::isContextActive() { return CGLGetCurrentContext() != 0; }