blob: 6c2dbf26a5c8eb1d04983dbf1d7cbc0f0dde3029 [file] [log] [blame]
/*
* MACDRV Cocoa OpenGL code
*
* Copyright 2012, 2013 Ken Thomases for CodeWeavers Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <OpenGL/gl.h>
#import "cocoa_opengl.h"
#include "macdrv_cocoa.h"
#include "cocoa_event.h"
@interface WineOpenGLContext ()
@property (retain, nonatomic) NSView* latentView;
@end
@implementation WineOpenGLContext
@synthesize latentView, needsUpdate;
- (void) dealloc
{
[latentView release];
[super dealloc];
}
/* On at least some versions of Mac OS X, -[NSOpenGLContext clearDrawable] has the
undesirable side effect of ordering the view's GL surface off-screen. This isn't
done when just changing the context's view to a different view (which I would
think would be analogous, since the old view and surface end up without a
context attached). So, we finesse things by first setting the context's view to
a different view (the content view of an off-screen window) and then letting the
original implementation proceed. */
- (void) clearDrawableLeavingSurfaceOnScreen
{
static NSWindow* dummyWindow;
static dispatch_once_t once;
dispatch_once(&once, ^{
OnMainThread(^{
dummyWindow = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100)
styleMask:NSBorderlessWindowMask
backing:NSBackingStoreBuffered
defer:NO];
});
});
[self setView:[dummyWindow contentView]];
[self clearDrawable];
}
@end
/***********************************************************************
* macdrv_create_opengl_context
*
* Returns a Cocoa OpenGL context created from a CoreGL context. The
* caller is responsible for calling macdrv_dispose_opengl_context()
* when done with the context object.
*/
macdrv_opengl_context macdrv_create_opengl_context(void* cglctx)
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
WineOpenGLContext *context;
context = [[WineOpenGLContext alloc] initWithCGLContextObj:cglctx];
[pool release];
return (macdrv_opengl_context)context;
}
/***********************************************************************
* macdrv_dispose_opengl_context
*
* Destroys a Cocoa OpenGL context previously created by
* macdrv_create_opengl_context();
*/
void macdrv_dispose_opengl_context(macdrv_opengl_context c)
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
WineOpenGLContext *context = (WineOpenGLContext*)c;
if ([context view])
macdrv_remove_view_opengl_context((macdrv_view)[context view], c);
if ([context latentView])
macdrv_remove_view_opengl_context((macdrv_view)[context latentView], c);
[context clearDrawableLeavingSurfaceOnScreen];
[context release];
[pool release];
}
/***********************************************************************
* macdrv_make_context_current
*/
void macdrv_make_context_current(macdrv_opengl_context c, macdrv_view v)
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
WineOpenGLContext *context = (WineOpenGLContext*)c;
NSView* view = (NSView*)v;
if (context)
{
if ([context view])
macdrv_remove_view_opengl_context((macdrv_view)[context view], c);
if ([context latentView])
macdrv_remove_view_opengl_context((macdrv_view)[context latentView], c);
context.needsUpdate = FALSE;
if (view)
{
__block BOOL viewIsValidDrawable;
macdrv_add_view_opengl_context(v, c);
OnMainThread(^{
viewIsValidDrawable = [[view window] windowNumber] > 0 &&
!NSIsEmptyRect([view visibleRect]);
});
if (viewIsValidDrawable)
{
[context setView:view];
[context setLatentView:nil];
}
else
[context setLatentView:view];
[context makeCurrentContext];
}
else
{
[WineOpenGLContext clearCurrentContext];
[context clearDrawableLeavingSurfaceOnScreen];
}
}
else
[WineOpenGLContext clearCurrentContext];
[pool release];
}
/***********************************************************************
* macdrv_update_opengl_context
*/
void macdrv_update_opengl_context(macdrv_opengl_context c)
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
WineOpenGLContext *context = (WineOpenGLContext*)c;
if (context.needsUpdate)
{
context.needsUpdate = FALSE;
if (context.latentView)
{
[context setView:context.latentView];
context.latentView = nil;
}
else
[context update];
}
[pool release];
}
/***********************************************************************
* macdrv_flush_opengl_context
*
* Performs an implicit glFlush() and then swaps the back buffer to the
* front (if the context is double-buffered).
*/
void macdrv_flush_opengl_context(macdrv_opengl_context c)
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
WineOpenGLContext *context = (WineOpenGLContext*)c;
macdrv_update_opengl_context(c);
[context flushBuffer];
[pool release];
}