3 * Copyright (C) 2019 Matthew Waters <matthew@centricular.com>
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.
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>
26 #include <QuartzCore/QuartzCore.h>
30 #include <gst/vulkan/vulkan.h>
32 #include "gstvkwindow_cocoa.h"
33 #include "gstvkdisplay_cocoa.h"
35 #include "gstvkcocoa_utils.h"
37 #define GET_PRIV(o) gst_vulkan_window_cocoa_get_instance_private (o)
39 #define GST_CAT_DEFAULT gst_vulkan_window_cocoa_debug
40 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
45 static gsize _init = 0;
47 if (g_once_init_enter (&_init)) {
48 GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "vulkanwindowmacos", 0,
49 "Vulkan MacOS Window");
50 g_once_init_leave (&_init, 1);
54 gboolean gst_vulkan_window_cocoa_handle_event (GstVulkanWindowCocoa * window_cocoa);
61 struct _GstVulkanWindowCocoaPrivate
63 gpointer internal_win_id;
64 gpointer internal_view;
67 gint preferred_height;
72 #define gst_vulkan_window_cocoa_parent_class parent_class
73 G_DEFINE_TYPE_WITH_CODE (GstVulkanWindowCocoa, gst_vulkan_window_cocoa,
74 GST_TYPE_VULKAN_WINDOW, G_ADD_PRIVATE (GstVulkanWindowCocoa) _init_debug ());
76 static VkSurfaceKHR gst_vulkan_window_cocoa_get_surface (GstVulkanWindow * window,
78 static gboolean gst_vulkan_window_cocoa_get_presentation_support (GstVulkanWindow
79 * window, GstVulkanDevice * device, guint32 queue_family_idx);
80 static gboolean gst_vulkan_window_cocoa_open (GstVulkanWindow * window,
82 static void gst_vulkan_window_cocoa_close (GstVulkanWindow * window);
85 gst_vulkan_window_cocoa_finalize (GObject * object)
87 G_OBJECT_CLASS (parent_class)->finalize (object);
91 gst_vulkan_window_cocoa_class_init (GstVulkanWindowCocoaClass * klass)
93 GObjectClass *obj_class = G_OBJECT_CLASS (klass);
94 GstVulkanWindowClass *window_class = (GstVulkanWindowClass *) klass;
96 obj_class->finalize = gst_vulkan_window_cocoa_finalize;
98 window_class->open = GST_DEBUG_FUNCPTR (gst_vulkan_window_cocoa_open);
99 window_class->close = GST_DEBUG_FUNCPTR (gst_vulkan_window_cocoa_close);
100 window_class->get_surface = gst_vulkan_window_cocoa_get_surface;
101 window_class->get_presentation_support =
102 gst_vulkan_window_cocoa_get_presentation_support;
106 gst_vulkan_window_cocoa_init (GstVulkanWindowCocoa * window)
108 GstVulkanWindowCocoaPrivate *priv = GET_PRIV (window);
110 priv->preferred_width = 320;
111 priv->preferred_height = 240;
114 /* Must be called in the gl thread */
115 GstVulkanWindowCocoa *
116 gst_vulkan_window_cocoa_new (GstVulkanDisplay * display)
118 GstVulkanWindowCocoa *window;
122 if ((gst_vulkan_display_get_handle_type (display) &
123 GST_VULKAN_DISPLAY_TYPE_COCOA)
124 == GST_VULKAN_DISPLAY_TYPE_NONE) {
125 GST_INFO ("Wrong display type %u for this window type %u", display->type,
126 GST_VULKAN_DISPLAY_TYPE_COCOA);
130 window = g_object_new (GST_TYPE_VULKAN_WINDOW_COCOA, NULL);
131 gst_object_ref_sink (window);
137 _show_window (gpointer data)
139 GstVulkanWindowCocoa *window_cocoa = GST_VULKAN_WINDOW_COCOA (data);
140 GstVulkanWindowCocoaPrivate *priv = GET_PRIV (window_cocoa);
141 GstVulkanNSWindow *internal_win_id = (__bridge GstVulkanNSWindow *)priv->internal_win_id;
143 GST_DEBUG_OBJECT (window_cocoa, "showing");
144 [internal_win_id makeMainWindow];
145 [internal_win_id orderFrontRegardless];
146 [internal_win_id setViewsNeedDisplay:YES];
148 priv->visible = TRUE;
152 gst_vulkan_window_cocoa_show (GstVulkanWindow * window)
154 GstVulkanWindowCocoa *window_cocoa = GST_VULKAN_WINDOW_COCOA (window);
155 GstVulkanWindowCocoaPrivate *priv = GET_PRIV (window_cocoa);
158 _invoke_on_main ((GstVulkanWindowFunc) _show_window, gst_object_ref (window),
159 (GDestroyNotify) gst_object_unref);
163 gst_vulkan_window_cocoa_hide (GstVulkanWindow * window)
165 // GstVulkanWindowCocoa *window_cocoa = GST_VULKAN_WINDOW_COCOA (window);
167 /* FIXME: implement */
171 _create_window (GstVulkanWindowCocoa * window_cocoa)
173 GstVulkanWindowCocoaPrivate *priv = GET_PRIV (window_cocoa);
174 NSRect mainRect = [[NSScreen mainScreen] visibleFrame];
175 gint h = priv->preferred_height;
176 gint y = mainRect.size.height > h ? (mainRect.size.height - h) * 0.5 : 0;
177 NSRect rect = NSMakeRect (0, y, priv->preferred_width, priv->preferred_height);
178 GstVulkanNSWindow *internal_win_id;
179 GstVulkanNSView *view;
181 view = [[GstVulkanNSView alloc] initWithFrame:rect];
182 view.wantsLayer = YES;
184 internal_win_id = [[GstVulkanNSWindow alloc] initWithContentRect:rect styleMask:
185 (NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
186 NSWindowStyleMaskResizable | NSWindowStyleMaskMiniaturizable)
187 backing: NSBackingStoreBuffered defer: NO screen: nil gstWin: window_cocoa];
189 [internal_win_id setContentView:view];
191 priv->internal_win_id = (__bridge_retained gpointer)internal_win_id;
192 priv->internal_view = (__bridge_retained gpointer)view;
194 gst_vulkan_window_cocoa_show (GST_VULKAN_WINDOW (window_cocoa));
198 gst_vulkan_window_cocoa_create_window (GstVulkanWindowCocoa * window_cocoa)
200 _invoke_on_main ((GstVulkanWindowFunc) _create_window,
201 gst_object_ref (window_cocoa), gst_object_unref);
209 gst_vulkan_window_cocoa_get_surface (GstVulkanWindow * window, GError ** error)
211 GstVulkanWindowCocoa *window_cocoa = GST_VULKAN_WINDOW_COCOA (window);
212 GstVulkanWindowCocoaPrivate *priv = GET_PRIV (window_cocoa);
213 VkMacOSSurfaceCreateInfoMVK info = { 0, };
217 info.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
220 info.pView = priv->internal_view;
222 if (!window_cocoa->CreateMacOSSurface)
223 window_cocoa->CreateMacOSSurface =
224 gst_vulkan_instance_get_proc_address (window->display->instance,
225 "vkCreateMacOSSurfaceMVK");
226 if (!window_cocoa->CreateMacOSSurface) {
227 g_set_error_literal (error, GST_VULKAN_ERROR, VK_ERROR_FEATURE_NOT_PRESENT,
228 "Could not retrieve \"vkCreateMacOSSurfaceMVK\" function pointer");
233 window_cocoa->CreateMacOSSurface (window->display->instance->instance, &info,
235 if (gst_vulkan_error_to_g_error (err, error, "vkCreateMacOSSurfaceMVK") < 0)
242 gst_vulkan_window_cocoa_get_presentation_support (GstVulkanWindow * window,
243 GstVulkanDevice * device, guint32 queue_family_idx)
249 gst_vulkan_window_cocoa_open (GstVulkanWindow * window, GError ** error)
251 GstVulkanWindowCocoa *window_cocoa = GST_VULKAN_WINDOW_COCOA (window);
253 if (!GST_VULKAN_WINDOW_CLASS (parent_class)->open (window, error))
256 return gst_vulkan_window_cocoa_create_window (window_cocoa);
260 _close_window (gpointer * data)
262 GstVulkanWindowCocoa *window_cocoa = GST_VULKAN_WINDOW_COCOA (data);
263 GstVulkanWindow *window = GST_VULKAN_WINDOW (window_cocoa);
264 GstVulkanWindowCocoaPrivate *priv = GET_PRIV (window_cocoa);
265 GstVulkanNSWindow *internal_win_id =
266 (__bridge GstVulkanNSWindow *) priv->internal_win_id;
268 gst_vulkan_window_cocoa_hide (window);
270 [[internal_win_id contentView] removeFromSuperview];
271 CFBridgingRelease (priv->internal_win_id);
272 priv->internal_win_id = NULL;
273 CFBridgingRelease (priv->internal_view);
274 priv->internal_view = NULL;
278 gst_vulkan_window_cocoa_close (GstVulkanWindow * window)
280 _invoke_on_main ((GstVulkanWindowFunc) _close_window, gst_object_ref (window),
281 (GDestroyNotify) gst_object_unref);
283 GST_VULKAN_WINDOW_CLASS (parent_class)->close (window);
286 @implementation GstVulkanNSWindow
288 - (id) initWithContentRect: (NSRect) contentRect
289 styleMask: (unsigned int) styleMask
290 backing: (NSBackingStoreType) bufferingType
291 defer: (BOOL) flag screen: (NSScreen *) aScreen
292 gstWin: (GstVulkanWindowCocoa *) cocoa {
295 window_cocoa = cocoa;
297 self = [super initWithContentRect: contentRect
298 styleMask: styleMask backing: bufferingType
299 defer: flag screen:aScreen];
301 GST_DEBUG ("initializing GstVulkanNSWindow");
303 [self setReleasedWhenClosed:NO];
304 [self setTitle:@"Vulkan renderer"];
305 [self setBackgroundColor:[NSColor blackColor]];
306 [self orderOut:self];
319 - (BOOL) canBecomeMainWindow {
323 - (BOOL) canBecomeKeyWindow {
327 - (BOOL) windowShouldClose:(id)sender {
329 GstVulkanWindowCocoaPrivate *priv = GET_PRIV (window_cocoa);
330 GstVulkanNSWindow *internal_win_id = (__bridge GstVulkanNSWindow *)priv->internal_win_id;
331 GST_DEBUG ("user clicked the close button");
332 [internal_win_id setClosed];
339 @implementation GstVulkanNSView
341 -(BOOL) wantsUpdateLayer
348 return [CAMetalLayer class];
351 -(CALayer*) makeBackingLayer
353 CALayer* layer = [self.class.layerClass layer];
354 CGSize viewScale = [self convertSizeToBacking: CGSizeMake(1.0, 1.0)];
355 layer.contentsScale = MIN(viewScale.width, viewScale.height);
362 _invoke_on_main (GstVulkanWindowFunc func, gpointer data, GDestroyNotify notify)
364 if ([NSThread isMainThread]) {
369 dispatch_async (dispatch_get_main_queue (), ^{