1 /*
2 * GStreamer
3 * Copyright (C) 2015 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/gst.h>
26 #include <locale.h>
27
28 #include "vkwindow_xcb.h"
29 #include "vkdisplay_xcb.h"
30
31 #define GST_VULKAN_WINDOW_XCB_GET_PRIVATE(o) \
32 (G_TYPE_INSTANCE_GET_PRIVATE((o), GST_TYPE_VULKAN_WINDOW_XCB, GstVulkanWindowXCBPrivate))
33
34 #define GST_CAT_DEFAULT gst_vulkan_window_xcb_debug
35 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
36
37 static void
_init_debug(void)38 _init_debug (void)
39 {
40 static volatile gsize _init = 0;
41
42 if (g_once_init_enter (&_init)) {
43 GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "vulkanwindowxcb", 0,
44 "Vulkan XCB Window");
45 g_once_init_leave (&_init, 1);
46 }
47 }
48
49 gboolean gst_vulkan_window_xcb_handle_event (GstVulkanWindowXCB * window_xcb);
50
51 enum
52 {
53 PROP_0,
54 };
55
56 struct _GstVulkanWindowXCBPrivate
57 {
58 gboolean activate;
59 gboolean activate_result;
60
61 gint preferred_width;
62 gint preferred_height;
63
64 xcb_intern_atom_reply_t *atom_wm_delete_window;
65 };
66
67 #define gst_vulkan_window_xcb_parent_class parent_class
68 G_DEFINE_TYPE_WITH_CODE (GstVulkanWindowXCB, gst_vulkan_window_xcb,
69 GST_TYPE_VULKAN_WINDOW, G_ADD_PRIVATE (GstVulkanWindowXCB) _init_debug ());
70
71 static VkSurfaceKHR gst_vulkan_window_xcb_get_surface (GstVulkanWindow * window,
72 GError ** error);
73 static gboolean gst_vulkan_window_xcb_get_presentation_support (GstVulkanWindow
74 * window, GstVulkanDevice * device, guint32 queue_family_idx);
75 static gboolean gst_vulkan_window_xcb_open (GstVulkanWindow * window,
76 GError ** error);
77 static void gst_vulkan_window_xcb_close (GstVulkanWindow * window);
78
79 static void
gst_vulkan_window_xcb_finalize(GObject * object)80 gst_vulkan_window_xcb_finalize (GObject * object)
81 {
82 G_OBJECT_CLASS (parent_class)->finalize (object);
83 }
84
85 static void
gst_vulkan_window_xcb_class_init(GstVulkanWindowXCBClass * klass)86 gst_vulkan_window_xcb_class_init (GstVulkanWindowXCBClass * klass)
87 {
88 GObjectClass *obj_class = G_OBJECT_CLASS (klass);
89 GstVulkanWindowClass *window_class = (GstVulkanWindowClass *) klass;
90
91 obj_class->finalize = gst_vulkan_window_xcb_finalize;
92
93 window_class->open = GST_DEBUG_FUNCPTR (gst_vulkan_window_xcb_open);
94 window_class->close = GST_DEBUG_FUNCPTR (gst_vulkan_window_xcb_close);
95 window_class->get_surface = gst_vulkan_window_xcb_get_surface;
96 window_class->get_presentation_support =
97 gst_vulkan_window_xcb_get_presentation_support;
98 }
99
100 static void
gst_vulkan_window_xcb_init(GstVulkanWindowXCB * window)101 gst_vulkan_window_xcb_init (GstVulkanWindowXCB * window)
102 {
103 window->priv = gst_vulkan_window_xcb_get_instance_private (window);
104 }
105
106 /* Must be called in the gl thread */
107 GstVulkanWindowXCB *
gst_vulkan_window_xcb_new(GstVulkanDisplay * display)108 gst_vulkan_window_xcb_new (GstVulkanDisplay * display)
109 {
110 GstVulkanWindowXCB *window;
111
112 _init_debug ();
113
114 if ((gst_vulkan_display_get_handle_type (display) &
115 GST_VULKAN_DISPLAY_TYPE_XCB)
116 == GST_VULKAN_DISPLAY_TYPE_NONE) {
117 GST_INFO ("Wrong display type %u for this window type %u", display->type,
118 GST_VULKAN_DISPLAY_TYPE_XCB);
119 return NULL;
120 }
121
122 window = g_object_new (GST_TYPE_VULKAN_WINDOW_XCB, NULL);
123 gst_object_ref_sink (window);
124
125 return window;
126 }
127
128 static void
gst_vulkan_window_xcb_show(GstVulkanWindow * window)129 gst_vulkan_window_xcb_show (GstVulkanWindow * window)
130 {
131 GstVulkanWindowXCB *window_xcb = GST_VULKAN_WINDOW_XCB (window);
132 GstVulkanDisplayXCB *display_xcb = GST_VULKAN_DISPLAY_XCB (window->display);
133 xcb_connection_t *connection =
134 GST_VULKAN_DISPLAY_XCB_CONNECTION (display_xcb);
135
136 if (!window_xcb->visible) {
137 xcb_map_window (connection, window_xcb->win_id);
138 xcb_flush (connection);
139 window_xcb->visible = TRUE;
140 }
141 }
142
143 static void
gst_vulkan_window_xcb_hide(GstVulkanWindow * window)144 gst_vulkan_window_xcb_hide (GstVulkanWindow * window)
145 {
146 GstVulkanWindowXCB *window_xcb = GST_VULKAN_WINDOW_XCB (window);
147 GstVulkanDisplayXCB *display_xcb = GST_VULKAN_DISPLAY_XCB (window->display);
148 xcb_connection_t *connection =
149 GST_VULKAN_DISPLAY_XCB_CONNECTION (display_xcb);
150
151 if (window_xcb->visible) {
152 xcb_unmap_window (connection, window_xcb->win_id);
153 window_xcb->visible = FALSE;
154 }
155 }
156
157 gboolean
gst_vulkan_window_xcb_create_window(GstVulkanWindowXCB * window_xcb)158 gst_vulkan_window_xcb_create_window (GstVulkanWindowXCB * window_xcb)
159 {
160 GstVulkanDisplayXCB *display_xcb;
161 xcb_connection_t *connection;
162 xcb_screen_t *screen;
163 xcb_window_t root_window;
164 uint32_t value_mask, value_list[32];
165 xcb_intern_atom_cookie_t cookie, cookie2;
166 xcb_intern_atom_reply_t *reply, *reply2;
167 // const gchar *title = "OpenGL renderer";
168 gint x = 0, y = 0, width = 320, height = 240;
169
170 display_xcb =
171 GST_VULKAN_DISPLAY_XCB (GST_VULKAN_WINDOW (window_xcb)->display);
172 connection = GST_VULKAN_DISPLAY_XCB_CONNECTION (display_xcb);
173 root_window = GST_VULKAN_DISPLAY_XCB_ROOT_WINDOW (display_xcb);
174 screen = GST_VULKAN_DISPLAY_XCB_SCREEN (display_xcb);
175
176 window_xcb->win_id = xcb_generate_id (connection);
177
178 value_mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
179 value_list[0] = screen->black_pixel;
180 value_list[1] =
181 XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_EXPOSURE |
182 XCB_EVENT_MASK_STRUCTURE_NOTIFY;
183
184 xcb_create_window (connection, XCB_COPY_FROM_PARENT, window_xcb->win_id,
185 root_window, x, y, width, height, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
186 screen->root_visual, value_mask, value_list);
187
188 GST_LOG_OBJECT (window_xcb, "gl window id: %p",
189 (gpointer) (guintptr) window_xcb->win_id);
190 GST_LOG_OBJECT (window_xcb, "gl window props: x:%d y:%d", x, y);
191
192 /* Magic code that will send notification when window is destroyed */
193 cookie = xcb_intern_atom (connection, 1, 12, "WM_PROTOCOLS");
194 reply = xcb_intern_atom_reply (connection, cookie, 0);
195
196 cookie2 = xcb_intern_atom (connection, 0, 16, "WM_DELETE_WINDOW");
197 reply2 = xcb_intern_atom_reply (connection, cookie2, 0);
198
199 xcb_change_property (connection, XCB_PROP_MODE_REPLACE, window_xcb->win_id,
200 reply->atom, 4, 32, 1, &reply2->atom);
201 g_free (reply);
202 g_free (reply2);
203
204 gst_vulkan_window_xcb_show (GST_VULKAN_WINDOW (window_xcb));
205
206 return TRUE;
207 }
208
209 static VkSurfaceKHR
gst_vulkan_window_xcb_get_surface(GstVulkanWindow * window,GError ** error)210 gst_vulkan_window_xcb_get_surface (GstVulkanWindow * window, GError ** error)
211 {
212 GstVulkanWindowXCB *window_xcb = GST_VULKAN_WINDOW_XCB (window);
213 VkXcbSurfaceCreateInfoKHR info = { 0, };
214 VkSurfaceKHR ret;
215 VkResult err;
216
217 info.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
218 info.pNext = NULL;
219 info.flags = 0;
220 info.connection = GST_VULKAN_DISPLAY_XCB_CONNECTION (window->display);
221 info.window = GST_VULKAN_WINDOW_XCB (window)->win_id;
222
223 if (!window_xcb->CreateXcbSurface)
224 window_xcb->CreateXcbSurface =
225 gst_vulkan_instance_get_proc_address (window->display->instance,
226 "vkCreateXcbSurfaceKHR");
227 if (!window_xcb->CreateXcbSurface) {
228 g_set_error_literal (error, GST_VULKAN_ERROR, VK_ERROR_FEATURE_NOT_PRESENT,
229 "Could not retrieve \"vkCreateXcbSurfaceKHR\" function pointer");
230 return NULL;
231 }
232
233 err =
234 window_xcb->CreateXcbSurface (window->display->instance->instance, &info,
235 NULL, &ret);
236 if (gst_vulkan_error_to_g_error (err, error, "vkCreateXcbSurfaceKHR") < 0)
237 return NULL;
238
239 return ret;
240 }
241
242 static gboolean
gst_vulkan_window_xcb_get_presentation_support(GstVulkanWindow * window,GstVulkanDevice * device,guint32 queue_family_idx)243 gst_vulkan_window_xcb_get_presentation_support (GstVulkanWindow * window,
244 GstVulkanDevice * device, guint32 queue_family_idx)
245 {
246 GstVulkanWindowXCB *window_xcb = GST_VULKAN_WINDOW_XCB (window);
247 VkPhysicalDevice gpu;
248 xcb_screen_t *screen;
249
250 screen = GST_VULKAN_DISPLAY_XCB_SCREEN (window->display);
251
252 if (!window_xcb->GetPhysicalDeviceXcbPresentationSupport)
253 window_xcb->GetPhysicalDeviceXcbPresentationSupport =
254 gst_vulkan_instance_get_proc_address (window->display->instance,
255 "vkGetPhysicalDeviceXcbPresentationSupportKHR");
256 if (!window_xcb->GetPhysicalDeviceXcbPresentationSupport) {
257 GST_WARNING_OBJECT (window, "Could not retrieve "
258 "\"vkGetPhysicalDeviceXcbPresentationSupportKHR\" " "function pointer");
259 return FALSE;
260 }
261
262 gpu = gst_vulkan_device_get_physical_device (device);
263 if (window_xcb->GetPhysicalDeviceXcbPresentationSupport (gpu,
264 queue_family_idx, GST_VULKAN_DISPLAY_XCB_CONNECTION (window->display),
265 screen->root_visual))
266 return TRUE;
267 return FALSE;
268 }
269
270 static gboolean
gst_vulkan_window_xcb_open(GstVulkanWindow * window,GError ** error)271 gst_vulkan_window_xcb_open (GstVulkanWindow * window, GError ** error)
272 {
273 GstVulkanWindowXCB *window_xcb = GST_VULKAN_WINDOW_XCB (window);
274 GstVulkanDisplayXCB *display_xcb = (GstVulkanDisplayXCB *) window->display;
275 xcb_connection_t *connection;
276
277 connection = GST_VULKAN_DISPLAY_XCB_CONNECTION (display_xcb);
278 if (connection == NULL) {
279 g_set_error (error, GST_VULKAN_WINDOW_ERROR,
280 GST_VULKAN_WINDOW_ERROR_RESOURCE_UNAVAILABLE,
281 "Failed to connect to X display server with XCB");
282 goto failure;
283 }
284
285 if (!GST_VULKAN_WINDOW_CLASS (parent_class)->open (window, error))
286 return FALSE;
287
288 return gst_vulkan_window_xcb_create_window (window_xcb);
289
290 failure:
291 return FALSE;
292 }
293
294 static void
gst_vulkan_window_xcb_close(GstVulkanWindow * window)295 gst_vulkan_window_xcb_close (GstVulkanWindow * window)
296 {
297 GstVulkanWindowXCB *window_xcb = GST_VULKAN_WINDOW_XCB (window);
298 GstVulkanDisplayXCB *display_xcb = (GstVulkanDisplayXCB *) window->display;
299 xcb_connection_t *connection =
300 GST_VULKAN_DISPLAY_XCB_CONNECTION (display_xcb);
301
302 if (connection) {
303 gst_vulkan_window_xcb_hide (window);
304
305 g_free (window_xcb->priv->atom_wm_delete_window);
306 window_xcb->priv->atom_wm_delete_window = NULL;
307 GST_DEBUG ("display receiver closed");
308 }
309
310 GST_VULKAN_WINDOW_CLASS (parent_class)->close (window);
311 }
312