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