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