1 /*
2  * GStreamer
3  * Copyright (C) 2016 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 #define GLIB_DISABLE_DEPRECATION_WARNINGS
22 
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 
27 #include <linux/input.h>
28 
29 #include "wayland_event_source.h"
30 
31 #include "vkdisplay_wayland.h"
32 #include "vkwindow_wayland.h"
33 
34 #define GST_CAT_DEFAULT gst_vulkan_window_wayland_debug
35 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
36 
37 #define gst_vulkan_window_wayland_parent_class parent_class
38 G_DEFINE_TYPE (GstVulkanWindowWayland, gst_vulkan_window_wayland,
39     GST_TYPE_VULKAN_WINDOW)
40 
41      static void gst_vulkan_window_wayland_close (GstVulkanWindow * window);
42      static gboolean gst_vulkan_window_wayland_open (GstVulkanWindow * window,
43     GError ** error);
44      static VkSurfaceKHR gst_vulkan_window_wayland_get_surface (GstVulkanWindow
45     * window, GError ** error);
46      static gboolean
47          gst_vulkan_window_wayland_get_presentation_support (GstVulkanWindow *
48     window, GstVulkanDevice * device, guint32 queue_family_idx);
49 
50      static void
handle_ping(void * data,struct wl_shell_surface * shell_surface,uint32_t serial)51          handle_ping (void *data, struct wl_shell_surface *shell_surface,
52     uint32_t serial)
53 {
54   GstVulkanWindowWayland *window_wl = data;
55 
56   GST_TRACE_OBJECT (window_wl, "ping received serial %u", serial);
57 
58   wl_shell_surface_pong (shell_surface, serial);
59 }
60 
61 /*
62 static void window_resize (GstVulkanWindowWayland * window_wl, guint width,
63     guint height);*/
64 
65 static void
handle_configure(void * data,struct wl_shell_surface * shell_surface,uint32_t edges,int32_t width,int32_t height)66 handle_configure (void *data, struct wl_shell_surface *shell_surface,
67     uint32_t edges, int32_t width, int32_t height)
68 {
69   GstVulkanWindowWayland *window_wl = data;
70 
71   GST_DEBUG_OBJECT (window_wl, "configure event on surface %p, %ix%i",
72       shell_surface, width, height);
73 
74   /*window_resize (window_wl, width, height); */
75 }
76 
77 static void
handle_popup_done(void * data,struct wl_shell_surface * shell_surface)78 handle_popup_done (void *data, struct wl_shell_surface *shell_surface)
79 {
80 }
81 
82 static const struct wl_shell_surface_listener shell_surface_listener = {
83   handle_ping,
84   handle_configure,
85   handle_popup_done
86 };
87 
88 static void
destroy_surfaces(GstVulkanWindowWayland * window_wl)89 destroy_surfaces (GstVulkanWindowWayland * window_wl)
90 {
91   GST_DEBUG_OBJECT (window_wl, "destroying created surfaces");
92 
93   if (window_wl->shell_surface) {
94     wl_shell_surface_destroy (window_wl->shell_surface);
95     window_wl->shell_surface = NULL;
96   }
97   if (window_wl->surface) {
98     wl_surface_destroy (window_wl->surface);
99     window_wl->surface = NULL;
100   }
101 }
102 
103 static void
create_surfaces(GstVulkanWindowWayland * window_wl)104 create_surfaces (GstVulkanWindowWayland * window_wl)
105 {
106   GstVulkanDisplayWayland *display =
107       GST_VULKAN_DISPLAY_WAYLAND (GST_VULKAN_WINDOW (window_wl)->display);
108   gint width, height;
109 
110   if (!window_wl->surface) {
111     window_wl->surface = wl_compositor_create_surface (display->compositor);
112     if (window_wl->queue)
113       wl_proxy_set_queue ((struct wl_proxy *) window_wl->surface,
114           window_wl->queue);
115   }
116 
117   if (!window_wl->shell_surface) {
118     window_wl->shell_surface =
119         wl_shell_get_shell_surface (display->shell, window_wl->surface);
120     if (window_wl->queue)
121       wl_proxy_set_queue ((struct wl_proxy *) window_wl->shell_surface,
122           window_wl->queue);
123 
124     wl_shell_surface_add_listener (window_wl->shell_surface,
125         &shell_surface_listener, window_wl);
126 
127     wl_shell_surface_set_title (window_wl->shell_surface, "Vulkan Renderer");
128     wl_shell_surface_set_toplevel (window_wl->shell_surface);
129     GST_DEBUG_OBJECT (window_wl, "Successfully created shell surface %p",
130         window_wl->shell_surface);
131   }
132 
133   if (window_wl->window_width > 0)
134     width = window_wl->window_width;
135   else
136     width = 320;
137   window_wl->window_width = width;
138 
139   if (window_wl->window_height > 0)
140     height = window_wl->window_height;
141   else
142     height = 240;
143   window_wl->window_height = height;
144 }
145 
146 static void
gst_vulkan_window_wayland_class_init(GstVulkanWindowWaylandClass * klass)147 gst_vulkan_window_wayland_class_init (GstVulkanWindowWaylandClass * klass)
148 {
149   GstVulkanWindowClass *window_class = (GstVulkanWindowClass *) klass;
150 
151   window_class->close = GST_DEBUG_FUNCPTR (gst_vulkan_window_wayland_close);
152   window_class->open = GST_DEBUG_FUNCPTR (gst_vulkan_window_wayland_open);
153   window_class->get_surface =
154       GST_DEBUG_FUNCPTR (gst_vulkan_window_wayland_get_surface);
155   window_class->get_presentation_support =
156       GST_DEBUG_FUNCPTR (gst_vulkan_window_wayland_get_presentation_support);
157 }
158 
159 static void
gst_vulkan_window_wayland_init(GstVulkanWindowWayland * window)160 gst_vulkan_window_wayland_init (GstVulkanWindowWayland * window)
161 {
162 }
163 
164 GstVulkanWindowWayland *
gst_vulkan_window_wayland_new(GstVulkanDisplay * display)165 gst_vulkan_window_wayland_new (GstVulkanDisplay * display)
166 {
167   GstVulkanWindowWayland *window;
168 
169   if ((gst_vulkan_display_get_handle_type (display) &
170           GST_VULKAN_DISPLAY_TYPE_WAYLAND)
171       == 0)
172     /* we require a wayland display to create wayland surfaces */
173     return NULL;
174 
175   GST_DEBUG ("creating Wayland window");
176 
177   window = g_object_new (GST_TYPE_VULKAN_WINDOW_WAYLAND, NULL);
178   gst_object_ref_sink (window);
179 
180   return window;
181 }
182 
183 static void
gst_vulkan_window_wayland_close(GstVulkanWindow * window)184 gst_vulkan_window_wayland_close (GstVulkanWindow * window)
185 {
186   GstVulkanWindowWayland *window_wl;
187 
188   window_wl = GST_VULKAN_WINDOW_WAYLAND (window);
189 
190   destroy_surfaces (window_wl);
191 
192   g_source_destroy (window_wl->wl_source);
193   g_source_unref (window_wl->wl_source);
194   window_wl->wl_source = NULL;
195 
196   GST_VULKAN_WINDOW_CLASS (parent_class)->close (window);
197 }
198 
199 static gboolean
gst_vulkan_window_wayland_open(GstVulkanWindow * window,GError ** error)200 gst_vulkan_window_wayland_open (GstVulkanWindow * window, GError ** error)
201 {
202   GstVulkanDisplayWayland *display;
203   GstVulkanWindowWayland *window_wl = GST_VULKAN_WINDOW_WAYLAND (window);
204 
205   if (!GST_IS_VULKAN_DISPLAY_WAYLAND (window->display)) {
206     g_set_error (error, GST_VULKAN_WINDOW_ERROR,
207         GST_VULKAN_WINDOW_ERROR_RESOURCE_UNAVAILABLE,
208         "Failed to retrieve Wayland display (wrong type?)");
209     return FALSE;
210   }
211   display = GST_VULKAN_DISPLAY_WAYLAND (window->display);
212 
213   if (!display->display) {
214     g_set_error (error, GST_VULKAN_WINDOW_ERROR,
215         GST_VULKAN_WINDOW_ERROR_RESOURCE_UNAVAILABLE,
216         "Failed to retrieve Wayland display");
217     return FALSE;
218   }
219 
220   window_wl->queue = NULL;
221 
222   if (!GST_VULKAN_WINDOW_CLASS (parent_class)->open (window, error))
223     return FALSE;
224 
225   create_surfaces (window_wl);
226 
227   gst_vulkan_display_wayland_roundtrip_async (display);
228 
229   return TRUE;
230 }
231 
232 static VkSurfaceKHR
gst_vulkan_window_wayland_get_surface(GstVulkanWindow * window,GError ** error)233 gst_vulkan_window_wayland_get_surface (GstVulkanWindow * window,
234     GError ** error)
235 {
236   GstVulkanWindowWayland *window_wl = GST_VULKAN_WINDOW_WAYLAND (window);
237   VkWaylandSurfaceCreateInfoKHR info = { 0, };
238   VkSurfaceKHR ret;
239   VkResult err;
240 
241   info.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
242   info.pNext = NULL;
243   info.flags = 0;
244   info.display = GST_VULKAN_DISPLAY_WAYLAND_DISPLAY (window->display);
245   info.surface = window_wl->surface;
246 
247   if (!window_wl->CreateWaylandSurface)
248     window_wl->CreateWaylandSurface =
249         gst_vulkan_instance_get_proc_address (window->display->instance,
250         "vkCreateWaylandSurfaceKHR");
251   if (!window_wl->CreateWaylandSurface) {
252     g_set_error_literal (error, GST_VULKAN_ERROR, VK_ERROR_FEATURE_NOT_PRESENT,
253         "Could not retrieve \"vkCreateWaylandSurfaceKHR\" function pointer");
254     return NULL;
255   }
256 
257   err =
258       window_wl->CreateWaylandSurface (window->display->instance->instance,
259       &info, NULL, &ret);
260   if (gst_vulkan_error_to_g_error (err, error, "vkCreateWaylandSurfaceKHR") < 0)
261     return NULL;
262 
263   return ret;
264 }
265 
266 static gboolean
gst_vulkan_window_wayland_get_presentation_support(GstVulkanWindow * window,GstVulkanDevice * device,guint32 queue_family_idx)267 gst_vulkan_window_wayland_get_presentation_support (GstVulkanWindow * window,
268     GstVulkanDevice * device, guint32 queue_family_idx)
269 {
270   GstVulkanWindowWayland *window_wl = GST_VULKAN_WINDOW_WAYLAND (window);
271   VkPhysicalDevice gpu;
272 
273   if (!window_wl->GetPhysicalDeviceWaylandPresentationSupport)
274     window_wl->GetPhysicalDeviceWaylandPresentationSupport =
275         gst_vulkan_instance_get_proc_address (window->display->instance,
276         "vkGetPhysicalDeviceWaylandPresentationSupportKHR");
277   if (!window_wl->GetPhysicalDeviceWaylandPresentationSupport) {
278     GST_WARNING_OBJECT (window, "Could not retrieve "
279         "\"vkGetPhysicalDeviceWaylandPresentationSupportKHR\" "
280         "function pointer");
281     return FALSE;
282   }
283 
284   gpu = gst_vulkan_device_get_physical_device (device);
285   if (window_wl->GetPhysicalDeviceWaylandPresentationSupport (gpu,
286           queue_family_idx,
287           GST_VULKAN_DISPLAY_WAYLAND_DISPLAY (window->display)))
288     return TRUE;
289   return FALSE;
290 }
291