ntdll: Make wine_build a hidden symbol.
[wine.git] / dlls / winemac.drv / cocoa_opengl.m
blobce8788b42697245cf1256ff1ff935911f6fa2742
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 #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;
36 @end
39 @implementation WineOpenGLContext
40 @synthesize latentView, needsUpdate, needsReattach, shouldClearToBlack;
42     - (void) dealloc
43     {
44         [[self view] release];
45         [latentView release];
46         [super dealloc];
47     }
49     + (NSView*) dummyView
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:NSWindowStyleMaskBorderless
58                                                             backing:NSBackingStoreBuffered
59                                                               defer:NO];
60             });
61         });
63         return dummyWindow.contentView;
64     }
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
71     {
72         if (!retina_enabled)
73             return;
75         int view_backing[2];
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]))
78         {
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;
84             OnMainThread(^{
85                 [super clearDrawable];
86                 [super setView:save];
87             });
88             shouldClearToBlack = TRUE;
89         }
90     }
92     - (void) wine_updateBackingSize:(const CGSize*)size
93     {
94         GLint enabled;
96         if (!retina_enabled)
97             return;
99         if (size)
100         {
101             if (CGLIsEnabled(self.CGLContextObj, kCGLCESurfaceBackingSize, &enabled) != kCGLNoError)
102                 enabled = 0;
104             if (!enabled || backing_size[0] != size->width || backing_size[1] != size->height)
105             {
106                 backing_size[0] = size->width;
107                 backing_size[1] = size->height;
108                 CGLSetParameter(self.CGLContextObj, kCGLCPSurfaceBackingSize, backing_size);
109             }
111             if (!enabled)
112                 CGLEnable(self.CGLContextObj, kCGLCESurfaceBackingSize);
114             [self resetSurfaceIfBackingSizeChanged];
115         }
116         else
117         {
118             backing_size[0] = 0;
119             backing_size[1] = 0;
121             if (CGLIsEnabled(self.CGLContextObj, kCGLCESurfaceBackingSize, &enabled) == kCGLNoError && enabled)
122                CGLDisable(self.CGLContextObj, kCGLCESurfaceBackingSize);
123         }
124     }
126     - (void) setView:(NSView*)newView
127     {
128         NSView* oldView = [self view];
129         if ([NSThread isMainThread])
130             [super setView:newView];
131         else OnMainThread(^{
132             [super setView:newView];
133         });
134         [newView retain];
135         [oldView release];
136     }
138     - (void) clearDrawable
139     {
140         NSView* oldView = [self view];
141         if ([NSThread isMainThread])
142             [super clearDrawable];
143         else OnMainThread(^{
144             [super clearDrawable];
145         });
146         [oldView release];
148         [self wine_updateBackingSize:NULL];
149     }
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
159     {
160         [self setView:[[self class] dummyView]];
161         [self clearDrawable];
162     }
164     - (void) clearToBlackIfNeeded
165     {
166         if (shouldClearToBlack)
167         {
168             NSOpenGLContext* origContext = [NSOpenGLContext currentContext];
169             const char *gl_version;
170             unsigned int major;
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. */
181             if (major >= 3)
182             {
183                 glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &draw_framebuffer_binding);
184                 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
185             }
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]);
199             if (scissor_test)
200                 glEnable(GL_SCISSOR_TEST);
201             glDrawBuffer(draw_buffer);
202             if (major >= 3)
203                 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, draw_framebuffer_binding);
204             glFlush();
206             if (origContext)
207                 [origContext makeCurrentContext];
208             else
209                 [NSOpenGLContext clearCurrentContext];
211             shouldClearToBlack = FALSE;
212         }
213     }
215     - (void) removeFromViews:(BOOL)removeViews
216     {
217         if ([self view])
218         {
219             macdrv_remove_view_opengl_context((macdrv_view)[self view], (macdrv_opengl_context)self);
220             if (removeViews)
221                 [self clearDrawableLeavingSurfaceOnScreen];
222         }
223         if ([self latentView])
224         {
225             macdrv_remove_view_opengl_context((macdrv_view)[self latentView], (macdrv_opengl_context)self);
226             if (removeViews)
227                 [self setLatentView:nil];
228         }
229         needsUpdate = FALSE;
230         needsReattach = FALSE;
231     }
233 @end
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.
242  */
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];
250     [pool release];
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();
259  */
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];
266     [context release];
268     [pool release];
271 /***********************************************************************
272  *              macdrv_make_context_current
273  */
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;
280     if (context && view)
281     {
282         if (view == [context view] || view == [context latentView])
283         {
284             [context wine_updateBackingSize:&r.size];
285             macdrv_update_opengl_context(c);
286         }
287         else
288         {
289             [context removeFromViews:NO];
290             macdrv_add_view_opengl_context(v, c);
292             if (context.needsUpdate)
293             {
294                 context.needsUpdate = FALSE;
295                 context.needsReattach = FALSE;
296                 if (context.view)
297                     [context setView:[[context class] dummyView]];
298                 [context wine_updateBackingSize:&r.size];
299                 [context setView:view];
300                 [context setLatentView:nil];
301                 [context resetSurfaceIfBackingSizeChanged];
302             }
303             else
304             {
305                 if ([context view])
306                     [context clearDrawableLeavingSurfaceOnScreen];
307                 [context wine_updateBackingSize:&r.size];
308                 [context setLatentView:view];
309             }
310         }
312         [context makeCurrentContext];
314         if ([context view])
315             [context clearToBlackIfNeeded];
316     }
317     else
318     {
319         WineOpenGLContext* currentContext = (WineOpenGLContext*)[WineOpenGLContext currentContext];
321         if ([currentContext isKindOfClass:[WineOpenGLContext class]])
322         {
323             [WineOpenGLContext clearCurrentContext];
324             if (currentContext != context)
325                 [currentContext removeFromViews:YES];
326         }
328         if (context)
329             [context removeFromViews:YES];
330     }
332     [pool release];
335 /***********************************************************************
336  *              macdrv_update_opengl_context
337  */
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)
344     {
345         BOOL reattach = context.needsReattach;
346         context.needsUpdate = FALSE;
347         context.needsReattach = FALSE;
348         if (context.latentView)
349         {
350             [context setView:context.latentView];
351             context.latentView = nil;
353             [context resetSurfaceIfBackingSizeChanged];
354             [context clearToBlackIfNeeded];
355         }
356         else
357         {
358             if (reattach)
359             {
360                 NSView* view = [[context.view retain] autorelease];
361                 [context clearDrawableLeavingSurfaceOnScreen];
362                 context.view = view;
363             }
364             else OnMainThread(^{
365                 [context update];
366             });
367             [context resetSurfaceIfBackingSizeChanged];
368         }
369     }
371     [pool release];
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).
379  */
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];
388     [pool release];