2 * MACDRV Cocoa OpenGL code
4 * Copyright 2012, 2013 Ken Thomases for CodeWeavers Inc.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #define GL_SILENCE_DEPRECATION
22 #include <OpenGL/gl.h>
23 #import "cocoa_opengl.h"
25 #include "macdrv_cocoa.h"
26 #include "cocoa_app.h"
27 #include "cocoa_event.h"
30 @interface WineOpenGLContext ()
31 @property (retain, nonatomic) NSView* latentView;
33 + (NSView*) dummyView;
34 - (void) wine_updateBackingSize:(const CGSize*)size;
39 @implementation WineOpenGLContext
40 @synthesize latentView, needsUpdate, needsReattach, shouldClearToBlack;
44 [[self view] release];
51 static NSWindow* dummyWindow;
52 static dispatch_once_t once;
54 dispatch_once(&once, ^{
56 dummyWindow = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100)
57 styleMask:NSWindowStyleMaskBorderless
58 backing:NSBackingStoreBuffered
63 return dummyWindow.contentView;
66 // Normally, we take care that disconnecting a context from a view doesn't
67 // destroy that view's GL surface (see -clearDrawableLeavingSurfaceOnScreen).
68 // However, if we're using a surface backing size and that size changes, we
69 // need to destroy and recreate the surface or we get weird behavior.
70 - (void) resetSurfaceIfBackingSizeChanged
76 if (macdrv_get_view_backing_size((macdrv_view)self.view, view_backing) &&
77 (view_backing[0] != backing_size[0] || view_backing[1] != backing_size[1]))
79 view_backing[0] = backing_size[0];
80 view_backing[1] = backing_size[1];
81 macdrv_set_view_backing_size((macdrv_view)self.view, view_backing);
83 NSView* save = self.view;
85 [super clearDrawable];
88 shouldClearToBlack = TRUE;
92 - (void) wine_updateBackingSize:(const CGSize*)size
101 if (CGLIsEnabled(self.CGLContextObj, kCGLCESurfaceBackingSize, &enabled) != kCGLNoError)
104 if (!enabled || backing_size[0] != size->width || backing_size[1] != size->height)
106 backing_size[0] = size->width;
107 backing_size[1] = size->height;
108 CGLSetParameter(self.CGLContextObj, kCGLCPSurfaceBackingSize, backing_size);
112 CGLEnable(self.CGLContextObj, kCGLCESurfaceBackingSize);
114 [self resetSurfaceIfBackingSizeChanged];
121 if (CGLIsEnabled(self.CGLContextObj, kCGLCESurfaceBackingSize, &enabled) == kCGLNoError && enabled)
122 CGLDisable(self.CGLContextObj, kCGLCESurfaceBackingSize);
126 - (void) setView:(NSView*)newView
128 NSView* oldView = [self view];
129 if ([NSThread isMainThread])
130 [super setView:newView];
132 [super setView:newView];
138 - (void) clearDrawable
140 NSView* oldView = [self view];
141 if ([NSThread isMainThread])
142 [super clearDrawable];
144 [super clearDrawable];
148 [self wine_updateBackingSize:NULL];
151 /* On at least some versions of Mac OS X, -[NSOpenGLContext clearDrawable] has the
152 undesirable side effect of ordering the view's GL surface off-screen. This isn't
153 done when just changing the context's view to a different view (which I would
154 think would be analogous, since the old view and surface end up without a
155 context attached). So, we finesse things by first setting the context's view to
156 a different view (the content view of an off-screen window) and then letting the
157 original implementation proceed. */
158 - (void) clearDrawableLeavingSurfaceOnScreen
160 [self setView:[[self class] dummyView]];
161 [self clearDrawable];
164 - (void) clearToBlackIfNeeded
166 if (shouldClearToBlack)
168 NSOpenGLContext* origContext = [NSOpenGLContext currentContext];
169 const char *gl_version;
171 GLint draw_framebuffer_binding, draw_buffer;
172 GLboolean scissor_test, color_mask[4];
173 GLfloat clear_color[4];
175 [self makeCurrentContext];
177 gl_version = (const char *)glGetString(GL_VERSION);
178 major = gl_version[0] - '0';
179 /* FIXME: Should check for GL_ARB_framebuffer_object and GL_EXT_framebuffer_object
180 * for older GL versions. */
183 glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &draw_framebuffer_binding);
184 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
186 glGetIntegerv(GL_DRAW_BUFFER, &draw_buffer);
187 scissor_test = glIsEnabled(GL_SCISSOR_TEST);
188 glGetBooleanv(GL_COLOR_WRITEMASK, color_mask);
189 glGetFloatv(GL_COLOR_CLEAR_VALUE, clear_color);
190 glDrawBuffer(GL_FRONT_AND_BACK);
191 glDisable(GL_SCISSOR_TEST);
192 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
193 glClearColor(0, 0, 0, gl_surface_mode == GL_SURFACE_IN_FRONT_TRANSPARENT ? 0 : 1);
195 glClear(GL_COLOR_BUFFER_BIT);
197 glClearColor(clear_color[0], clear_color[1], clear_color[2], clear_color[3]);
198 glColorMask(color_mask[0], color_mask[1], color_mask[2], color_mask[3]);
200 glEnable(GL_SCISSOR_TEST);
201 glDrawBuffer(draw_buffer);
203 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, draw_framebuffer_binding);
207 [origContext makeCurrentContext];
209 [NSOpenGLContext clearCurrentContext];
211 shouldClearToBlack = FALSE;
215 - (void) removeFromViews:(BOOL)removeViews
219 macdrv_remove_view_opengl_context((macdrv_view)[self view], (macdrv_opengl_context)self);
221 [self clearDrawableLeavingSurfaceOnScreen];
223 if ([self latentView])
225 macdrv_remove_view_opengl_context((macdrv_view)[self latentView], (macdrv_opengl_context)self);
227 [self setLatentView:nil];
230 needsReattach = FALSE;
236 /***********************************************************************
237 * macdrv_create_opengl_context
239 * Returns a Cocoa OpenGL context created from a CoreGL context. The
240 * caller is responsible for calling macdrv_dispose_opengl_context()
241 * when done with the context object.
243 macdrv_opengl_context macdrv_create_opengl_context(void* cglctx)
245 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
246 WineOpenGLContext *context;
248 context = [[WineOpenGLContext alloc] initWithCGLContextObj:cglctx];
251 return (macdrv_opengl_context)context;
254 /***********************************************************************
255 * macdrv_dispose_opengl_context
257 * Destroys a Cocoa OpenGL context previously created by
258 * macdrv_create_opengl_context();
260 void macdrv_dispose_opengl_context(macdrv_opengl_context c)
262 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
263 WineOpenGLContext *context = (WineOpenGLContext*)c;
265 [context removeFromViews:YES];
271 /***********************************************************************
272 * macdrv_make_context_current
274 void macdrv_make_context_current(macdrv_opengl_context c, macdrv_view v, CGRect r)
276 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
277 WineOpenGLContext *context = (WineOpenGLContext*)c;
278 NSView* view = (NSView*)v;
282 if (view == [context view] || view == [context latentView])
284 [context wine_updateBackingSize:&r.size];
285 macdrv_update_opengl_context(c);
289 [context removeFromViews:NO];
290 macdrv_add_view_opengl_context(v, c);
292 if (context.needsUpdate)
294 context.needsUpdate = FALSE;
295 context.needsReattach = FALSE;
297 [context setView:[[context class] dummyView]];
298 [context wine_updateBackingSize:&r.size];
299 [context setView:view];
300 [context setLatentView:nil];
301 [context resetSurfaceIfBackingSizeChanged];
306 [context clearDrawableLeavingSurfaceOnScreen];
307 [context wine_updateBackingSize:&r.size];
308 [context setLatentView:view];
312 [context makeCurrentContext];
315 [context clearToBlackIfNeeded];
319 WineOpenGLContext* currentContext = (WineOpenGLContext*)[WineOpenGLContext currentContext];
321 if ([currentContext isKindOfClass:[WineOpenGLContext class]])
323 [WineOpenGLContext clearCurrentContext];
324 if (currentContext != context)
325 [currentContext removeFromViews:YES];
329 [context removeFromViews:YES];
335 /***********************************************************************
336 * macdrv_update_opengl_context
338 void macdrv_update_opengl_context(macdrv_opengl_context c)
340 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
341 WineOpenGLContext *context = (WineOpenGLContext*)c;
343 if (context.needsUpdate)
345 BOOL reattach = context.needsReattach;
346 context.needsUpdate = FALSE;
347 context.needsReattach = FALSE;
348 if (context.latentView)
350 [context setView:context.latentView];
351 context.latentView = nil;
353 [context resetSurfaceIfBackingSizeChanged];
354 [context clearToBlackIfNeeded];
360 NSView* view = [[context.view retain] autorelease];
361 [context clearDrawableLeavingSurfaceOnScreen];
367 [context resetSurfaceIfBackingSizeChanged];
374 /***********************************************************************
375 * macdrv_flush_opengl_context
377 * Performs an implicit glFlush() and then swaps the back buffer to the
378 * front (if the context is double-buffered).
380 void macdrv_flush_opengl_context(macdrv_opengl_context c)
382 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
383 WineOpenGLContext *context = (WineOpenGLContext*)c;
385 macdrv_update_opengl_context(c);
386 [context flushBuffer];