winemac: When clearing the OpenGL context, disassociate it from its view.
[wine/multimedia.git] / dlls / winemac.drv / cocoa_opengl.m
blob0b67ed189900c85e07cb0d93344f3dad37e75975
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         [latentView release];
39         [super dealloc];
40     }
42     /* On at least some versions of Mac OS X, -[NSOpenGLContext clearDrawable] has the
43        undesirable side effect of ordering the view's GL surface off-screen.  This isn't
44        done when just changing the context's view to a different view (which I would
45        think would be analogous, since the old view and surface end up without a
46        context attached).  So, we finesse things by first setting the context's view to
47        a different view (the content view of an off-screen window) and then letting the
48        original implementation proceed. */
49     - (void) clearDrawableLeavingSurfaceOnScreen
50     {
51         static NSWindow* dummyWindow;
52         static dispatch_once_t once;
54         dispatch_once(&once, ^{
55             OnMainThread(^{
56                 dummyWindow = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100)
57                                                           styleMask:NSBorderlessWindowMask
58                                                             backing:NSBackingStoreBuffered
59                                                               defer:NO];
60             });
61         });
63         [self setView:[dummyWindow contentView]];
64         [self clearDrawable];
65     }
67     - (void) clearToBlackIfNeeded
68     {
69         if (shouldClearToBlack)
70         {
71             NSOpenGLContext* origContext = [NSOpenGLContext currentContext];
73             [self makeCurrentContext];
75             glPushAttrib(GL_COLOR_BUFFER_BIT | GL_SCISSOR_BIT);
76             glDrawBuffer(GL_FRONT_AND_BACK);
77             glDisable(GL_SCISSOR_TEST);
78             glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
79             glClearColor(0, 0, 0, 1);
80             glClear(GL_COLOR_BUFFER_BIT);
81             glPopAttrib();
82             glFlush();
84             if (origContext)
85                 [origContext makeCurrentContext];
86             else
87                 [NSOpenGLContext clearCurrentContext];
89             shouldClearToBlack = FALSE;
90         }
91     }
93     - (void) removeFromViews:(BOOL)removeViews
94     {
95         if ([self view])
96         {
97             macdrv_remove_view_opengl_context((macdrv_view)[self view], (macdrv_opengl_context)self);
98             if (removeViews)
99                 [self clearDrawableLeavingSurfaceOnScreen];
100         }
101         if ([self latentView])
102         {
103             macdrv_remove_view_opengl_context((macdrv_view)[self latentView], (macdrv_opengl_context)self);
104             if (removeViews)
105                 [self setLatentView:nil];
106         }
107         needsUpdate = FALSE;
108     }
110 @end
113 /***********************************************************************
114  *              macdrv_create_opengl_context
116  * Returns a Cocoa OpenGL context created from a CoreGL context.  The
117  * caller is responsible for calling macdrv_dispose_opengl_context()
118  * when done with the context object.
119  */
120 macdrv_opengl_context macdrv_create_opengl_context(void* cglctx)
122     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
123     WineOpenGLContext *context;
125     context = [[WineOpenGLContext alloc] initWithCGLContextObj:cglctx];
127     [pool release];
128     return (macdrv_opengl_context)context;
131 /***********************************************************************
132  *              macdrv_dispose_opengl_context
134  * Destroys a Cocoa OpenGL context previously created by
135  * macdrv_create_opengl_context();
136  */
137 void macdrv_dispose_opengl_context(macdrv_opengl_context c)
139     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
140     WineOpenGLContext *context = (WineOpenGLContext*)c;
142     [context removeFromViews:YES];
143     [context release];
145     [pool release];
148 /***********************************************************************
149  *              macdrv_make_context_current
150  */
151 void macdrv_make_context_current(macdrv_opengl_context c, macdrv_view v)
153     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
154     WineOpenGLContext *context = (WineOpenGLContext*)c;
155     NSView* view = (NSView*)v;
157     if (context)
158     {
159         [context removeFromViews:NO];
160         if (view)
161         {
162             macdrv_add_view_opengl_context(v, c);
164             if (context.needsUpdate)
165             {
166                 context.needsUpdate = FALSE;
167                 [context setView:view];
168                 [context setLatentView:nil];
169             }
170             else
171                 [context setLatentView:view];
173             [context makeCurrentContext];
175             if ([context view])
176                 [context clearToBlackIfNeeded];
177         }
178         else
179         {
180             [WineOpenGLContext clearCurrentContext];
181             [context removeFromViews:YES];
182         }
183     }
184     else
185     {
186         WineOpenGLContext* currentContext = (WineOpenGLContext*)[WineOpenGLContext currentContext];
188         if ([currentContext isKindOfClass:[WineOpenGLContext class]])
189         {
190             [WineOpenGLContext clearCurrentContext];
191             [currentContext removeFromViews:YES];
192         }
193     }
195     [pool release];
198 /***********************************************************************
199  *              macdrv_update_opengl_context
200  */
201 void macdrv_update_opengl_context(macdrv_opengl_context c)
203     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
204     WineOpenGLContext *context = (WineOpenGLContext*)c;
206     if (context.needsUpdate)
207     {
208         context.needsUpdate = FALSE;
209         if (context.latentView)
210         {
211             [context setView:context.latentView];
212             context.latentView = nil;
214             [context clearToBlackIfNeeded];
215         }
216         else
217             [context update];
218     }
220     [pool release];
223 /***********************************************************************
224  *              macdrv_flush_opengl_context
226  * Performs an implicit glFlush() and then swaps the back buffer to the
227  * front (if the context is double-buffered).
228  */
229 void macdrv_flush_opengl_context(macdrv_opengl_context c)
231     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
232     WineOpenGLContext *context = (WineOpenGLContext*)c;
234     macdrv_update_opengl_context(c);
235     [context flushBuffer];
237     [pool release];