01126ebcaf874d31f98c2d702f715d522c1a731d
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-base / gst-libs / gst / gl / cocoa / gstgldisplay_cocoa.m
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
33 GST_DEBUG_CATEGORY_STATIC (gst_gl_display_debug);
34 #define GST_CAT_DEFAULT gst_gl_display_debug
35
36 G_DEFINE_TYPE (GstGLDisplayCocoa, gst_gl_display_cocoa, GST_TYPE_GL_DISPLAY);
37
38 static void gst_gl_display_cocoa_finalize (GObject * object);
39 static 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
51 static GstGLDisplayCocoa *singleton = NULL;
52 static gint nsapp_source_id = 0;
53 static GMutex nsapp_lock;
54 static GCond nsapp_cond;
55
56 static gboolean
57 gst_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
76 static void
77 gst_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
96 static gboolean
97 gst_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
109 static GstGLDisplayCocoa *
110 gst_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
189 static void
190 gst_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
198 static void
199 gst_gl_display_cocoa_init (GstGLDisplayCocoa * display_cocoa)
200 {
201   GstGLDisplay *display = (GstGLDisplay *) display_cocoa;
202   display->type = GST_GL_DISPLAY_TYPE_COCOA;
203 }
204
205 static void
206 gst_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  */
233 GstGLDisplayCocoa *
234 gst_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
250 static guintptr
251 gst_gl_display_cocoa_get_handle (GstGLDisplay * display)
252 {
253   return (guintptr) NSApp;
254 }