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