gstreamer/gst-libs/gst/gl/cocoa/gstglwindow_cocoa.m
Julien Isorce 3c49f0f42a gl/cocoa: ensure to call NSApplication:sharedApplication in the main thread
"(NSApplication *)sharedApplication This method also makes a connection
to the window server and completes other initialization"
The implicit thing which is not mentioned is that it required
to be called in the main thread.

Fix a regression introduces by 82b7c915bb
When using with gst-launch, it was not possible to click on the close
cross of the window anymore which is a bit anoying and also because
it's was possible before.

Prior to this commit the GstGLContextCocoaClass was initialized
in the main thread because gst_gl_context_new was called in the
state change function from going from ready to paused.

From this commit this call is done from the streaming thread.
So that the call to [NSApplication sharedApplication];
was not done in the main thread anymore.

We now ensure that by assuming there is a GMainLoop running.
It's for debugging purpose so that's ok to do that. Also
note we already do this assumtion to run app itereations.

The regression had no consequence on the cocoa/videooverlay example
(that should be moved from gst-plugins-gl to -bad) because the
application is responsible for that necessary call.
2014-04-12 15:46:47 +01:00

617 lines
18 KiB
Objective-C

/*
* GStreamer
* Copyright (C) 2008 Julien Isorce <julien.isorce@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it un der the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <Cocoa/Cocoa.h>
#include "gstgl_cocoa_private.h"
/* =============================================================*/
/* */
/* GstGLNSWindow declaration */
/* */
/* =============================================================*/
@interface GstGLNSWindow: NSWindow {
BOOL m_isClosed;
GstGLWindowCocoa *m_cocoa;
}
- (id)initWithContentRect:(NSRect)contentRect
styleMask: (unsigned int) styleMask
backing: (NSBackingStoreType) bufferingType
defer: (BOOL) flag screen: (NSScreen *) aScreen
gstWin: (GstGLWindowCocoa *) window;
- (void) setClosed;
- (BOOL) isClosed;
- (BOOL) canBecomeMainWindow;
- (BOOL) canBecomeKeyWindow;
@end
/* =============================================================*/
/* */
/* GstGLWindow */
/* */
/* =============================================================*/
#ifndef GNUSTEP
static BOOL GSRegisterCurrentThread(void) { return TRUE; };
static void GSUnregisterCurrentThread(void) {};
#endif
#define GST_GL_WINDOW_COCOA_GET_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE((o), GST_GL_TYPE_WINDOW_COCOA, GstGLWindowCocoaPrivate))
#define GST_CAT_DEFAULT gst_gl_window_cocoa_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
#define DEBUG_INIT \
GST_DEBUG_CATEGORY_GET (GST_CAT_DEFAULT, "glwindow");
#define gst_gl_window_cocoa_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstGLWindowCocoa, gst_gl_window_cocoa, GST_GL_TYPE_WINDOW, DEBUG_INIT);
static guintptr gst_gl_window_cocoa_get_window_handle (GstGLWindow * window);
static void gst_gl_window_cocoa_set_window_handle (GstGLWindow * window,
guintptr handle);
static void gst_gl_window_cocoa_draw (GstGLWindow * window, guint width, guint height);
static void gst_gl_window_cocoa_run (GstGLWindow * window);
static void gst_gl_window_cocoa_quit (GstGLWindow * window);
static void gst_gl_window_cocoa_send_message_async (GstGLWindow * window,
GstGLWindowCB callback, gpointer data, GDestroyNotify destroy);
struct _GstGLWindowCocoaPrivate
{
GstGLNSWindow *internal_win_id;
gboolean visible;
NSWindow *parent;
NSThread *thread;
gboolean running;
};
static void
gst_gl_window_cocoa_class_init (GstGLWindowCocoaClass * klass)
{
GstGLWindowClass *window_class;
window_class = (GstGLWindowClass *) klass;
g_type_class_add_private (klass, sizeof (GstGLWindowCocoaPrivate));
window_class->get_window_handle =
GST_DEBUG_FUNCPTR (gst_gl_window_cocoa_get_window_handle);
window_class->set_window_handle =
GST_DEBUG_FUNCPTR (gst_gl_window_cocoa_set_window_handle);
window_class->draw_unlocked = GST_DEBUG_FUNCPTR (gst_gl_window_cocoa_draw);
window_class->draw = GST_DEBUG_FUNCPTR (gst_gl_window_cocoa_draw);
window_class->run = GST_DEBUG_FUNCPTR (gst_gl_window_cocoa_run);
window_class->quit = GST_DEBUG_FUNCPTR (gst_gl_window_cocoa_quit);
window_class->send_message_async =
GST_DEBUG_FUNCPTR (gst_gl_window_cocoa_send_message_async);
}
static void
gst_gl_window_cocoa_init (GstGLWindowCocoa * window)
{
window->priv = GST_GL_WINDOW_COCOA_GET_PRIVATE (window);
}
/* Must be called in the gl thread */
GstGLWindowCocoa *
gst_gl_window_cocoa_new (void)
{
GstGLWindowCocoa *window = g_object_new (GST_GL_TYPE_WINDOW_COCOA, NULL);
return window;
}
gboolean
gst_gl_window_cocoa_create_window (GstGLWindowCocoa *window_cocoa)
{
GstGLWindow *window = GST_GL_WINDOW (window_cocoa);
GstGLContext *context = gst_gl_window_get_context (window);
GstGLContextCocoa *context_cocoa = GST_GL_CONTEXT_COCOA (context);
GstGLWindowCocoaPrivate *priv = window_cocoa->priv;
NSRect rect = context_cocoa->priv->rect;
priv->internal_win_id =[[GstGLNSWindow alloc] initWithContentRect:rect styleMask:
(NSTitledWindowMask | NSClosableWindowMask |
NSResizableWindowMask | NSMiniaturizableWindowMask)
backing: NSBackingStoreBuffered defer: NO screen: nil gstWin: window_cocoa];
GST_DEBUG ("NSWindow id: %"G_GUINTPTR_FORMAT, (guintptr) priv->internal_win_id);
priv->thread = [NSThread currentThread];
[NSApp setDelegate: priv->internal_win_id];
gst_object_unref (context);
return TRUE;
}
static guintptr
gst_gl_window_cocoa_get_window_handle (GstGLWindow *window)
{
return (guintptr) GST_GL_WINDOW_COCOA (window)->priv->internal_win_id;
}
static void
gst_gl_window_cocoa_set_window_handle (GstGLWindow * window, guintptr handle)
{
GstGLWindowCocoa *window_cocoa;
GstGLWindowCocoaPrivate *priv;
window_cocoa = GST_GL_WINDOW_COCOA (window);
priv = window_cocoa->priv;
if (priv->internal_win_id) {
GstGLContextCocoa *context = (GstGLContextCocoa *) gst_gl_window_get_context (window);
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
AppThreadPerformer* app_thread_performer = [[AppThreadPerformer alloc] init:window_cocoa];
GSRegisterCurrentThread();
if (context) {
if (context->priv->source_id) {
g_source_remove (context->priv->source_id);
context->priv->source_id = 0;
}
gst_object_unref (context);
}
if (handle) {
priv->parent = (NSWindow*) handle;
priv->visible = TRUE;
} else {
/* bring back our internal window */
priv->parent = priv->internal_win_id;
priv->visible = FALSE;
}
[app_thread_performer performSelectorOnMainThread:@selector(setWindow)
withObject:0 waitUntilDone:YES];
[pool release];
} else {
/* not internal window yet so delay it to the next drawing */
priv->parent = (NSWindow*) handle;
priv->visible = FALSE;
}
}
/* Thread safe */
static void
gst_gl_window_cocoa_draw (GstGLWindow * window, guint width, guint height)
{
GstGLWindowCocoa *window_cocoa;
GstGLWindowCocoaPrivate *priv;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
AppThreadPerformer* app_thread_performer;
window_cocoa = GST_GL_WINDOW_COCOA (window);
priv = window_cocoa->priv;
GSRegisterCurrentThread();
app_thread_performer = [[AppThreadPerformer alloc] init:window_cocoa];
[app_thread_performer performSelector:@selector(updateWindow)
onThread:priv->thread withObject:nil waitUntilDone:YES];
/* useful when set_window_handle is called before
* the internal NSWindow */
if (priv->parent && !priv->visible) {
gst_gl_window_cocoa_set_window_handle (window, (guintptr) priv->parent);
priv->visible = TRUE;
}
if (!priv->parent && !priv->visible) {
static gint x = 0;
static gint y = 0;
NSRect mainRect = [[NSScreen mainScreen] visibleFrame];
NSRect windowRect = [priv->internal_win_id frame];
GST_DEBUG ("main screen rect: %d %d %d %d\n", (int) mainRect.origin.x,
(int) mainRect.origin.y, (int) mainRect.size.width,
(int) mainRect.size.height);
windowRect.origin.x += x;
windowRect.origin.y += mainRect.size.height > y ? (mainRect.size.height - y) * 0.5 : y;
windowRect.size.width = width;
windowRect.size.height = height;
GST_DEBUG ("window rect: %d %d %d %d\n", (int) windowRect.origin.x,
(int) windowRect.origin.y, (int) windowRect.size.width,
(int) windowRect.size.height);
x += 20;
y += 20;
#ifndef GNUSTEP
[priv->internal_win_id setFrame:windowRect display:NO];
GST_DEBUG ("make the window available\n");
[priv->internal_win_id makeMainWindow];
#endif
[app_thread_performer performSelector:@selector(orderFront)
onThread:priv->thread withObject:nil waitUntilDone:YES];
/*[priv->internal_win_id setViewsNeedDisplay:YES]; */
priv->visible = TRUE;
}
[pool release];
}
static void
gst_gl_window_cocoa_run (GstGLWindow * window)
{
GstGLWindowCocoa *window_cocoa;
GstGLWindowCocoaPrivate *priv;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSRunLoop *run_loop = [NSRunLoop currentRunLoop];
window_cocoa = GST_GL_WINDOW_COCOA (window);
priv = window_cocoa->priv;
[run_loop addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
GST_DEBUG ("begin loop\n");
if (priv->internal_win_id != nil) {
priv->running = TRUE;
while (priv->running)
[run_loop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
[priv->internal_win_id release];
priv->internal_win_id = nil;
}
[pool release];
GST_DEBUG ("end loop\n");
}
/* Thread safe */
static void
gst_gl_window_cocoa_quit (GstGLWindow * window)
{
GstGLWindowCocoa *window_cocoa;
GstGLWindowCocoaPrivate *priv;
window_cocoa = GST_GL_WINDOW_COCOA (window);
priv = window_cocoa->priv;
if (window) {
if (GSRegisterCurrentThread() || 1) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
AppThreadPerformer* app_thread_performer = [[AppThreadPerformer alloc]
initWithAll:window_cocoa callback:NULL userData:NULL];
[app_thread_performer performSelector:@selector(stopApp)
onThread:priv->thread withObject:nil waitUntilDone:YES];
[pool release];
GSUnregisterCurrentThread();
}
else
GST_DEBUG ("failed to register current thread, application thread is lost\n");
}
}
/* Thread safe */
static void
gst_gl_window_cocoa_send_message_async (GstGLWindow * window,
GstGLWindowCB callback, gpointer data, GDestroyNotify destroy)
{
GstGLWindowCocoa *window_cocoa;
GstGLWindowCocoaPrivate *priv;
window_cocoa = GST_GL_WINDOW_COCOA (window);
priv = window_cocoa->priv;
GSRegisterCurrentThread ();
if (window) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
AppThreadPerformer* app_thread_performer = [[AppThreadPerformer alloc]
initWithAll:window_cocoa callback:callback userData:data];
[app_thread_performer performSelector:@selector(sendToApp) onThread:priv->thread
withObject:nil waitUntilDone:NO];
[pool release];
}
}
/* =============================================================*/
/* */
/* GstGLNSWindow implementation */
/* */
/* =============================================================*/
@implementation GstGLNSWindow
- (id) initWithContentRect: (NSRect) contentRect
styleMask: (unsigned int) styleMask
backing: (NSBackingStoreType) bufferingType
defer: (BOOL) flag screen: (NSScreen *) aScreen
gstWin: (GstGLWindowCocoa *) cocoa {
m_isClosed = NO;
m_cocoa = cocoa;
self = [super initWithContentRect: contentRect
styleMask: styleMask backing: bufferingType
defer: flag screen:aScreen];
[self setReleasedWhenClosed:NO];
GST_DEBUG ("initializing GstGLNSWindow\n");
[self setTitle:@"OpenGL renderer"];
[self setBackgroundColor:[NSColor clearColor]];
[self orderOut:m_cocoa->priv->internal_win_id];
if (m_cocoa->priv->parent) {
NSWindow *window = m_cocoa->priv->parent;
[window setContentView: [m_cocoa->priv->internal_win_id contentView]];
}
return self;
}
- (void) setClosed {
m_isClosed = YES;
}
- (BOOL) isClosed {
return m_isClosed;
}
- (BOOL) canBecomeMainWindow {
return YES;
}
- (BOOL) canBecomeKeyWindow {
return YES;
}
/* Called in the main thread which is never the gl thread */
- (BOOL) windowShouldClose:(id)sender {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
AppThreadPerformer* app_thread_performer = [[AppThreadPerformer alloc]
init:m_cocoa];
GST_DEBUG ("user clicked the close button\n");
[app_thread_performer performSelector:@selector(closeWindow) onThread:m_cocoa->priv->thread
withObject:nil waitUntilDone:YES];
[pool release];
return YES;
}
- (void) applicationDidFinishLaunching: (NSNotification *) not {
}
- (void) applicationWillFinishLaunching: (NSNotification *) not {
}
- (BOOL) applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)app {
/* the application is manually stopped by calling stopApp on the AppThreadPerformer */
return NO;
}
- (void) applicationWillTerminate:(NSNotification *)aNotification {
#ifdef GNUSTEP
/* fixes segfault with gst-launch-1.0 -e ... and sending SIGINT (Ctrl-C)
* which causes GNUstep to run a signal handler in the main thread.
* However that thread has never been 'registered' with GNUstep so
* the autorelease magic of objective-c causes a segfault from accessing
* a null NSThread object somewhere deep in GNUstep.
*
* I put it here because this is the first time we can register the thread.
*/
GSRegisterCurrentThread();
#endif
}
@end
/* =============================================================*/
/* */
/* GstGLNSOpenGLView implementation */
/* */
/* =============================================================*/
@implementation GstGLNSOpenGLView
- (id)initWithFrame:(GstGLWindowCocoa *)window rect:(NSRect)contentRect pixelFormat:(NSOpenGLPixelFormat *)fmt {
self = [super initWithFrame: contentRect pixelFormat: fmt];
m_cocoa = window;
#ifndef GNUSTEP
[self setWantsLayer:NO];
#endif
return self;
}
- (void)reshape {
GstGLWindow *window;
window = GST_GL_WINDOW (m_cocoa);
if (window->resize) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSRect bounds = [self bounds];
AppThreadPerformer* app_thread_performer = [[AppThreadPerformer alloc]
initWithSize:m_cocoa callback:window->resize userData:window->resize_data
toSize:bounds.size];
[app_thread_performer performSelector:@selector(resizeWindow) onThread:m_cocoa->priv->thread
withObject:nil waitUntilDone:YES];
[pool release];
}
}
- (void) update {
}
@end
/* =============================================================*/
/* */
/* AppThreadPerformer implementation */
/* */
/* =============================================================*/
@implementation AppThreadPerformer
- (id) init: (GstGLWindowCocoa *) window {
m_cocoa = window;
m_callback = NULL;
m_callback2 = NULL;
m_data = NULL;
m_width = 0;
m_height = 0;
return self;
}
- (id) initWithCallback:(GstGLWindowCocoa *)window callback:(GstGLWindowCB)callback userData:(gpointer)data {
m_cocoa = window;
m_callback = callback;
m_callback2 = NULL;
m_data = data;
m_width = 0;
m_height = 0;
return self;
}
- (id) initWithSize: (GstGLWindowCocoa *) window
callback:(GstGLWindowResizeCB)callback userData:(gpointer)data
toSize:(NSSize)size {
m_cocoa = window;
m_callback = NULL;
m_callback2 = callback;
m_data = data;
m_width = size.width;
m_height = size.height;
return self;
}
- (id) initWithAll: (GstGLWindowCocoa *) window
callback:(GstGLWindowCB) callback userData: (gpointer) data {
m_cocoa = window;
m_callback = callback;
m_callback2 = NULL;
m_data = data;
m_width = 0;
m_height = 0;
return self;
}
- (void) updateWindow {
if (m_cocoa->priv->running) {
if (![m_cocoa->priv->internal_win_id isClosed]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
/* draw opengl scene in the back buffer */
GST_GL_WINDOW (m_cocoa)->draw (GST_GL_WINDOW (m_cocoa)->draw_data);
/* Copy the back buffer to the front buffer */
[[[m_cocoa->priv->internal_win_id contentView] openGLContext] flushBuffer];
[pool release];
}
}
}
- (void) resizeWindow {
if (m_cocoa->priv->running && ![m_cocoa->priv->internal_win_id isClosed]) {
m_callback2 (m_data, m_width, m_height);
[[[m_cocoa->priv->internal_win_id contentView] openGLContext] update];
GST_GL_WINDOW (m_cocoa)->draw (GST_GL_WINDOW (m_cocoa)->draw_data);
[[[m_cocoa->priv->internal_win_id contentView] openGLContext] flushBuffer];
}
}
- (void) sendToApp {
if (m_callback)
m_callback (m_data);
}
- (void) setWindow {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSWindow *window = m_cocoa->priv->parent;
[m_cocoa->priv->internal_win_id orderOut:m_cocoa->priv->internal_win_id];
[window setContentView: [m_cocoa->priv->internal_win_id contentView]];
[pool release];
}
- (void) stopApp {
#ifdef GNUSTEP
NSAutoreleasePool *pool = nil;
#endif
m_cocoa->priv->running = FALSE;
if (m_callback)
m_callback (m_data);
#ifdef GNUSTEP
pool = [[NSAutoreleasePool alloc] init];
if ([NSApp isRunning])
[NSApp stop:self];
[pool release];
#endif
}
- (void) closeWindow {
GstGLWindow *window;
window = GST_GL_WINDOW (m_cocoa);
[m_cocoa->priv->internal_win_id setClosed];
if (window->close) {
window->close (window->close_data);
}
}
- (void) orderFront {
[m_cocoa->priv->internal_win_id orderFront:m_cocoa->priv->internal_win_id];
}
@end