wayland: fix double disconnect of display.
[vaapi:windyuan-gstreamer-vaapi.git] / gst-libs / gst / vaapi / gstvaapidisplay_wayland.c
1 /*
2  *  gstvaapidisplay_wayland.c - VA/Wayland display abstraction
3  *
4  *  Copyright (C) 2012 Intel Corporation
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Lesser General Public License
8  *  as published by the Free Software Foundation; either version 2.1
9  *  of the License, or (at your option) any later version.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public
17  *  License along with this library; if not, write to the Free
18  *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  *  Boston, MA 02110-1301 USA
20  */
21
22 /**
23  * SECTION:gstvaapidisplay_wayland
24  * @short_description: VA/Wayland display abstraction
25  */
26
27 #include "sysdeps.h"
28 #include <string.h>
29 #include "gstvaapidisplay_priv.h"
30 #include "gstvaapidisplay_wayland.h"
31 #include "gstvaapidisplay_wayland_priv.h"
32
33 #define DEBUG 1
34 #include "gstvaapidebug.h"
35
36 G_DEFINE_TYPE(GstVaapiDisplayWayland,
37               gst_vaapi_display_wayland,
38               GST_VAAPI_TYPE_DISPLAY);
39
40 enum {
41     PROP_0,
42
43     PROP_DISPLAY_NAME,
44     PROP_WL_DISPLAY
45 };
46
47 #define NAME_PREFIX "WLD:"
48 #define NAME_PREFIX_LENGTH 4
49
50 static inline gboolean
51 is_display_name(const gchar *display_name)
52 {
53     return strncmp(display_name, NAME_PREFIX, NAME_PREFIX_LENGTH) == 0;
54 }
55
56 static inline const gchar *
57 get_default_display_name(void)
58 {
59     static const gchar *g_display_name;
60
61     if (!g_display_name)
62         g_display_name = getenv("WAYLAND_DISPLAY");
63     return g_display_name;
64 }
65
66 static inline guint
67 get_display_name_length(const gchar *display_name)
68 {
69     const gchar *str;
70
71     str = strchr(display_name, '-');
72     if (str)
73         return str - display_name;
74     return strlen(display_name);
75 }
76
77 static gboolean
78 compare_display_name(gconstpointer a, gconstpointer b, gpointer user_data)
79 {
80     const gchar *cached_name = a;
81     const gchar *tested_name = b;
82     guint cached_name_length, tested_name_length;
83
84     if (!cached_name || !is_display_name(cached_name))
85         return FALSE;
86     cached_name += NAME_PREFIX_LENGTH;
87     cached_name_length = get_display_name_length(cached_name);
88
89     g_return_val_if_fail(tested_name && is_display_name(tested_name), FALSE);
90     tested_name += NAME_PREFIX_LENGTH;
91     tested_name_length = get_display_name_length(tested_name);
92
93     /* XXX: handle screen number and default WAYLAND_DISPLAY name */
94     if (cached_name_length != tested_name_length)
95         return FALSE;
96     if (strncmp(cached_name, tested_name, cached_name_length) != 0)
97         return FALSE;
98     return TRUE;
99 }
100
101 static void
102 gst_vaapi_display_wayland_finalize(GObject *object)
103 {
104     G_OBJECT_CLASS(gst_vaapi_display_wayland_parent_class)->finalize(object);
105 }
106
107 /* Reconstruct a display name without our prefix */
108 static const gchar *
109 get_display_name(gpointer ptr)
110 {
111     GstVaapiDisplayWayland * const display = GST_VAAPI_DISPLAY_WAYLAND(ptr);
112     const gchar *display_name = display->priv->display_name;
113
114     if (!display_name)
115         return NULL;
116
117     if (is_display_name(display_name)) {
118         display_name += NAME_PREFIX_LENGTH;
119         if (*display_name == '\0')
120             return NULL;
121         return display_name;
122     }
123
124     /* XXX: this should not happen */
125     g_assert(0 && "display name without prefix");
126     return display_name;
127 }
128
129 /* Mangle display name with our prefix */
130 static void
131 set_display_name(GstVaapiDisplayWayland *display, const gchar *display_name)
132 {
133     GstVaapiDisplayWaylandPrivate * const priv = display->priv;
134
135     g_free(priv->display_name);
136
137     if (!display_name) {
138         display_name = get_default_display_name();
139         if (!display_name)
140             display_name = "";
141     }
142     priv->display_name = g_strdup_printf("%s%s", NAME_PREFIX, display_name);
143 }
144
145 static void
146 gst_vaapi_display_wayland_set_property(
147     GObject      *object,
148     guint         prop_id,
149     const GValue *value,
150     GParamSpec   *pspec
151 )
152 {
153     GstVaapiDisplayWayland * const display = GST_VAAPI_DISPLAY_WAYLAND(object);
154
155     switch (prop_id) {
156     case PROP_DISPLAY_NAME:
157         set_display_name(display, g_value_get_string(value));
158         break;
159     case PROP_WL_DISPLAY:
160         display->priv->wl_display = g_value_get_pointer(value);
161         break;
162     default:
163         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
164         break;
165     }
166 }
167 static void
168 gst_vaapi_display_wayland_get_property(
169     GObject    *object,
170     guint       prop_id,
171     GValue     *value,
172     GParamSpec *pspec
173 )
174 {
175     GstVaapiDisplayWayland * const display = GST_VAAPI_DISPLAY_WAYLAND(object);
176
177     switch (prop_id) {
178     case PROP_DISPLAY_NAME:
179         g_value_set_string(value, get_display_name(display));
180         break;
181     case PROP_WL_DISPLAY:
182         g_value_set_pointer(value, gst_vaapi_display_wayland_get_display(display));
183         break;
184     default:
185         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
186         break;
187     }
188 }
189
190 static void
191 gst_vaapi_display_wayland_constructed(GObject *object)
192 {
193     GstVaapiDisplayWayland * const display = GST_VAAPI_DISPLAY_WAYLAND(object);
194     GstVaapiDisplayWaylandPrivate * const priv = display->priv;
195     GstVaapiDisplayCache * const cache = gst_vaapi_display_get_cache();
196     const GstVaapiDisplayInfo *info;
197     GObjectClass *parent_class;
198
199     priv->create_display = priv->wl_display == NULL;
200
201     /* Don't create Wayland display if there is one in the cache already */
202     if (priv->create_display) {
203         info = gst_vaapi_display_cache_lookup_by_name(
204             cache,
205             priv->display_name,
206             compare_display_name, NULL
207         );
208         if (info) {
209             priv->wl_display     = info->native_display;
210             priv->create_display = FALSE;
211         }
212     }
213
214     /* Reset display-name if the user provided his own Wayland display */
215     if (!priv->create_display) {
216         /* XXX: how to get socket/display name? */
217         GST_WARNING("wayland: get display name");
218         set_display_name(display, NULL);
219     }
220
221     parent_class = G_OBJECT_CLASS(gst_vaapi_display_wayland_parent_class);
222     if (parent_class->constructed)
223         parent_class->constructed(object);
224 }
225
226 static void
227 display_handle_global(
228     struct wl_display *display,
229     uint32_t           id,
230     const char        *interface,
231     uint32_t           version,
232     void              *data
233 )
234 {
235     GstVaapiDisplayWaylandPrivate * const priv = data;
236
237     if (strcmp(interface, "wl_compositor") == 0)
238         priv->compositor = wl_display_bind(display, id, &wl_compositor_interface);
239     else if (strcmp(interface, "wl_shell") == 0)
240         priv->shell = wl_display_bind(display, id, &wl_shell_interface);
241 }
242
243 static int
244 event_mask_update(uint32_t mask, void *data)
245 {
246     GstVaapiDisplayWaylandPrivate * const priv = data;
247
248     priv->event_mask = mask;
249     return 0;
250 }
251
252 static gboolean
253 gst_vaapi_display_wayland_open_display(GstVaapiDisplay * display)
254 {
255     GstVaapiDisplayWaylandPrivate * const priv =
256         GST_VAAPI_DISPLAY_WAYLAND(display)->priv;
257
258     if (!priv->create_display)
259         return priv->wl_display != NULL;
260
261     priv->wl_display = wl_display_connect(get_display_name(display));
262     if (!priv->wl_display)
263         return FALSE;
264
265     wl_display_set_user_data(priv->wl_display, priv);
266     wl_display_add_global_listener(priv->wl_display, display_handle_global, priv);
267     priv->event_fd = wl_display_get_fd(priv->wl_display, event_mask_update, priv);
268     wl_display_iterate(priv->wl_display, priv->event_mask);
269     wl_display_roundtrip(priv->wl_display);
270
271     if (!priv->compositor) {
272         GST_ERROR("failed to bind compositor interface");
273         return FALSE;
274     }
275
276     if (!priv->shell) {
277         GST_ERROR("failed to bind shell interface");
278         return FALSE;
279     }
280     return TRUE;
281 }
282
283 static void
284 gst_vaapi_display_wayland_close_display(GstVaapiDisplay * display)
285 {
286     GstVaapiDisplayWaylandPrivate * const priv =
287         GST_VAAPI_DISPLAY_WAYLAND(display)->priv;
288
289     if (priv->compositor) {
290         wl_compositor_destroy(priv->compositor);
291         priv->compositor = NULL;
292     }
293
294     if (priv->wl_display) {
295         if (priv->create_display)
296             wl_display_disconnect(priv->wl_display);
297         priv->wl_display = NULL;
298     }
299
300     if (priv->display_name) {
301         g_free(priv->display_name);
302         priv->display_name = NULL;
303     }
304 }
305
306 static gboolean
307 gst_vaapi_display_wayland_get_display_info(
308     GstVaapiDisplay     *display,
309     GstVaapiDisplayInfo *info
310 )
311 {
312     GstVaapiDisplayWaylandPrivate * const priv =
313         GST_VAAPI_DISPLAY_WAYLAND(display)->priv;
314     GstVaapiDisplayCache *cache;
315     const GstVaapiDisplayInfo *cached_info;
316
317     /* Return any cached info even if child has its own VA display */
318     cache = gst_vaapi_display_get_cache();
319     if (!cache)
320         return FALSE;
321     cached_info =
322         gst_vaapi_display_cache_lookup_by_native_display(cache, priv->wl_display);
323     if (cached_info) {
324         *info = *cached_info;
325         return TRUE;
326     }
327
328     /* Otherwise, create VA display if there is none already */
329     info->native_display = priv->wl_display;
330     info->display_name   = priv->display_name;
331     if (!info->va_display) {
332         info->va_display = vaGetDisplayWl(priv->wl_display);
333         if (!info->va_display)
334             return FALSE;
335         info->display_type = GST_VAAPI_DISPLAY_TYPE_WAYLAND;
336     }
337     return TRUE;
338 }
339
340 static void
341 gst_vaapi_display_wayland_class_init(GstVaapiDisplayWaylandClass * klass)
342 {
343     GObjectClass * const object_class = G_OBJECT_CLASS(klass);
344     GstVaapiDisplayClass * const dpy_class = GST_VAAPI_DISPLAY_CLASS(klass);
345
346     g_type_class_add_private(klass, sizeof(GstVaapiDisplayWaylandPrivate));
347
348     object_class->finalize      = gst_vaapi_display_wayland_finalize;
349     object_class->set_property  = gst_vaapi_display_wayland_set_property;
350     object_class->get_property  = gst_vaapi_display_wayland_get_property;
351     object_class->constructed   = gst_vaapi_display_wayland_constructed;
352
353     dpy_class->open_display     = gst_vaapi_display_wayland_open_display;
354     dpy_class->close_display    = gst_vaapi_display_wayland_close_display;
355     dpy_class->get_display      = gst_vaapi_display_wayland_get_display_info;
356
357     /**
358      * GstVaapiDisplayWayland:wayland-display:
359      *
360      * The Wayland #wl_display that was created by
361      * gst_vaapi_display_wayland_new() or that was bound from
362      * gst_vaapi_display_wayland_new_with_display().
363      */
364     g_object_class_install_property
365         (object_class,
366          PROP_WL_DISPLAY,
367          g_param_spec_pointer("wl-display",
368                               "Wayland display",
369                               "Wayland display",
370                               G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
371
372     /**
373      * GstVaapiDisplayWayland:display-name:
374      *
375      * The Wayland display name.
376      */
377     g_object_class_install_property
378         (object_class,
379          PROP_DISPLAY_NAME,
380          g_param_spec_string("display-name",
381                              "Wayland display name",
382                              "Wayland display name",
383                              NULL,
384                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
385 }
386
387 static void
388 gst_vaapi_display_wayland_init(GstVaapiDisplayWayland *display)
389 {
390     GstVaapiDisplayWaylandPrivate *priv =
391         GST_VAAPI_DISPLAY_WAYLAND_GET_PRIVATE(display);
392
393     display->priv        = priv;
394     priv->create_display = TRUE;
395     priv->display_name   = NULL;
396     priv->wl_display     = NULL;
397     priv->compositor     = NULL;
398     priv->shell          = NULL;
399     priv->event_fd       = -1;
400     priv->event_mask     = 0;
401 }
402
403 /**
404  * gst_vaapi_display_wayland_new:
405  * @display_name: the Wayland display name
406  *
407  * Opens an Wayland #wl_display using @display_name and returns a
408  * newly allocated #GstVaapiDisplay object. The Wayland display will
409  * be cloed when the reference count of the object reaches zero.
410  *
411  * Return value: a newly allocated #GstVaapiDisplay object
412  */
413 GstVaapiDisplay *
414 gst_vaapi_display_wayland_new(const gchar *display_name)
415 {
416     return g_object_new(GST_VAAPI_TYPE_DISPLAY_WAYLAND,
417                         "display-name", display_name,
418                         NULL);
419 }
420
421 /**
422  * gst_vaapi_display_wayland_new_with_display:
423  * @wl_display: an Wayland #wl_display
424  *
425  * Creates a #GstVaapiDisplay based on the Wayland @wl_display
426  * display. The caller still owns the display and must call
427  * wl_display_disconnect() when all #GstVaapiDisplay references are
428  * released. Doing so too early can yield undefined behaviour.
429  *
430  * Return value: a newly allocated #GstVaapiDisplay object
431  */
432 GstVaapiDisplay *
433 gst_vaapi_display_wayland_new_with_display(struct wl_display *wl_display)
434 {
435     g_return_val_if_fail(wl_display, NULL);
436
437     return g_object_new(GST_VAAPI_TYPE_DISPLAY_WAYLAND,
438                         "wl-display", wl_display,
439                         NULL);
440 }
441
442 /**
443  * gst_vaapi_display_wayland_get_display:
444  * @display: a #GstVaapiDisplayWayland
445  *
446  * Returns the underlying Wayland #wl_display that was created by
447  * gst_vaapi_display_wayland_new() or that was bound from
448  * gst_vaapi_display_wayland_new_with_display().
449  *
450  * Return value: the Wayland #wl_display attached to @display
451  */
452 struct wl_display *
453 gst_vaapi_display_wayland_get_display(GstVaapiDisplayWayland *display)
454 {
455     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY_WAYLAND(display), NULL);
456
457     return display->priv->wl_display;
458 }