winemac: Make the implementation of clearToBlackIfNeeded compatible with core contexts.
[wine/multimedia.git] / dlls / winemac.drv / cocoa_opengl.m
blob2f46b87ba33c374225b24234e24de53cf955aaac
1 /*
2  * MACDRV Cocoa OpenGL code
3  *
4  * Copyright 2012, 2013 Ken Thomases for CodeWeavers Inc.
5  *
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.
10  *
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.
15  *
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
19  */
21 #include <OpenGL/gl.h>
22 #import "cocoa_opengl.h"
24 #include "macdrv_cocoa.h"
25 #include "cocoa_event.h"
28 @interface WineOpenGLContext ()
29 @property (retain, nonatomic) NSView* latentView;
30 @end
33 @implementation WineOpenGLContext
34 @synthesize latentView, needsUpdate, shouldClearToBlack;
36     - (void) dealloc
37     {
38         [[self view] release];
39         [latentView release];
40         [super dealloc];
41     }
43     - (void) setView:(NSView*)newView
44     {
45         NSView* oldView = [self view];
46         [super setView:newView];
47         [newView retain];
48         [oldView release];
49     }
51     - (void) clearDrawable
52     {
53         NSView* oldView = [self view];
54         [super clearDrawable];
55         [oldView release];
56     }
58     /* On at least some versions of Mac OS X, -[NSOpenGLContext clearDrawable] has the
59        undesirable side effect of ordering the view's GL surface off-screen.  This isn't
60        done when just changing the context's view to a different view (which I would
61        think would be analogous, since the old view and surface end up without a
62        context attached).  So, we finesse things by first setting the context's view to
63        a different view (the content view of an off-screen window) and then letting the
64        original implementation proceed. */
65     - (void) clearDrawableLeavingSurfaceOnScreen
66     {
67         static NSWindow* dummyWindow;
68         static dispatch_once_t once;
70         dispatch_once(&once, ^{
71             OnMainThread(^{
72                 dummyWindow = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100)
73                                                           styleMask:NSBorderlessWindowMask
74                                                             backing:NSBackingStoreBuffered
75                                                               defer:NO];
76             });
77         });
79         [self setView:[dummyWindow contentView]];
80         [self clearDrawable];
81     }
83     - (void) clearToBlackIfNeeded
84     {
85         if (shouldClearToBlack)
86         {
87             NSOpenGLContext* origContext = [NSOpenGLContext currentContext];
88             const char *gl_version;
89             unsigned int major;
90             GLint draw_framebuffer_binding, draw_buffer;
91             GLboolean scissor_test, color_mask[4];
92             GLfloat clear_color[4];
94             [self makeCurrentContext];
96             gl_version = (const char *)glGetString(GL_VERSION);
97             major = gl_version[0] - '0';
98             /* FIXME: Should check for GL_ARB_framebuffer_object and GL_EXT_framebuffer_object
99              * for older GL versions. */
100             if (major >= 3)
101             {
102                 glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &draw_framebuffer_binding);
103                 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
104             }
105             glGetIntegerv(GL_DRAW_BUFFER, &draw_buffer);
106             scissor_test = glIsEnabled(GL_SCISSOR_TEST);
107             glGetBooleanv(GL_COLOR_WRITEMASK, color_mask);
108             glGetFloatv(GL_COLOR_CLEAR_VALUE, clear_color);
109             glDrawBuffer(GL_FRONT_AND_BACK);
110             glDisable(GL_SCISSOR_TEST);
111             glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
112             glClearColor(0, 0, 0, 1);
114             glClear(GL_COLOR_BUFFER_BIT);
116             glClearColor(clear_color[0], clear_color[1], clear_color[2], clear_color[3]);
117             glColorMask(color_mask[0], color_mask[1], color_mask[2], color_mask[3]);
118             if (scissor_test)
119                 glEnable(GL_SCISSOR_TEST);
120             glDrawBuffer(draw_buffer);
121             if (major >= 3)
122                 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, draw_framebuffer_binding);
123             glFlush();
125             if (origContext)
126                 [origContext makeCurrentContext];
127             else
128                 [NSOpenGLContext clearCurrentContext];
130             shouldClearToBlack = FALSE;
131         }
132     }
134     - (void) removeFromViews:(BOOL)removeViews
135     {
136         if ([self view])
137         {
138             macdrv_remove_view_opengl_context((macdrv_view)[self view], (macdrv_opengl_context)self);
139             if (removeViews)
140                 [self clearDrawableLeavingSurfaceOnScreen];
141         }
142         if ([self latentView])
143         {
144             macdrv_remove_view_opengl_context((macdrv_view)[self latentView], (macdrv_opengl_context)self);
145             if (removeViews)
146                 [self setLatentView:nil];
147         }
148         needsUpdate = FALSE;
149     }
151 @end
154 /***********************************************************************
155  *              macdrv_create_opengl_context
157  * Returns a Cocoa OpenGL context created from a CoreGL context.  The
158  * caller is responsible for calling macdrv_dispose_opengl_context()
159  * when done with the context object.
160  */
161 macdrv_opengl_context macdrv_create_opengl_context(void* cglctx)
163     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
164     WineOpenGLContext *context;
166     context = [[WineOpenGLContext alloc] initWithCGLContextObj:cglctx];
168     [pool release];
169     return (macdrv_opengl_context)context;
172 /***********************************************************************
173  *              macdrv_dispose_opengl_context
175  * Destroys a Cocoa OpenGL context previously created by
176  * macdrv_create_opengl_context();
177  */
178 void macdrv_dispose_opengl_context(macdrv_opengl_context c)
180     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
181     WineOpenGLContext *context = (WineOpenGLContext*)c;
183     [context removeFromViews:YES];
184     [context release];
186     [pool release];
189 /***********************************************************************
190  *              macdrv_make_context_current
191  */
192 void macdrv_make_context_current(macdrv_opengl_context c, macdrv_view v)
194     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
195     WineOpenGLContext *context = (WineOpenGLContext*)c;
196     NSView* view = (NSView*)v;
198     if (context && view)
199     {
200         if (view == [context view] || view == [context latentView])
201             macdrv_update_opengl_context(c);
202         else
203         {
204             [context removeFromViews:NO];
205             macdrv_add_view_opengl_context(v, c);
207             if (context.needsUpdate)
208             {
209                 context.needsUpdate = FALSE;
210                 [context setView:view];
211                 [context setLatentView:nil];
212             }
213             else
214             {
215                 if ([context view])
216                     [context clearDrawableLeavingSurfaceOnScreen];
217                 [context setLatentView:view];
218             }
219         }
221         [context makeCurrentContext];
223         if ([context view])
224             [context clearToBlackIfNeeded];
225     }
226     else
227     {
228         WineOpenGLContext* currentContext = (WineOpenGLContext*)[WineOpenGLContext currentContext];
230         if ([currentContext isKindOfClass:[WineOpenGLContext class]])
231         {
232             [WineOpenGLContext clearCurrentContext];
233             if (currentContext != context)
234                 [currentContext removeFromViews:YES];
235         }
237         if (context)
238             [context removeFromViews:YES];
239     }
241     [pool release];
244 /***********************************************************************
245  *              macdrv_update_opengl_context
246  */
247 void macdrv_update_opengl_context(macdrv_opengl_context c)
249     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
250     WineOpenGLContext *context = (WineOpenGLContext*)c;
252     if (context.needsUpdate)
253     {
254         context.needsUpdate = FALSE;
255         if (context.latentView)
256         {
257             [context setView:context.latentView];
258             context.latentView = nil;
260             [context clearToBlackIfNeeded];
261         }
262         else
263             [context update];
264     }
266     [pool release];
269 /***********************************************************************
270  *              macdrv_flush_opengl_context
272  * Performs an implicit glFlush() and then swaps the back buffer to the
273  * front (if the context is double-buffered).
274  */
275 void macdrv_flush_opengl_context(macdrv_opengl_context c)
277     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
278     WineOpenGLContext *context = (WineOpenGLContext*)c;
280     macdrv_update_opengl_context(c);
281     [context flushBuffer];
283     [pool release];