3 * Copyright (C) 2008 Julien Isorce <julien.isorce@gmail.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it un der the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
25 #include <Cocoa/Cocoa.h>
27 #include "gstgl_cocoa_private.h"
29 /* =============================================================*/
31 /* GstGLNSWindow declaration */
33 /* =============================================================*/
35 @interface GstGLNSWindow: NSWindow {
37 GstGLWindowCocoa *m_cocoa;
39 - (id)initWithContentRect:(NSRect)contentRect
40 styleMask: (unsigned int) styleMask
41 backing: (NSBackingStoreType) bufferingType
42 defer: (BOOL) flag screen: (NSScreen *) aScreen
43 gstWin: (GstGLWindowCocoa *) window;
46 - (BOOL) canBecomeMainWindow;
47 - (BOOL) canBecomeKeyWindow;
50 /* =============================================================*/
54 /* =============================================================*/
57 static BOOL GSRegisterCurrentThread(void) { return TRUE; };
58 static void GSUnregisterCurrentThread(void) {};
61 #define GST_GL_WINDOW_COCOA_GET_PRIVATE(o) \
62 (G_TYPE_INSTANCE_GET_PRIVATE((o), GST_GL_TYPE_WINDOW_COCOA, GstGLWindowCocoaPrivate))
64 #define GST_CAT_DEFAULT gst_gl_window_cocoa_debug
65 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
68 GST_DEBUG_CATEGORY_GET (GST_CAT_DEFAULT, "glwindow");
69 #define gst_gl_window_cocoa_parent_class parent_class
70 G_DEFINE_TYPE_WITH_CODE (GstGLWindowCocoa, gst_gl_window_cocoa, GST_GL_TYPE_WINDOW, DEBUG_INIT);
72 static guintptr gst_gl_window_cocoa_get_window_handle (GstGLWindow * window);
73 static void gst_gl_window_cocoa_set_window_handle (GstGLWindow * window,
75 static void gst_gl_window_cocoa_draw (GstGLWindow * window, guint width, guint height);
76 static void gst_gl_window_cocoa_run (GstGLWindow * window);
77 static void gst_gl_window_cocoa_quit (GstGLWindow * window);
78 static void gst_gl_window_cocoa_send_message_async (GstGLWindow * window,
79 GstGLWindowCB callback, gpointer data, GDestroyNotify destroy);
81 struct _GstGLWindowCocoaPrivate
83 GstGLNSWindow *internal_win_id;
91 gst_gl_window_cocoa_class_init (GstGLWindowCocoaClass * klass)
93 GstGLWindowClass *window_class;
96 NSAutoreleasePool* pool = nil;
99 window_class = (GstGLWindowClass *) klass;
101 g_type_class_add_private (klass, sizeof (GstGLWindowCocoaPrivate));
103 window_class->get_window_handle =
104 GST_DEBUG_FUNCPTR (gst_gl_window_cocoa_get_window_handle);
105 window_class->set_window_handle =
106 GST_DEBUG_FUNCPTR (gst_gl_window_cocoa_set_window_handle);
107 window_class->draw_unlocked = GST_DEBUG_FUNCPTR (gst_gl_window_cocoa_draw);
108 window_class->draw = GST_DEBUG_FUNCPTR (gst_gl_window_cocoa_draw);
109 window_class->run = GST_DEBUG_FUNCPTR (gst_gl_window_cocoa_run);
110 window_class->quit = GST_DEBUG_FUNCPTR (gst_gl_window_cocoa_quit);
111 window_class->send_message_async =
112 GST_DEBUG_FUNCPTR (gst_gl_window_cocoa_send_message_async);
115 pool = [[NSAutoreleasePool alloc] init];
116 [NSApplication sharedApplication];
123 gst_gl_window_cocoa_init (GstGLWindowCocoa * window)
125 window->priv = GST_GL_WINDOW_COCOA_GET_PRIVATE (window);
127 gst_gl_window_set_need_lock (GST_GL_WINDOW (window), FALSE);
130 /* Must be called in the gl thread */
132 gst_gl_window_cocoa_new (void)
134 GstGLWindowCocoa *window = g_object_new (GST_GL_TYPE_WINDOW_COCOA, NULL);
140 gst_gl_window_cocoa_create_window (GstGLWindowCocoa *window_cocoa)
142 GstGLWindow *window = GST_GL_WINDOW (window_cocoa);
143 GstGLContext *context = gst_gl_window_get_context (window);
144 GstGLContextCocoa *context_cocoa = GST_GL_CONTEXT_COCOA (context);
145 GstGLWindowCocoaPrivate *priv = window_cocoa->priv;
146 NSRect rect = context_cocoa->priv->rect;
148 priv->internal_win_id =[[GstGLNSWindow alloc] initWithContentRect:rect styleMask:
149 (NSTitledWindowMask | NSClosableWindowMask |
150 NSResizableWindowMask | NSMiniaturizableWindowMask)
151 backing: NSBackingStoreBuffered defer: NO screen: nil gstWin: window_cocoa];
153 GST_DEBUG ("NSWindow id: %lud\n", (guintptr) priv->internal_win_id);
155 priv->thread = [NSThread currentThread];
157 [NSApp setDelegate: priv->internal_win_id];
159 gst_object_unref (context);
165 gst_gl_window_cocoa_get_window_handle (GstGLWindow *window)
167 return (guintptr) GST_GL_WINDOW_COCOA (window)->priv->internal_win_id;
171 gst_gl_window_cocoa_set_window_handle (GstGLWindow * window, guintptr handle)
173 GstGLWindowCocoa *window_cocoa;
174 GstGLWindowCocoaPrivate *priv;
176 window_cocoa = GST_GL_WINDOW_COCOA (window);
177 priv = window_cocoa->priv;
179 priv->parent = (NSWindow*) handle;
180 if (priv->internal_win_id) {
181 GstGLContextCocoa *context = (GstGLContextCocoa *) gst_gl_window_get_context (window);
182 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
183 AppThreadPerformer* app_thread_performer = [[AppThreadPerformer alloc] init:window_cocoa];
186 GSRegisterCurrentThread();
189 g_source_remove (context->priv->source_id);
190 gst_object_unref (context);
193 [app_thread_performer performSelectorOnMainThread:@selector(setWindow)
194 withObject:0 waitUntilDone:YES];
202 gst_gl_window_cocoa_draw (GstGLWindow * window, guint width, guint height)
204 GstGLWindowCocoa *window_cocoa;
205 GstGLWindowCocoaPrivate *priv;
206 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
207 AppThreadPerformer* app_thread_performer;
209 window_cocoa = GST_GL_WINDOW_COCOA (window);
210 priv = window_cocoa->priv;
212 GSRegisterCurrentThread();
214 app_thread_performer = [[AppThreadPerformer alloc] init:window_cocoa];
215 [app_thread_performer performSelector:@selector(updateWindow)
216 onThread:priv->thread withObject:nil waitUntilDone:YES];
218 if (!priv->parent && !priv->visible) {
222 NSRect mainRect = [[NSScreen mainScreen] visibleFrame];
223 NSRect windowRect = [priv->internal_win_id frame];
225 GST_DEBUG ("main screen rect: %d %d %d %d\n", (int) mainRect.origin.x,
226 (int) mainRect.origin.y, (int) mainRect.size.width,
227 (int) mainRect.size.height);
229 windowRect.origin.x += x;
230 windowRect.origin.y += mainRect.size.height > y ? (mainRect.size.height - y) * 0.5 : y;
231 windowRect.size.width = width;
232 windowRect.size.height = height;
234 GST_DEBUG ("window rect: %d %d %d %d\n", (int) windowRect.origin.x,
235 (int) windowRect.origin.y, (int) windowRect.size.width,
236 (int) windowRect.size.height);
242 [priv->internal_win_id setFrame:windowRect display:NO];
243 GST_DEBUG ("make the window available\n");
244 [priv->internal_win_id makeMainWindow];
246 [app_thread_performer performSelector:@selector(orderFront)
247 onThread:priv->thread withObject:nil waitUntilDone:YES];
249 /*[priv->internal_win_id setViewsNeedDisplay:YES]; */
250 priv->visible = TRUE;
257 gst_gl_window_cocoa_run (GstGLWindow * window)
259 GstGLWindowCocoa *window_cocoa;
260 GstGLWindowCocoaPrivate *priv;
261 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
262 NSRunLoop *run_loop = [NSRunLoop currentRunLoop];
264 window_cocoa = GST_GL_WINDOW_COCOA (window);
265 priv = window_cocoa->priv;
267 [run_loop addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
269 GST_DEBUG ("begin loop\n");
271 if (priv->internal_win_id != nil) {
272 priv->running = TRUE;
273 while (priv->running)
274 [run_loop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
276 [priv->internal_win_id release];
277 priv->internal_win_id = nil;
282 GST_DEBUG ("end loop\n");
287 gst_gl_window_cocoa_quit (GstGLWindow * window)
289 GstGLWindowCocoa *window_cocoa;
290 GstGLWindowCocoaPrivate *priv;
292 window_cocoa = GST_GL_WINDOW_COCOA (window);
293 priv = window_cocoa->priv;
296 if (GSRegisterCurrentThread() || 1) {
297 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
299 AppThreadPerformer* app_thread_performer = [[AppThreadPerformer alloc]
300 initWithAll:window_cocoa callback:NULL userData:NULL];
301 [app_thread_performer performSelector:@selector(stopApp)
302 onThread:priv->thread withObject:nil waitUntilDone:YES];
306 GSUnregisterCurrentThread();
309 GST_DEBUG ("failed to register current thread, application thread is lost\n");
315 gst_gl_window_cocoa_send_message_async (GstGLWindow * window,
316 GstGLWindowCB callback, gpointer data, GDestroyNotify destroy)
318 GstGLWindowCocoa *window_cocoa;
319 GstGLWindowCocoaPrivate *priv;
321 window_cocoa = GST_GL_WINDOW_COCOA (window);
322 priv = window_cocoa->priv;
324 GSRegisterCurrentThread ();
327 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
329 AppThreadPerformer* app_thread_performer = [[AppThreadPerformer alloc]
330 initWithAll:window_cocoa callback:callback userData:data];
332 [app_thread_performer performSelector:@selector(sendToApp) onThread:priv->thread
333 withObject:nil waitUntilDone:NO];
339 /* =============================================================*/
341 /* GstGLNSWindow implementation */
343 /* =============================================================*/
345 @implementation GstGLNSWindow
347 - (id) initWithContentRect: (NSRect) contentRect
348 styleMask: (unsigned int) styleMask
349 backing: (NSBackingStoreType) bufferingType
350 defer: (BOOL) flag screen: (NSScreen *) aScreen
351 gstWin: (GstGLWindowCocoa *) cocoa {
356 self = [super initWithContentRect: contentRect
357 styleMask: styleMask backing: bufferingType
358 defer: flag screen:aScreen];
360 [self setReleasedWhenClosed:NO];
362 GST_DEBUG ("initializing GstGLNSWindow\n");
364 [self setTitle:@"OpenGL renderer"];
366 [self setBackgroundColor:[NSColor clearColor]];
368 [self orderOut:m_cocoa->priv->internal_win_id];
370 if (m_cocoa->priv->parent) {
371 NSWindow *window = m_cocoa->priv->parent;
372 [window setContentView: [m_cocoa->priv->internal_win_id contentView]];
386 - (BOOL) canBecomeMainWindow {
390 - (BOOL) canBecomeKeyWindow {
394 /* Called in the main thread which is never the gl thread */
395 - (BOOL) windowShouldClose:(id)sender {
397 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
398 AppThreadPerformer* app_thread_performer = [[AppThreadPerformer alloc]
401 GST_DEBUG ("user clicked the close button\n");
403 [app_thread_performer performSelector:@selector(closeWindow) onThread:m_cocoa->priv->thread
404 withObject:nil waitUntilDone:YES];
411 - (void) applicationDidFinishLaunching: (NSNotification *) not {
414 - (void) applicationWillFinishLaunching: (NSNotification *) not {
417 - (BOOL) applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)app {
418 /* the application is manually stopped by calling stopApp on the AppThreadPerformer */
422 - (void) applicationWillTerminate:(NSNotification *)aNotification {
424 /* fixes segfault with gst-launch-1.0 -e ... and sending SIGINT (Ctrl-C)
425 * which causes GNUstep to run a signal handler in the main thread.
426 * However that thread has never been 'registered' with GNUstep so
427 * the autorelease magic of objective-c causes a segfault from accessing
428 * a null NSThread object somewhere deep in GNUstep.
430 * I put it here because this is the first time we can register the thread.
432 GSRegisterCurrentThread();
439 /* =============================================================*/
441 /* GstGLNSOpenGLView implementation */
443 /* =============================================================*/
445 @implementation GstGLNSOpenGLView
447 - (id)initWithFrame:(GstGLWindowCocoa *)window rect:(NSRect)contentRect pixelFormat:(NSOpenGLPixelFormat *)fmt {
449 self = [super initWithFrame: contentRect pixelFormat: fmt];
455 [self setWantsLayer:NO];
464 window = GST_GL_WINDOW (m_cocoa);
466 if (m_resizeCount % 5 == 0) {
468 if (window->resize) {
470 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
471 NSRect bounds = [self bounds];
472 AppThreadPerformer* app_thread_performer = [[AppThreadPerformer alloc]
473 initWithSize:m_cocoa callback:window->resize userData:window->resize_data
476 [app_thread_performer performSelector:@selector(resizeWindow) onThread:m_cocoa->priv->thread
477 withObject:nil waitUntilDone:YES];
490 /* =============================================================*/
492 /* AppThreadPerformer implementation */
494 /* =============================================================*/
496 @implementation AppThreadPerformer
498 - (id) init: (GstGLWindowCocoa *) window {
508 - (id) initWithCallback:(GstGLWindowCocoa *)window callback:(GstGLWindowCB)callback userData:(gpointer)data {
510 m_callback = callback;
518 - (id) initWithSize: (GstGLWindowCocoa *) window
519 callback:(GstGLWindowResizeCB)callback userData:(gpointer)data
520 toSize:(NSSize)size {
523 m_callback2 = callback;
525 m_width = size.width;
526 m_height = size.height;
530 - (id) initWithAll: (GstGLWindowCocoa *) window
531 callback:(GstGLWindowCB) callback userData: (gpointer) data {
533 m_callback = callback;
541 - (void) updateWindow {
542 if (m_cocoa->priv->running) {
544 if (![m_cocoa->priv->internal_win_id isClosed]) {
545 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
547 /* draw opengl scene in the back buffer */
548 GST_GL_WINDOW (m_cocoa)->draw (GST_GL_WINDOW (m_cocoa)->draw_data);
549 /* Copy the back buffer to the front buffer */
550 [[[m_cocoa->priv->internal_win_id contentView] openGLContext] flushBuffer];
557 - (void) resizeWindow {
558 if (m_cocoa->priv->running && ![m_cocoa->priv->internal_win_id isClosed]) {
559 m_callback2 (m_data, m_width, m_height);
560 [[[m_cocoa->priv->internal_win_id contentView] openGLContext] update];
561 GST_GL_WINDOW (m_cocoa)->draw (GST_GL_WINDOW (m_cocoa)->draw_data);
562 [[[m_cocoa->priv->internal_win_id contentView] openGLContext] flushBuffer];
572 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
573 NSWindow *window = m_cocoa->priv->parent;
575 [m_cocoa->priv->internal_win_id orderOut:m_cocoa->priv->internal_win_id];
577 [window setContentView: [m_cocoa->priv->internal_win_id contentView]];
584 NSAutoreleasePool *pool = nil;
587 m_cocoa->priv->running = FALSE;
592 pool = [[NSAutoreleasePool alloc] init];
593 if ([NSApp isRunning])
599 - (void) closeWindow {
602 window = GST_GL_WINDOW (m_cocoa);
604 [m_cocoa->priv->internal_win_id setClosed];
606 window->close (window->close_data);
610 - (void) orderFront {
611 [m_cocoa->priv->internal_win_id orderFront:m_cocoa->priv->internal_win_id];