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