Tizen 2.0 Release
[framework/multimedia/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 output_handle_geometry(void *data, struct wl_output *output,
228                        int x, int y, int physical_width, int physical_height,
229                        int subpixel, const char *make, const char *model,
230                        int transform)
231 {
232     GstVaapiDisplayWaylandPrivate * const priv = data;
233
234     priv->phys_width  = physical_width;
235     priv->phys_height = physical_height;
236 }
237
238 static void
239 output_handle_mode(void *data, struct wl_output *wl_output,
240                    uint32_t flags, int width, int height, int refresh)
241 {
242     GstVaapiDisplayWaylandPrivate * const priv = data;
243
244     if (flags & WL_OUTPUT_MODE_CURRENT) {
245         priv->width  = width;
246         priv->height = height;
247     }
248 }
249
250 static const struct wl_output_listener output_listener = {
251     output_handle_geometry,
252     output_handle_mode,
253 };
254
255 static void
256 display_handle_global(
257     struct wl_display *display,
258     uint32_t           id,
259     const char        *interface,
260     uint32_t           version,
261     void              *data
262 )
263 {
264     GstVaapiDisplayWaylandPrivate * const priv = data;
265
266     if (strcmp(interface, "wl_compositor") == 0)
267         priv->compositor = wl_display_bind(display, id, &wl_compositor_interface);
268     else if (strcmp(interface, "wl_shell") == 0)
269         priv->shell = wl_display_bind(display, id, &wl_shell_interface);
270     else if (strcmp(interface, "wl_output") == 0) {
271         priv->output = wl_display_bind(display, id, &wl_output_interface);
272         wl_output_add_listener(priv->output, &output_listener, priv);
273     }
274 }
275
276 static int
277 event_mask_update(uint32_t mask, void *data)
278 {
279     GstVaapiDisplayWaylandPrivate * const priv = data;
280
281     priv->event_mask = mask;
282     return 0;
283 }
284
285 static gboolean
286 gst_vaapi_display_wayland_open_display(GstVaapiDisplay * display)
287 {
288     GstVaapiDisplayWaylandPrivate * const priv =
289         GST_VAAPI_DISPLAY_WAYLAND(display)->priv;
290
291     if (!priv->create_display)
292         return priv->wl_display != NULL;
293
294     priv->wl_display = wl_display_connect(get_display_name(display));
295     if (!priv->wl_display)
296         return FALSE;
297
298     wl_display_set_user_data(priv->wl_display, priv);
299     wl_display_add_global_listener(priv->wl_display, display_handle_global, priv);
300     priv->event_fd = wl_display_get_fd(priv->wl_display, event_mask_update, priv);
301     wl_display_iterate(priv->wl_display, priv->event_mask);
302     wl_display_roundtrip(priv->wl_display);
303
304     if (!priv->compositor) {
305         GST_ERROR("failed to bind compositor interface");
306         return FALSE;
307     }
308
309     if (!priv->shell) {
310         GST_ERROR("failed to bind shell interface");
311         return FALSE;
312     }
313     return TRUE;
314 }
315
316 static void
317 gst_vaapi_display_wayland_close_display(GstVaapiDisplay * display)
318 {
319     GstVaapiDisplayWaylandPrivate * const priv =
320         GST_VAAPI_DISPLAY_WAYLAND(display)->priv;
321
322     if (priv->compositor) {
323         wl_compositor_destroy(priv->compositor);
324         priv->compositor = NULL;
325     }
326
327     if (priv->wl_display) {
328         if (priv->create_display)
329             wl_display_disconnect(priv->wl_display);
330         priv->wl_display = NULL;
331     }
332
333     if (priv->display_name) {
334         g_free(priv->display_name);
335         priv->display_name = NULL;
336     }
337 }
338
339 static gboolean
340 gst_vaapi_display_wayland_get_display_info(
341     GstVaapiDisplay     *display,
342     GstVaapiDisplayInfo *info
343 )
344 {
345     GstVaapiDisplayWaylandPrivate * const priv =
346         GST_VAAPI_DISPLAY_WAYLAND(display)->priv;
347     GstVaapiDisplayCache *cache;
348     const GstVaapiDisplayInfo *cached_info;
349
350     /* Return any cached info even if child has its own VA display */
351     cache = gst_vaapi_display_get_cache();
352     if (!cache)
353         return FALSE;
354     cached_info =
355         gst_vaapi_display_cache_lookup_by_native_display(cache, priv->wl_display);
356     if (cached_info) {
357         *info = *cached_info;
358         return TRUE;
359     }
360
361     /* Otherwise, create VA display if there is none already */
362     info->native_display = priv->wl_display;
363     info->display_name   = priv->display_name;
364     if (!info->va_display) {
365         info->va_display = vaGetDisplayWl(priv->wl_display);
366         if (!info->va_display)
367             return FALSE;
368         info->display_type = GST_VAAPI_DISPLAY_TYPE_WAYLAND;
369     }
370     return TRUE;
371 }
372
373 static void
374 gst_vaapi_display_wayland_get_size(
375     GstVaapiDisplay *display,
376     guint           *pwidth,
377     guint           *pheight
378 )
379 {
380     GstVaapiDisplayWaylandPrivate * const priv =
381         GST_VAAPI_DISPLAY_WAYLAND(display)->priv;
382
383     if (!priv->output)
384         return;
385
386     if (pwidth)
387         *pwidth = priv->width;
388
389     if (pheight)
390         *pheight = priv->height;
391 }
392
393 static void
394 gst_vaapi_display_wayland_get_size_mm(
395     GstVaapiDisplay *display,
396     guint           *pwidth,
397     guint           *pheight
398 )
399 {
400     GstVaapiDisplayWaylandPrivate * const priv =
401         GST_VAAPI_DISPLAY_WAYLAND(display)->priv;
402
403     if (!priv->output)
404         return;
405
406     if (pwidth)
407         *pwidth = priv->phys_width;
408
409     if (pheight)
410         *pheight = priv->phys_height;
411 }
412
413 static void
414 gst_vaapi_display_wayland_class_init(GstVaapiDisplayWaylandClass * klass)
415 {
416     GObjectClass * const object_class = G_OBJECT_CLASS(klass);
417     GstVaapiDisplayClass * const dpy_class = GST_VAAPI_DISPLAY_CLASS(klass);
418
419     g_type_class_add_private(klass, sizeof(GstVaapiDisplayWaylandPrivate));
420
421     object_class->finalize      = gst_vaapi_display_wayland_finalize;
422     object_class->set_property  = gst_vaapi_display_wayland_set_property;
423     object_class->get_property  = gst_vaapi_display_wayland_get_property;
424     object_class->constructed   = gst_vaapi_display_wayland_constructed;
425
426     dpy_class->open_display     = gst_vaapi_display_wayland_open_display;
427     dpy_class->close_display    = gst_vaapi_display_wayland_close_display;
428     dpy_class->get_display      = gst_vaapi_display_wayland_get_display_info;
429     dpy_class->get_size         = gst_vaapi_display_wayland_get_size;
430     dpy_class->get_size_mm      = gst_vaapi_display_wayland_get_size_mm;
431
432     /**
433      * GstVaapiDisplayWayland:wayland-display:
434      *
435      * The Wayland #wl_display that was created by
436      * gst_vaapi_display_wayland_new() or that was bound from
437      * gst_vaapi_display_wayland_new_with_display().
438      */
439     g_object_class_install_property
440         (object_class,
441          PROP_WL_DISPLAY,
442          g_param_spec_pointer("wl-display",
443                               "Wayland display",
444                               "Wayland display",
445                               G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
446
447     /**
448      * GstVaapiDisplayWayland:display-name:
449      *
450      * The Wayland display name.
451      */
452     g_object_class_install_property
453         (object_class,
454          PROP_DISPLAY_NAME,
455          g_param_spec_string("display-name",
456                              "Wayland display name",
457                              "Wayland display name",
458                              NULL,
459                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
460 }
461
462 static void
463 gst_vaapi_display_wayland_init(GstVaapiDisplayWayland *display)
464 {
465     GstVaapiDisplayWaylandPrivate * const priv =
466         GST_VAAPI_DISPLAY_WAYLAND_GET_PRIVATE(display);
467
468     display->priv        = priv;
469     priv->create_display = TRUE;
470     priv->display_name   = NULL;
471     priv->wl_display     = NULL;
472     priv->compositor     = NULL;
473     priv->shell          = NULL;
474     priv->output         = NULL;
475     priv->width          = 0;
476     priv->height         = 0;
477     priv->phys_width     = 0;
478     priv->phys_height    = 0;
479     priv->event_fd       = -1;
480     priv->event_mask     = 0;
481 }
482
483 /**
484  * gst_vaapi_display_wayland_new:
485  * @display_name: the Wayland display name
486  *
487  * Opens an Wayland #wl_display using @display_name and returns a
488  * newly allocated #GstVaapiDisplay object. The Wayland display will
489  * be cloed when the reference count of the object reaches zero.
490  *
491  * Return value: a newly allocated #GstVaapiDisplay object
492  */
493 GstVaapiDisplay *
494 gst_vaapi_display_wayland_new(const gchar *display_name)
495 {
496     return g_object_new(GST_VAAPI_TYPE_DISPLAY_WAYLAND,
497                         "display-name", display_name,
498                         NULL);
499 }
500
501 /**
502  * gst_vaapi_display_wayland_new_with_display:
503  * @wl_display: an Wayland #wl_display
504  *
505  * Creates a #GstVaapiDisplay based on the Wayland @wl_display
506  * display. The caller still owns the display and must call
507  * wl_display_disconnect() when all #GstVaapiDisplay references are
508  * released. Doing so too early can yield undefined behaviour.
509  *
510  * Return value: a newly allocated #GstVaapiDisplay object
511  */
512 GstVaapiDisplay *
513 gst_vaapi_display_wayland_new_with_display(struct wl_display *wl_display)
514 {
515     g_return_val_if_fail(wl_display, NULL);
516
517     return g_object_new(GST_VAAPI_TYPE_DISPLAY_WAYLAND,
518                         "wl-display", wl_display,
519                         NULL);
520 }
521
522 /**
523  * gst_vaapi_display_wayland_get_display:
524  * @display: a #GstVaapiDisplayWayland
525  *
526  * Returns the underlying Wayland #wl_display that was created by
527  * gst_vaapi_display_wayland_new() or that was bound from
528  * gst_vaapi_display_wayland_new_with_display().
529  *
530  * Return value: the Wayland #wl_display attached to @display
531  */
532 struct wl_display *
533 gst_vaapi_display_wayland_get_display(GstVaapiDisplayWayland *display)
534 {
535     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY_WAYLAND(display), NULL);
536
537     return display->priv->wl_display;
538 }