1/* 2 * GStreamer 3 * Copyright (C) 2015 Julien Isorce <julien.isorce@gmail.com> 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under 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. 9 * 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. 14 * 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. 19 */ 20 21#ifdef HAVE_CONFIG_H 22#include "config.h" 23#endif 24 25#if !defined(MAC_OS_X_VERSION_MAX_ALLOWED) || MAC_OS_X_VERSION_MAX_ALLOWED >= 1014 26# define GL_SILENCE_DEPRECATION 27#endif 28 29#include <Cocoa/Cocoa.h> 30 31#include <gst/gl/cocoa/gstgldisplay_cocoa.h> 32 33GST_DEBUG_CATEGORY_STATIC (gst_gl_display_debug); 34#define GST_CAT_DEFAULT gst_gl_display_debug 35 36G_DEFINE_TYPE (GstGLDisplayCocoa, gst_gl_display_cocoa, GST_TYPE_GL_DISPLAY); 37 38static void gst_gl_display_cocoa_finalize (GObject * object); 39static guintptr gst_gl_display_cocoa_get_handle (GstGLDisplay * display); 40 41#if MAC_OS_X_VERSION_MAX_ALLOWED < 101200 42#define NSEventMaskAny NSAnyEventMask 43#endif 44 45/* Define this if the GLib patch from 46 * https://bugzilla.gnome.org/show_bug.cgi?id=741450 47 * is used 48 */ 49#ifndef GSTREAMER_GLIB_COCOA_NSAPPLICATION 50 51static GstGLDisplayCocoa *singleton = NULL; 52static gint nsapp_source_id = 0; 53static GMutex nsapp_lock; 54static GCond nsapp_cond; 55 56static gboolean 57gst_gl_display_cocoa_nsapp_iteration (gpointer data) 58{ 59 NSEvent *event = nil; 60 61 if (![NSThread isMainThread]) { 62 GST_WARNING ("NSApp iteration not running in the main thread"); 63 return FALSE; 64 } 65 66 67 while ((event = ([NSApp nextEventMatchingMask:NSEventMaskAny 68 untilDate:[NSDate dateWithTimeIntervalSinceNow:0.05] 69 inMode:NSDefaultRunLoopMode dequeue:YES])) != nil) { 70 [NSApp sendEvent:event]; 71 } 72 73 return TRUE; 74} 75 76static void 77gst_gl_display_cocoa_open_and_attach_source (gpointer data) 78{ 79 if ([NSThread isMainThread]) { 80 /* The sharedApplication class method initializes 81 * the display environment and connects your program 82 * to the window server and the display server. 83 * It has to be done in the main thread. 84 */ 85 [NSApplication sharedApplication]; 86 87 GST_DEBUG ("Custom NSApp initialization done"); 88 89 nsapp_source_id = g_timeout_add (60, gst_gl_display_cocoa_nsapp_iteration, 90 NULL); 91 92 GST_DEBUG ("NSApp iteration loop attached, id %d", nsapp_source_id); 93 } 94} 95 96static gboolean 97gst_gl_display_cocoa_init_nsapp (gpointer data) 98{ 99 g_mutex_lock (&nsapp_lock); 100 101 gst_gl_display_cocoa_open_and_attach_source (data); 102 103 g_cond_signal (&nsapp_cond); 104 g_mutex_unlock (&nsapp_lock); 105 106 return FALSE; 107} 108 109static GstGLDisplayCocoa * 110gst_gl_display_cocoa_setup_nsapp (gpointer data) 111{ 112 GMainContext *context = g_main_context_default (); 113 gint delta_ms = 0; 114 115 g_mutex_lock (&nsapp_lock); 116 117 if (singleton) { 118 GST_DEBUG ("Get existing display"); 119 singleton = gst_object_ref (singleton); 120 g_mutex_unlock (&nsapp_lock); 121 return singleton; 122 } 123 124 if (NSApp != nil && !singleton) { 125 GstGLDisplayCocoa *ret = g_object_new (GST_TYPE_GL_DISPLAY_COCOA, NULL); 126 gst_object_ref_sink (ret); 127 g_mutex_unlock (&nsapp_lock); 128 return ret; 129 } 130 131 /* All application have to start with [NSApplication sharedApplication] 132 * so if NSApp is nil here let's assume this is a debugging application 133 * that runs a glib main loop. */ 134 g_assert (NSApp == nil); 135 136 GST_DEBUG ("The application has not initialized NSApp"); 137 138 if ([NSThread isMainThread]) { 139 140 GST_DEBUG ("Setting up NSApp from the main thread"); 141 if (g_main_context_is_owner (context)) { 142 GST_DEBUG ("The main thread own the context"); 143 gst_gl_display_cocoa_open_and_attach_source (data); 144 } else if (g_main_context_acquire (context)) { 145 GST_DEBUG ("The main loop should be shortly running in the main thread"); 146 gst_gl_display_cocoa_open_and_attach_source (data); 147 g_main_context_release (context); 148 } else { 149 GST_WARNING ("Main loop running in another thread"); 150 } 151 } else { 152 153 GST_DEBUG ("Setting up NSApp not from the main thread"); 154 155 if (g_main_context_is_owner (context)) { 156 GST_WARNING ("Default context not own by the main thread"); 157 delta_ms = -1; 158 } else if (g_main_context_acquire (context)) { 159 GST_DEBUG ("The main loop should be shortly running in the main thread"); 160 delta_ms = 1000; 161 g_main_context_release (context); 162 } else { 163 GST_DEBUG ("Main loop running in main thread"); 164 delta_ms = 500; 165 } 166 167 if (delta_ms > 0) { 168 gint64 end_time = g_get_monotonic_time () + delta_ms * 1000;; 169 g_idle_add_full (G_PRIORITY_HIGH, gst_gl_display_cocoa_init_nsapp, data, NULL); 170 g_cond_wait_until (&nsapp_cond, &nsapp_lock, end_time); 171 } 172 } 173 174 if (NSApp == nil) { 175 GST_ERROR ("Custom NSApp initialization failed"); 176 } else { 177 GST_DEBUG ("Create display"); 178 singleton = g_object_new (GST_TYPE_GL_DISPLAY_COCOA, NULL); 179 gst_object_ref_sink (singleton); 180 } 181 182 g_mutex_unlock (&nsapp_lock); 183 184 return singleton; 185} 186 187#endif 188 189static void 190gst_gl_display_cocoa_class_init (GstGLDisplayCocoaClass * klass) 191{ 192 GST_GL_DISPLAY_CLASS (klass)->get_handle = 193 GST_DEBUG_FUNCPTR (gst_gl_display_cocoa_get_handle); 194 195 G_OBJECT_CLASS (klass)->finalize = gst_gl_display_cocoa_finalize; 196} 197 198static void 199gst_gl_display_cocoa_init (GstGLDisplayCocoa * display_cocoa) 200{ 201 GstGLDisplay *display = (GstGLDisplay *) display_cocoa; 202 display->type = GST_GL_DISPLAY_TYPE_COCOA; 203} 204 205static void 206gst_gl_display_cocoa_finalize (GObject * object) 207{ 208#ifndef GSTREAMER_GLIB_COCOA_NSAPPLICATION 209 g_mutex_lock (&nsapp_lock); 210 if (singleton) { 211 GST_DEBUG ("Destroy display"); 212 singleton = NULL; 213 if (nsapp_source_id) { 214 GST_DEBUG ("Remove NSApp loop iteration, id %d", nsapp_source_id); 215 g_source_remove (nsapp_source_id); 216 } 217 nsapp_source_id = 0; 218 g_mutex_unlock (&nsapp_lock); 219 } 220 g_mutex_unlock (&nsapp_lock); 221#endif 222 223 G_OBJECT_CLASS (gst_gl_display_cocoa_parent_class)->finalize (object); 224} 225 226/** 227 * gst_gl_display_cocoa_new: 228 * 229 * Create a new #GstGLDisplayCocoa. 230 * 231 * Returns: (transfer full): a new #GstGLDisplayCocoa or %NULL 232 */ 233GstGLDisplayCocoa * 234gst_gl_display_cocoa_new (void) 235{ 236 GstGLDisplayCocoa *display; 237 238 GST_DEBUG_CATEGORY_GET (gst_gl_display_debug, "gldisplay"); 239 240#ifndef GSTREAMER_GLIB_COCOA_NSAPPLICATION 241 display = gst_gl_display_cocoa_setup_nsapp (NULL); 242#else 243 display = g_object_new (GST_TYPE_GL_DISPLAY_COCOA, NULL); 244 gst_object_ref_sink (display); 245#endif 246 247 return display; 248} 249 250static guintptr 251gst_gl_display_cocoa_get_handle (GstGLDisplay * display) 252{ 253 return (guintptr) NSApp; 254} 255