1/*
2 * GStreamer
3 * Copyright (C) 2008 Julien Isorce <julien.isorce@gmail.com>
4 * Copyright (C) 2014 Sebastian Dröge <sebastian@centricular.com>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it un der the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 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 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif
25
26#include <Cocoa/Cocoa.h>
27#include <QuartzCore/QuartzCore.h>
28
29#include "gstgl_cocoa_private.h"
30
31#if MAC_OS_X_VERSION_MAX_ALLOWED < 101200
32#define NSWindowStyleMaskTitled              NSTitledWindowMask
33#define NSWindowStyleMaskClosable            NSClosableWindowMask
34#define NSWindowStyleMaskResizable           NSResizableWindowMask
35#define NSWindowStyleMaskMiniaturizable      NSMiniaturizableWindowMask
36#endif
37
38/* =============================================================*/
39/*                                                              */
40/*               GstGLNSWindow declaration                      */
41/*                                                              */
42/* =============================================================*/
43
44@interface GstGLNSWindow: NSWindow {
45  BOOL m_isClosed;
46  GstGLWindowCocoa *window_cocoa;
47}
48- (id)initWithContentRect:(NSRect)contentRect
49    styleMask: (unsigned int) styleMask
50    backing: (NSBackingStoreType) bufferingType
51    defer: (BOOL) flag screen: (NSScreen *) aScreen
52    gstWin: (GstGLWindowCocoa *) window;
53- (void) setClosed;
54- (BOOL) isClosed;
55- (BOOL) canBecomeMainWindow;
56- (BOOL) canBecomeKeyWindow;
57@end
58
59/* =============================================================*/
60/*                                                              */
61/*                      GstGLWindow                             */
62/*                                                              */
63/* =============================================================*/
64
65#define GST_CAT_DEFAULT gst_gl_window_cocoa_debug
66GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
67
68static void gst_gl_window_cocoa_finalize (GObject * object);
69
70static gboolean gst_gl_window_cocoa_open (GstGLWindow *window, GError **err);
71static void gst_gl_window_cocoa_close (GstGLWindow *window);
72static guintptr gst_gl_window_cocoa_get_window_handle (GstGLWindow * window);
73static void gst_gl_window_cocoa_set_window_handle (GstGLWindow * window,
74    guintptr handle);
75static void gst_gl_window_cocoa_draw (GstGLWindow * window);
76static void gst_gl_window_cocoa_set_preferred_size (GstGLWindow * window,
77    gint width, gint height);
78static void gst_gl_window_cocoa_show (GstGLWindow * window);
79static void gst_gl_window_cocoa_queue_resize (GstGLWindow * window);
80static void gst_gl_window_cocoa_send_message_async (GstGLWindow * window,
81    GstGLWindowCB callback, gpointer data, GDestroyNotify destroy);
82static gboolean gst_gl_window_cocoa_set_render_rectangle (GstGLWindow * window,
83    gint x, gint y, gint width, gint height);
84static gboolean gst_gl_window_cocoa_controls_viewport (GstGLWindow * window);
85
86
87struct _GstGLWindowCocoaPrivate
88{
89  gpointer internal_win_id;
90  gpointer external_view;
91  gboolean visible;
92  gint preferred_width;
93  gint preferred_height;
94
95  /* atomic set when the internal NSView has been created */
96  int view_ready;
97
98  gpointer gl_queue;
99};
100
101#define DEBUG_INIT \
102  GST_DEBUG_CATEGORY_GET (GST_CAT_DEFAULT, "glwindow");
103
104#define gst_gl_window_cocoa_parent_class parent_class
105G_DEFINE_TYPE_WITH_CODE (GstGLWindowCocoa, gst_gl_window_cocoa, GST_TYPE_GL_WINDOW,
106    G_ADD_PRIVATE (GstGLWindowCocoa)
107    DEBUG_INIT);
108
109static void
110gst_gl_window_cocoa_class_init (GstGLWindowCocoaClass * klass)
111{
112  GstGLWindowClass *window_class = (GstGLWindowClass *) klass;
113  GObjectClass *gobject_class = (GObjectClass *) klass;
114
115  window_class->open = GST_DEBUG_FUNCPTR (gst_gl_window_cocoa_open);
116  window_class->close = GST_DEBUG_FUNCPTR (gst_gl_window_cocoa_close);
117  window_class->get_window_handle =
118      GST_DEBUG_FUNCPTR (gst_gl_window_cocoa_get_window_handle);
119  window_class->set_window_handle =
120      GST_DEBUG_FUNCPTR (gst_gl_window_cocoa_set_window_handle);
121  window_class->draw = GST_DEBUG_FUNCPTR (gst_gl_window_cocoa_draw);
122  window_class->set_preferred_size =
123      GST_DEBUG_FUNCPTR (gst_gl_window_cocoa_set_preferred_size);
124  window_class->show = GST_DEBUG_FUNCPTR (gst_gl_window_cocoa_show);
125  window_class->queue_resize = GST_DEBUG_FUNCPTR (gst_gl_window_cocoa_queue_resize);
126  window_class->send_message_async =
127      GST_DEBUG_FUNCPTR (gst_gl_window_cocoa_send_message_async);
128  window_class->set_render_rectangle =
129      GST_DEBUG_FUNCPTR (gst_gl_window_cocoa_set_render_rectangle);
130  window_class->controls_viewport =
131      GST_DEBUG_FUNCPTR (gst_gl_window_cocoa_controls_viewport);
132
133  gobject_class->finalize = gst_gl_window_cocoa_finalize;
134}
135
136static void
137gst_gl_window_cocoa_init (GstGLWindowCocoa * window)
138{
139  window->priv = gst_gl_window_cocoa_get_instance_private (window);
140
141  window->priv->preferred_width = 320;
142  window->priv->preferred_height = 240;
143#if OS_OBJECT_USE_OBJC
144  window->priv->gl_queue = (__bridge_retained gpointer)
145      (dispatch_queue_create ("org.freedesktop.gstreamer.glwindow", NULL));
146#else
147  window->priv->gl_queue = (gpointer)
148      (dispatch_queue_create ("org.freedesktop.gstreamer.glwindow", NULL));
149#endif
150}
151
152static void
153gst_gl_window_cocoa_finalize (GObject * object)
154{
155  GstGLWindowCocoa *window = GST_GL_WINDOW_COCOA (object);
156
157#if OS_OBJECT_USE_OBJC
158  /* Let ARC clean up our queue */
159  dispatch_queue_t queue = (__bridge_transfer dispatch_queue_t) window->priv->gl_queue;
160#else
161  dispatch_release (window->priv->gl_queue);
162#endif
163
164  window->priv->gl_queue = NULL;
165  G_OBJECT_CLASS (parent_class)->finalize (object);
166}
167
168GstGLWindowCocoa *
169gst_gl_window_cocoa_new (GstGLDisplay * display)
170{
171  GstGLWindowCocoa *window;
172
173  if ((gst_gl_display_get_handle_type (display) & GST_GL_DISPLAY_TYPE_COCOA) == 0)
174    /* we require an cocoa display to create CGL windows */
175    return NULL;
176
177  window = g_object_new (GST_TYPE_GL_WINDOW_COCOA, NULL);
178  gst_object_ref_sink (window);
179
180  return window;
181}
182
183/* Must be called from the main thread */
184gboolean
185gst_gl_window_cocoa_create_window (GstGLWindowCocoa *window_cocoa)
186{
187  GstGLWindowCocoaPrivate *priv = window_cocoa->priv;
188  GstGLWindow *window = GST_GL_WINDOW (window_cocoa);
189  GstGLNSWindow *internal_win_id;
190  NSRect mainRect = [[NSScreen mainScreen] visibleFrame];
191  gint h = priv->preferred_height;
192  gint y = mainRect.size.height > h ? (mainRect.size.height - h) * 0.5 : 0;
193  NSRect rect = NSMakeRect (0, y, priv->preferred_width, priv->preferred_height);
194  NSRect windowRect = NSMakeRect (0, y, priv->preferred_width, priv->preferred_height);
195  GstGLContext *context = gst_gl_window_get_context (window);
196  GstGLContextCocoa *context_cocoa;
197  GstGLCAOpenGLLayer *layer;
198  GstGLNSView *glView;
199
200  if (!context)
201    return FALSE;
202
203  context_cocoa = GST_GL_CONTEXT_COCOA (context);
204  layer = [[GstGLCAOpenGLLayer alloc] initWithGstGLContext:context];
205  layer.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
206  layer.needsDisplayOnBoundsChange = YES;
207  glView = [[GstGLNSView alloc] initWithFrameLayer:window_cocoa rect:windowRect layer:layer];
208
209  gst_object_unref (context);
210
211  internal_win_id = [[GstGLNSWindow alloc] initWithContentRect:rect styleMask:
212      (NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
213      NSWindowStyleMaskResizable | NSWindowStyleMaskMiniaturizable)
214      backing: NSBackingStoreBuffered defer: NO screen: nil gstWin: window_cocoa];
215
216  priv->internal_win_id = (__bridge_retained gpointer)internal_win_id;
217
218  GST_DEBUG ("NSWindow id: %"G_GUINTPTR_FORMAT, (guintptr) priv->internal_win_id);
219
220  [internal_win_id setContentView:glView];
221
222  g_atomic_int_set (&window_cocoa->priv->view_ready, 1);
223
224  /* Set the window handle for real now that the NSWindow has been created. */
225  if (priv->external_view)
226    gst_gl_window_cocoa_set_window_handle (window,
227        (guintptr) priv->external_view);
228
229  return TRUE;
230}
231
232static gboolean
233gst_gl_window_cocoa_open (GstGLWindow *window, GError **err)
234{
235  GstGLWindowCocoa *window_cocoa;
236
237  window_cocoa = GST_GL_WINDOW_COCOA (window);
238
239  return TRUE;
240}
241
242static void
243_close_window (gpointer * data)
244{
245  GstGLWindowCocoa *window_cocoa = GST_GL_WINDOW_COCOA (data);
246  GstGLNSWindow *internal_win_id =
247      (__bridge GstGLNSWindow *) window_cocoa->priv->internal_win_id;
248
249  [[internal_win_id contentView] removeFromSuperview];
250  CFBridgingRelease (window_cocoa->priv->internal_win_id);
251  window_cocoa->priv->internal_win_id = NULL;
252}
253
254static void
255gst_gl_window_cocoa_close (GstGLWindow * window)
256{
257  _invoke_on_main ((GstGLWindowCB) _close_window, gst_object_ref (window),
258      (GDestroyNotify) gst_object_unref);
259}
260
261static guintptr
262gst_gl_window_cocoa_get_window_handle (GstGLWindow *window)
263{
264  return (guintptr) GST_GL_WINDOW_COCOA (window)->priv->internal_win_id;
265}
266
267static void
268gst_gl_window_cocoa_set_window_handle (GstGLWindow * window, guintptr handle)
269{
270  GstGLWindowCocoa *window_cocoa;
271  GstGLWindowCocoaPrivate *priv;
272
273  window_cocoa = GST_GL_WINDOW_COCOA (window);
274  priv = window_cocoa->priv;
275
276  if (priv->internal_win_id) {
277    if (handle) {
278      priv->external_view = (gpointer)handle;
279      priv->visible = TRUE;
280    } else {
281      /* bring back our internal window */
282      priv->external_view = 0;
283      priv->visible = FALSE;
284    }
285
286
287    dispatch_async (dispatch_get_main_queue (), ^{
288      GstGLNSWindow *internal_win_id =
289          (__bridge GstGLNSWindow *)window_cocoa->priv->internal_win_id;
290      NSView *external_view =
291          (__bridge NSView *)window_cocoa->priv->external_view;
292
293      NSView *view = [internal_win_id contentView];
294      [internal_win_id orderOut:internal_win_id];
295
296      [external_view addSubview: view];
297
298      [external_view setAutoresizesSubviews: YES];
299      [view setFrame: [external_view bounds]];
300      [view setAutoresizingMask: NSViewWidthSizable|NSViewHeightSizable];
301    });
302  } else {
303    /* no internal window yet so delay it to the next drawing */
304    priv->external_view = (gpointer)handle;
305    priv->visible = FALSE;
306  }
307}
308
309static void
310_show_window (gpointer data)
311{
312  GstGLWindowCocoa *window_cocoa = GST_GL_WINDOW_COCOA (data);
313  GstGLWindowCocoaPrivate *priv = window_cocoa->priv;
314  GstGLNSWindow *internal_win_id = (__bridge GstGLNSWindow *)priv->internal_win_id;
315
316  GST_DEBUG_OBJECT (window_cocoa, "make the window available\n");
317  [internal_win_id makeMainWindow];
318  [internal_win_id orderFrontRegardless];
319  [internal_win_id setViewsNeedDisplay:YES];
320
321  priv->visible = TRUE;
322}
323
324static void
325gst_gl_window_cocoa_show (GstGLWindow * window)
326{
327  GstGLWindowCocoa *window_cocoa = GST_GL_WINDOW_COCOA (window);
328  GstGLWindowCocoaPrivate *priv = window_cocoa->priv;
329
330  if (!priv->visible) {
331    /* useful when set_window_handle is called before
332     * the internal NSWindow */
333    if (priv->external_view) {
334      gst_gl_window_cocoa_set_window_handle (window, (guintptr) priv->external_view);
335      priv->visible = TRUE;
336      return;
337    }
338
339    if (!priv->external_view && !priv->visible)
340      _invoke_on_main ((GstGLWindowCB) _show_window, gst_object_ref (window),
341          (GDestroyNotify) gst_object_unref);
342  }
343}
344
345static void
346gst_gl_window_cocoa_queue_resize (GstGLWindow * window)
347{
348  GstGLWindowCocoa *window_cocoa = GST_GL_WINDOW_COCOA (window);
349  GstGLNSView *view;
350  GstGLWindowCocoaPrivate *priv = window_cocoa->priv;
351  GstGLNSWindow *internal_win_id = (__bridge GstGLNSWindow *)priv->internal_win_id;
352
353  if (!g_atomic_int_get (&window_cocoa->priv->view_ready))
354    return;
355
356  view = (GstGLNSView *)[internal_win_id contentView];
357
358  [view->layer queueResize];
359}
360
361static void
362gst_gl_window_cocoa_draw (GstGLWindow * window)
363{
364  GstGLWindowCocoa *window_cocoa = GST_GL_WINDOW_COCOA (window);
365  GstGLNSView *view;
366  GstGLWindowCocoaPrivate *priv = window_cocoa->priv;
367  GstGLNSWindow *internal_win_id = (__bridge GstGLNSWindow *)priv->internal_win_id;
368
369  /* As the view is created asynchronously in the main thread we cannot know
370   * exactly when it will be ready to draw to */
371  if (!g_atomic_int_get (&window_cocoa->priv->view_ready))
372    return;
373
374  view = (GstGLNSView *)[internal_win_id contentView];
375
376  /* this redraws the GstGLCAOpenGLLayer which calls
377   * gst_gl_window_cocoa_draw_thread(). Use an explicit CATransaction since we
378   * don't know how often the main runloop is running.
379   */
380  [CATransaction begin];
381  [view setNeedsDisplay:YES];
382  [CATransaction commit];
383}
384
385static void
386gst_gl_window_cocoa_set_preferred_size (GstGLWindow * window, gint width,
387    gint height)
388{
389  GstGLWindowCocoa *window_cocoa = GST_GL_WINDOW_COCOA (window);
390
391  window_cocoa->priv->preferred_width = width;
392  window_cocoa->priv->preferred_height = height;
393}
394
395static void
396gst_gl_cocoa_draw_cb (GstGLWindowCocoa *window_cocoa)
397{
398  GstGLWindowCocoaPrivate *priv = window_cocoa->priv;
399  GstGLNSWindow *internal_win_id = (__bridge GstGLNSWindow *)priv->internal_win_id;
400
401  if (internal_win_id && ![internal_win_id isClosed]) {
402    GstGLWindow *window = GST_GL_WINDOW (window_cocoa);
403
404    /* draw opengl scene in the back buffer */
405    /* We do not need to change viewports like in other window implementations
406     * as the caopengllayer will take care of that for us. */
407    if (window->draw)
408      window->draw (window->draw_data);
409  }
410}
411
412static void
413gst_gl_cocoa_resize_cb (GstGLNSView * view, guint width, guint height)
414{
415  GstGLWindowCocoa *window_cocoa = view->window_cocoa;
416  GstGLWindow *window = GST_GL_WINDOW (window_cocoa);
417  GstGLContext *context = gst_gl_window_get_context (window);
418  GstGLWindowCocoaPrivate *priv = window_cocoa->priv;
419  GstGLNSWindow *internal_win_id = (__bridge GstGLNSWindow *)priv->internal_win_id;
420
421  if (internal_win_id && ![internal_win_id isClosed]) {
422    const GstGLFuncs *gl;
423    NSRect bounds = [view bounds];
424    NSRect visibleRect = [view visibleRect];
425    gint viewport_dim[4];
426    GstVideoRectangle viewport;
427
428    gl = context->gl_vtable;
429
430#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
431    bounds = [view convertRectToBacking:bounds];
432    visibleRect = [view convertRectToBacking:visibleRect];
433#endif
434
435    /* don't use the default gst_gl_window_resize() as that will marshal through
436     * the GL thread.  We are being called from the main thread by the
437     * caopengllayer */
438    if (window->resize)
439      window->resize (window->resize_data, width, height);
440
441    gl->GetIntegerv (GL_VIEWPORT, viewport_dim);
442
443    GST_DEBUG_OBJECT (window, "Window resized: bounds %lf %lf %lf %lf "
444                      "visibleRect %lf %lf %lf %lf, "
445                      "viewport dimensions %i %i %i %i",
446                      bounds.origin.x, bounds.origin.y,
447                      bounds.size.width, bounds.size.height,
448                      visibleRect.origin.x, visibleRect.origin.y,
449                      visibleRect.size.width, visibleRect.size.height,
450                      viewport_dim[0], viewport_dim[1], viewport_dim[2],
451                      viewport_dim[3]);
452
453    viewport.x = viewport_dim[0] - visibleRect.origin.x;
454    viewport.x = viewport_dim[1] - visibleRect.origin.y;
455    viewport.w = viewport_dim[2];
456    viewport.h = viewport_dim[3];
457
458    gl->Viewport (viewport.x, viewport.y, viewport.w, viewport.h);
459  }
460
461  gst_object_unref (context);
462}
463
464static void
465gst_gl_window_cocoa_send_message_async (GstGLWindow * window,
466    GstGLWindowCB callback, gpointer data, GDestroyNotify destroy)
467{
468  GstGLWindowCocoa *window_cocoa = (GstGLWindowCocoa *) window;
469  GstGLContext *context = gst_gl_window_get_context (window);
470  GThread *thread = gst_gl_context_get_thread (context);
471  GstGLWindowCocoaPrivate *priv = window_cocoa->priv;
472#if OS_OBJECT_USE_OBJC
473  dispatch_queue_t gl_queue = (__bridge dispatch_queue_t)priv->gl_queue;
474#else
475  dispatch_queue_t gl_queue = (dispatch_queue_t)priv->gl_queue;
476#endif
477
478  if (thread == g_thread_self()) {
479    /* this case happens for nested calls happening from inside the GCD queue */
480    callback (data);
481    if (destroy)
482      destroy (data);
483    gst_object_unref (context);
484  } else {
485    dispatch_async (gl_queue, ^{
486      gst_gl_context_activate (context, TRUE);
487      gst_object_unref (context);
488      callback (data);
489      if (destroy)
490        destroy (data);
491    });
492  }
493  if (thread)
494    g_thread_unref (thread);
495}
496
497struct SetRenderRectangle
498{
499 GstGLWindowCocoa *window_cocoa;
500 GstVideoRectangle rect;
501};
502
503static void
504_free_set_render_rectangle (struct SetRenderRectangle *render)
505{
506  if (render) {
507    if (render->window_cocoa) {
508      gst_object_unref (render->window_cocoa);
509    }
510    g_free (render);
511  }
512}
513
514static void
515_set_render_rectangle (gpointer data)
516{
517 struct SetRenderRectangle *render = data;
518 NSView *view;
519 GstGLWindowCocoaPrivate *priv = render->window_cocoa->priv;
520 GstGLNSWindow *internal_win_id = (__bridge GstGLNSWindow *)priv->internal_win_id;
521
522 GST_LOG_OBJECT (render->window_cocoa, "setting render rectangle %i,%i+%ix%i",
523                 render->rect.x, render->rect.y, render->rect.w, render->rect.h);
524 if (!g_atomic_int_get (&render->window_cocoa->priv->view_ready)) {
525   return;
526 }
527
528 view = [internal_win_id contentView];
529 NSRect newMainViewFrame = NSMakeRect(render->rect.x,
530                                      render->rect.y,
531                                      render->rect.w,
532                                      render->rect.h);
533
534 [view.superview setFrame:newMainViewFrame];
535 [view setFrame: view.superview.bounds];
536
537 [CATransaction begin];
538 [view setNeedsDisplay:YES];
539 [CATransaction commit];
540}
541
542static gboolean
543gst_gl_window_cocoa_set_render_rectangle (GstGLWindow * window, gint x, gint y, gint width, gint height)
544{
545 GstGLWindowCocoa *window_cocoa = (GstGLWindowCocoa *) window;
546 struct SetRenderRectangle *render;
547
548 render = g_new0 (struct SetRenderRectangle, 1);
549 render->window_cocoa = gst_object_ref (window_cocoa);
550 render->rect.x = x;
551 render->rect.y = y;
552 render->rect.w = width;
553 render->rect.h = height;
554
555 _invoke_on_main ((GstGLWindowCB) _set_render_rectangle, render,
556                  (GDestroyNotify) _free_set_render_rectangle);
557
558 return TRUE;
559}
560
561static gboolean
562gst_gl_window_cocoa_controls_viewport (GstGLWindow * window)
563{
564  return TRUE;
565}
566
567/* =============================================================*/
568/*                                                              */
569/*                    GstGLNSWindow implementation              */
570/*                                                              */
571/* =============================================================*/
572
573/* Must be called from the main thread */
574@implementation GstGLNSWindow
575
576- (id) initWithContentRect: (NSRect) contentRect
577        styleMask: (unsigned int) styleMask
578    backing: (NSBackingStoreType) bufferingType
579    defer: (BOOL) flag screen: (NSScreen *) aScreen
580    gstWin: (GstGLWindowCocoa *) cocoa {
581
582  m_isClosed = NO;
583  window_cocoa = cocoa;
584  GstGLWindowCocoaPrivate *priv = window_cocoa->priv;
585  GstGLNSWindow *internal_win_id = (__bridge GstGLNSWindow *)priv->internal_win_id;
586
587  self = [super initWithContentRect: contentRect
588        styleMask: styleMask backing: bufferingType
589        defer: flag screen:aScreen];
590
591  [self setReleasedWhenClosed:NO];
592
593  GST_DEBUG ("initializing GstGLNSWindow\n");
594
595  [self setTitle:@"OpenGL renderer"];
596
597  [self setBackgroundColor:[NSColor blackColor]];
598
599  [self orderOut:internal_win_id];
600
601  return self;
602}
603
604- (void) setClosed {
605  m_isClosed = YES;
606}
607
608- (BOOL) isClosed {
609  return m_isClosed;
610}
611
612- (BOOL) canBecomeMainWindow {
613  return YES;
614}
615
616- (BOOL) canBecomeKeyWindow {
617  return YES;
618}
619
620static void
621close_window_cb (gpointer data)
622{
623  GstGLWindowCocoa *window_cocoa = data;
624  GstGLWindow *window;
625
626  window = GST_GL_WINDOW (window_cocoa);
627
628  if (window->close) {
629    window->close (window->close_data);
630  }
631}
632
633/* Called in the main thread which is never the gl thread */
634- (BOOL) windowShouldClose:(id)sender {
635
636  GstGLWindowCocoaPrivate *priv = window_cocoa->priv;
637  GstGLNSWindow *internal_win_id = (__bridge GstGLNSWindow *)priv->internal_win_id;
638  GST_DEBUG ("user clicked the close button\n");
639  [internal_win_id setClosed];
640  gst_gl_window_send_message_async (GST_GL_WINDOW (window_cocoa),
641      (GstGLWindowCB) close_window_cb, gst_object_ref (window_cocoa),
642      (GDestroyNotify) gst_object_unref);
643  return YES;
644}
645
646@end
647
648/* =============================================================*/
649/*                                                              */
650/*                GstGLNSView implementation              */
651/*                                                              */
652/* =============================================================*/
653
654@implementation GstGLNSView
655
656/* Must be called from the application main thread */
657- (id)initWithFrameLayer:(GstGLWindowCocoa *)window rect:(NSRect)contentRect layer:(CALayer *)layerContent {
658
659  self = [super initWithFrame: contentRect];
660
661  window_cocoa = window;
662
663  /* The order of the next two calls matters.  This creates a layer-hosted
664   * NSView.  Calling setWantsLayer before setLayer will create a
665   * layer-backed NSView.  See the apple developer documentation on the
666   * difference.
667   */
668  [self setLayer:layerContent];
669  [self setWantsLayer:YES];
670  self->layer = (GstGLCAOpenGLLayer *)layerContent;
671  [self->layer setDrawCallback:(GstGLWindowCB)gst_gl_cocoa_draw_cb
672      data:window notify:NULL];
673  [self->layer setResizeCallback:(GstGLWindowResizeCB)gst_gl_cocoa_resize_cb
674      data:(__bridge_retained gpointer)self notify:(GDestroyNotify)CFRelease];
675
676  [self setLayerContentsRedrawPolicy:NSViewLayerContentsRedrawOnSetNeedsDisplay];
677
678  [self setWantsBestResolutionOpenGLSurface:YES];
679
680  return self;
681}
682
683- (void) dealloc {
684  self->layer = nil;
685}
686
687- (void)renewGState {
688  /* Don't update the screen until we redraw, this
689   * prevents flickering during scrolling, clipping,
690   * resizing, etc
691   */
692  [[self window] disableScreenUpdatesUntilFlush];
693
694  [super renewGState];
695}
696
697- (BOOL) isOpaque {
698    return YES;
699}
700
701- (BOOL) isFlipped {
702    return NO;
703}
704
705@end
706
707void
708_invoke_on_main (GstGLWindowCB func, gpointer data, GDestroyNotify notify)
709{
710  if ([NSThread isMainThread]) {
711    func (data);
712    if (notify)
713      notify (data);
714  } else {
715    dispatch_async (dispatch_get_main_queue (), ^{
716      func (data);
717      if (notify)
718        notify (data);
719    });
720  }
721}
722