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