winex11: Avoid deadlock when setting cursor.
[wine.git] / dlls / winemac.drv / cocoa_opengl.m
blob5a097a12d26b8ed8fc12fe697523d25c005a4153
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;
31     + (NSView*) dummyView;
32     - (void) wine_updateBackingSize:(const CGSize*)size;
34 @end
37 @implementation WineOpenGLContext
38 @synthesize latentView, needsUpdate, needsReattach, shouldClearToBlack;
40     - (void) dealloc
41     {
42         [[self view] release];
43         [latentView release];
44         [super dealloc];
45     }
47     + (NSView*) dummyView
48     {
49         static NSWindow* dummyWindow;
50         static dispatch_once_t once;
52         dispatch_once(&once, ^{
53             OnMainThread(^{
54                 dummyWindow = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100)
55                                                           styleMask:NSBorderlessWindowMask
56                                                             backing:NSBackingStoreBuffered
57                                                               defer:NO];
58             });
59         });
61         return dummyWindow.contentView;
62     }
64     // Normally, we take care that disconnecting a context from a view doesn't
65     // destroy that view's GL surface (see -clearDrawableLeavingSurfaceOnScreen).
66     // However, if we're using a surface backing size and that size changes, we
67     // need to destroy and recreate the surface or we get weird behavior.
68     - (void) resetSurfaceIfBackingSizeChanged
69     {
70         if (!retina_enabled)
71             return;
73         int view_backing[2];
74         if (macdrv_get_view_backing_size((macdrv_view)self.view, view_backing) &&
75             (view_backing[0] != backing_size[0] || view_backing[1] != backing_size[1]))
76         {
77             view_backing[0] = backing_size[0];
78             view_backing[1] = backing_size[1];
79             macdrv_set_view_backing_size((macdrv_view)self.view, view_backing);
81             NSView* save = self.view;
82             [super clearDrawable];
83             [super setView:save];
84             shouldClearToBlack = TRUE;
85         }
86     }
88     - (void) wine_updateBackingSize:(const CGSize*)size
89     {
90         GLint enabled;
92         if (!retina_enabled)
93             return;
95         if (size)
96         {
97             if (CGLIsEnabled(self.CGLContextObj, kCGLCESurfaceBackingSize, &enabled) != kCGLNoError)
98                 enabled = 0;
100             if (!enabled || backing_size[0] != size->width || backing_size[1] != size->height)
101             {
102                 backing_size[0] = size->width;
103                 backing_size[1] = size->height;
104                 CGLSetParameter(self.CGLContextObj, kCGLCPSurfaceBackingSize, backing_size);
105             }
107             if (!enabled)
108                 CGLEnable(self.CGLContextObj, kCGLCESurfaceBackingSize);
110             [self resetSurfaceIfBackingSizeChanged];
111         }
112         else
113         {
114             backing_size[0] = 0;
115             backing_size[1] = 0;
117             if (CGLIsEnabled(self.CGLContextObj, kCGLCESurfaceBackingSize, &enabled) == kCGLNoError && enabled)
118                CGLDisable(self.CGLContextObj, kCGLCESurfaceBackingSize);
119         }
120     }
122     - (void) setView:(NSView*)newView
123     {
124         NSView* oldView = [self view];
125         [super setView:newView];
126         [newView retain];
127         [oldView release];
128     }
130     - (void) clearDrawable
131     {
132         NSView* oldView = [self view];
133         [super clearDrawable];
134         [oldView release];
136         [self wine_updateBackingSize:NULL];
137     }
139     /* On at least some versions of Mac OS X, -[NSOpenGLContext clearDrawable] has the
140        undesirable side effect of ordering the view's GL surface off-screen.  This isn't
141        done when just changing the context's view to a different view (which I would
142        think would be analogous, since the old view and surface end up without a
143        context attached).  So, we finesse things by first setting the context's view to
144        a different view (the content view of an off-screen window) and then letting the
145        original implementation proceed. */
146     - (void) clearDrawableLeavingSurfaceOnScreen
147     {
148         [self setView:[[self class] dummyView]];
149         [self clearDrawable];
150     }
152     - (void) clearToBlackIfNeeded
153     {
154         if (shouldClearToBlack)
155         {
156             NSOpenGLContext* origContext = [NSOpenGLContext currentContext];
157             const char *gl_version;
158             unsigned int major;
159             GLint draw_framebuffer_binding, draw_buffer;
160             GLboolean scissor_test, color_mask[4];
161             GLfloat clear_color[4];
163             [self makeCurrentContext];
165             gl_version = (const char *)glGetString(GL_VERSION);
166             major = gl_version[0] - '0';
167             /* FIXME: Should check for GL_ARB_framebuffer_object and GL_EXT_framebuffer_object
168              * for older GL versions. */
169             if (major >= 3)
170             {
171                 glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &draw_framebuffer_binding);
172                 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
173             }
174             glGetIntegerv(GL_DRAW_BUFFER, &draw_buffer);
175             scissor_test = glIsEnabled(GL_SCISSOR_TEST);
176             glGetBooleanv(GL_COLOR_WRITEMASK, color_mask);
177             glGetFloatv(GL_COLOR_CLEAR_VALUE, clear_color);
178             glDrawBuffer(GL_FRONT_AND_BACK);
179             glDisable(GL_SCISSOR_TEST);
180             glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
181             glClearColor(0, 0, 0, gl_surface_mode == GL_SURFACE_IN_FRONT_TRANSPARENT ? 0 : 1);
183             glClear(GL_COLOR_BUFFER_BIT);
185             glClearColor(clear_color[0], clear_color[1], clear_color[2], clear_color[3]);
186             glColorMask(color_mask[0], color_mask[1], color_mask[2], color_mask[3]);
187             if (scissor_test)
188                 glEnable(GL_SCISSOR_TEST);
189             glDrawBuffer(draw_buffer);
190             if (major >= 3)
191                 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, draw_framebuffer_binding);
192             glFlush();
194             if (origContext)
195                 [origContext makeCurrentContext];
196             else
197                 [NSOpenGLContext clearCurrentContext];
199             shouldClearToBlack = FALSE;
200         }
201     }
203     - (void) removeFromViews:(BOOL)removeViews
204     {
205         if ([self view])
206         {
207             macdrv_remove_view_opengl_context((macdrv_view)[self view], (macdrv_opengl_context)self);
208             if (removeViews)
209                 [self clearDrawableLeavingSurfaceOnScreen];
210         }
211         if ([self latentView])
212         {
213             macdrv_remove_view_opengl_context((macdrv_view)[self latentView], (macdrv_opengl_context)self);
214             if (removeViews)
215                 [self setLatentView:nil];
216         }
217         needsUpdate = FALSE;
218         needsReattach = FALSE;
219     }
221 @end
224 /***********************************************************************
225  *              macdrv_create_opengl_context
227  * Returns a Cocoa OpenGL context created from a CoreGL context.  The
228  * caller is responsible for calling macdrv_dispose_opengl_context()
229  * when done with the context object.
230  */
231 macdrv_opengl_context macdrv_create_opengl_context(void* cglctx)
233     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
234     WineOpenGLContext *context;
236     context = [[WineOpenGLContext alloc] initWithCGLContextObj:cglctx];
238     [pool release];
239     return (macdrv_opengl_context)context;
242 /***********************************************************************
243  *              macdrv_dispose_opengl_context
245  * Destroys a Cocoa OpenGL context previously created by
246  * macdrv_create_opengl_context();
247  */
248 void macdrv_dispose_opengl_context(macdrv_opengl_context c)
250     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
251     WineOpenGLContext *context = (WineOpenGLContext*)c;
253     [context removeFromViews:YES];
254     [context release];
256     [pool release];
259 /***********************************************************************
260  *              macdrv_make_context_current
261  */
262 void macdrv_make_context_current(macdrv_opengl_context c, macdrv_view v, CGRect r)
264     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
265     WineOpenGLContext *context = (WineOpenGLContext*)c;
266     NSView* view = (NSView*)v;
268     if (context && view)
269     {
270         if (view == [context view] || view == [context latentView])
271         {
272             [context wine_updateBackingSize:&r.size];
273             macdrv_update_opengl_context(c);
274         }
275         else
276         {
277             [context removeFromViews:NO];
278             macdrv_add_view_opengl_context(v, c);
280             if (context.needsUpdate)
281             {
282                 context.needsUpdate = FALSE;
283                 context.needsReattach = FALSE;
284                 if (context.view)
285                     [context setView:[[context class] dummyView]];
286                 [context wine_updateBackingSize:&r.size];
287                 [context setView:view];
288                 [context setLatentView:nil];
289                 [context resetSurfaceIfBackingSizeChanged];
290             }
291             else
292             {
293                 if ([context view])
294                     [context clearDrawableLeavingSurfaceOnScreen];
295                 [context wine_updateBackingSize:&r.size];
296                 [context setLatentView:view];
297             }
298         }
300         [context makeCurrentContext];
302         if ([context view])
303             [context clearToBlackIfNeeded];
304     }
305     else
306     {
307         WineOpenGLContext* currentContext = (WineOpenGLContext*)[WineOpenGLContext currentContext];
309         if ([currentContext isKindOfClass:[WineOpenGLContext class]])
310         {
311             [WineOpenGLContext clearCurrentContext];
312             if (currentContext != context)
313                 [currentContext removeFromViews:YES];
314         }
316         if (context)
317             [context removeFromViews:YES];
318     }
320     [pool release];
323 /***********************************************************************
324  *              macdrv_update_opengl_context
325  */
326 void macdrv_update_opengl_context(macdrv_opengl_context c)
328     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
329     WineOpenGLContext *context = (WineOpenGLContext*)c;
331     if (context.needsUpdate)
332     {
333         BOOL reattach = context.needsReattach;
334         context.needsUpdate = FALSE;
335         context.needsReattach = FALSE;
336         if (context.latentView)
337         {
338             [context setView:context.latentView];
339             context.latentView = nil;
341             [context resetSurfaceIfBackingSizeChanged];
342             [context clearToBlackIfNeeded];
343         }
344         else
345         {
346             if (reattach)
347             {
348                 NSView* view = [[context.view retain] autorelease];
349                 [context clearDrawableLeavingSurfaceOnScreen];
350                 context.view = view;
351             }
352             else
353                 [context update];
354             [context resetSurfaceIfBackingSizeChanged];
355         }
356     }
358     [pool release];
361 /***********************************************************************
362  *              macdrv_flush_opengl_context
364  * Performs an implicit glFlush() and then swaps the back buffer to the
365  * front (if the context is double-buffered).
366  */
367 void macdrv_flush_opengl_context(macdrv_opengl_context c)
369     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
370     WineOpenGLContext *context = (WineOpenGLContext*)c;
372     macdrv_update_opengl_context(c);
373     [context flushBuffer];
375     [pool release];