2 * gstvaapiwindow_x11.c - VA/X11 window abstraction
4 * Copyright (C) 2010-2011 Splitted-Desktop Systems
5 * Author: Gwenole Beauchesne <gwenole.beauchesne@splitted-desktop.com>
6 * Copyright (C) 2012-2014 Intel Corporation
7 * Author: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free
21 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301 USA
26 * SECTION:gstvaapiwindow_x11
27 * @short_description: VA/X11 window abstraction
32 #include <X11/Xatom.h>
33 #include "gstvaapicompat.h"
34 #include "gstvaapiwindow_x11.h"
35 #include "gstvaapiwindow_x11_priv.h"
36 #include "gstvaapipixmap_x11.h"
37 #include "gstvaapipixmap_priv.h"
38 #include "gstvaapidisplay_x11.h"
39 #include "gstvaapidisplay_x11_priv.h"
40 #include "gstvaapiutils.h"
41 #include "gstvaapiutils_x11.h"
44 #include "gstvaapidebug.h"
46 #define _NET_WM_STATE_REMOVE 0 /* remove/unset property */
47 #define _NET_WM_STATE_ADD 1 /* add/set property */
48 #define _NET_WM_STATE_TOGGLE 2 /* toggle property */
51 send_wmspec_change_state (GstVaapiWindow * window, Atom state, gboolean add)
53 GstVaapiWindowX11Private *const priv =
54 GST_VAAPI_WINDOW_X11_GET_PRIVATE (window);
55 Display *const dpy = GST_VAAPI_OBJECT_XDISPLAY (window);
56 XClientMessageEvent xclient;
58 memset (&xclient, 0, sizeof (xclient));
60 xclient.type = ClientMessage;
61 xclient.window = GST_VAAPI_OBJECT_ID (window);
62 xclient.message_type = priv->atom_NET_WM_STATE;
65 xclient.data.l[0] = add ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
66 xclient.data.l[1] = state;
67 xclient.data.l[2] = 0;
68 xclient.data.l[3] = 0;
69 xclient.data.l[4] = 0;
72 DefaultRootWindow (dpy),
74 SubstructureRedirectMask | SubstructureNotifyMask, (XEvent *) & xclient);
78 wait_event (GstVaapiWindow * window, int type)
80 Display *const dpy = GST_VAAPI_OBJECT_XDISPLAY (window);
81 const Window xid = GST_VAAPI_OBJECT_ID (window);
86 GST_VAAPI_OBJECT_LOCK_DISPLAY (window);
87 got_event = XCheckTypedWindowEvent (dpy, xid, type, &e);
88 GST_VAAPI_OBJECT_UNLOCK_DISPLAY (window);
96 timed_wait_event (GstVaapiWindow * window, int type, guint64 end_time,
99 Display *const dpy = GST_VAAPI_OBJECT_XDISPLAY (window);
100 const Window xid = GST_VAAPI_OBJECT_ID (window);
109 GST_VAAPI_OBJECT_LOCK_DISPLAY (window);
110 got_event = XCheckTypedWindowEvent (dpy, xid, type, e);
111 GST_VAAPI_OBJECT_UNLOCK_DISPLAY (window);
117 GST_VAAPI_OBJECT_LOCK_DISPLAY (window);
118 got_event = XCheckTypedWindowEvent (dpy, xid, type, e);
119 GST_VAAPI_OBJECT_UNLOCK_DISPLAY (window);
122 g_get_current_time (&now);
123 now_time = (guint64) now.tv_sec * 1000000 + now.tv_usec;
124 } while (now_time < end_time);
129 gst_vaapi_window_x11_show (GstVaapiWindow * window)
131 GstVaapiWindowX11Private *const priv =
132 GST_VAAPI_WINDOW_X11_GET_PRIVATE (window);
133 Display *const dpy = GST_VAAPI_OBJECT_XDISPLAY (window);
134 const Window xid = GST_VAAPI_OBJECT_ID (window);
135 XWindowAttributes wattr;
141 GST_VAAPI_OBJECT_LOCK_DISPLAY (window);
143 if (window->use_foreign_window) {
144 XGetWindowAttributes (dpy, xid, &wattr);
145 if (!(wattr.your_event_mask & StructureNotifyMask))
146 XSelectInput (dpy, xid, StructureNotifyMask);
148 XMapWindow (dpy, xid);
149 has_errors = x11_untrap_errors () != 0;
150 GST_VAAPI_OBJECT_UNLOCK_DISPLAY (window);
153 wait_event (window, MapNotify);
154 if (window->use_foreign_window &&
155 !(wattr.your_event_mask & StructureNotifyMask)) {
156 GST_VAAPI_OBJECT_LOCK_DISPLAY (window);
158 XSelectInput (dpy, xid, wattr.your_event_mask);
159 has_errors = x11_untrap_errors () != 0;
160 GST_VAAPI_OBJECT_UNLOCK_DISPLAY (window);
162 priv->is_mapped = TRUE;
164 if (priv->fullscreen_on_map)
165 gst_vaapi_window_set_fullscreen (window, TRUE);
171 gst_vaapi_window_x11_hide (GstVaapiWindow * window)
173 GstVaapiWindowX11Private *const priv =
174 GST_VAAPI_WINDOW_X11_GET_PRIVATE (window);
175 Display *const dpy = GST_VAAPI_OBJECT_XDISPLAY (window);
176 const Window xid = GST_VAAPI_OBJECT_ID (window);
177 XWindowAttributes wattr;
180 if (!priv->is_mapped)
183 GST_VAAPI_OBJECT_LOCK_DISPLAY (window);
185 if (window->use_foreign_window) {
186 XGetWindowAttributes (dpy, xid, &wattr);
187 if (!(wattr.your_event_mask & StructureNotifyMask))
188 XSelectInput (dpy, xid, StructureNotifyMask);
190 XUnmapWindow (dpy, xid);
191 has_errors = x11_untrap_errors () != 0;
192 GST_VAAPI_OBJECT_UNLOCK_DISPLAY (window);
195 wait_event (window, UnmapNotify);
196 if (window->use_foreign_window &&
197 !(wattr.your_event_mask & StructureNotifyMask)) {
198 GST_VAAPI_OBJECT_LOCK_DISPLAY (window);
200 XSelectInput (dpy, xid, wattr.your_event_mask);
201 has_errors = x11_untrap_errors () != 0;
202 GST_VAAPI_OBJECT_UNLOCK_DISPLAY (window);
204 priv->is_mapped = FALSE;
210 gst_vaapi_window_x11_create (GstVaapiWindow * window, guint * width,
213 GstVaapiWindowX11Private *const priv =
214 GST_VAAPI_WINDOW_X11_GET_PRIVATE (window);
215 Display *const dpy = GST_VAAPI_OBJECT_XDISPLAY (window);
216 Window xid = GST_VAAPI_OBJECT_ID (window);
218 Colormap cmap = None;
219 const GstVaapiWindowX11Class *klass;
220 XWindowAttributes wattr;
224 static const char *atom_names[2] = {
226 "_NET_WM_STATE_FULLSCREEN",
230 GST_VAAPI_DISPLAY_HAS_XRENDER (GST_VAAPI_OBJECT_DISPLAY (window));
232 if (window->use_foreign_window && xid) {
233 GST_VAAPI_OBJECT_LOCK_DISPLAY (window);
234 XGetWindowAttributes (dpy, xid, &wattr);
235 priv->is_mapped = wattr.map_state == IsViewable;
236 ok = x11_get_geometry (dpy, xid, NULL, NULL, width, height, NULL);
237 GST_VAAPI_OBJECT_UNLOCK_DISPLAY (window);
241 klass = GST_VAAPI_WINDOW_X11_GET_CLASS (window);
243 if (klass->get_visual)
244 vis = klass->get_visual (window);
245 if (klass->get_colormap)
246 cmap = klass->get_colormap (window);
249 GST_VAAPI_OBJECT_LOCK_DISPLAY (window);
251 (char **) atom_names, G_N_ELEMENTS (atom_names), False, atoms);
252 priv->atom_NET_WM_STATE = atoms[0];
253 priv->atom_NET_WM_STATE_FULLSCREEN = atoms[1];
255 xid = x11_create_window (dpy, *width, *height, vis, cmap);
257 XRaiseWindow (dpy, xid);
258 GST_VAAPI_OBJECT_UNLOCK_DISPLAY (window);
260 GST_DEBUG ("xid %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS (xid));
261 GST_VAAPI_OBJECT_ID (window) = xid;
266 gst_vaapi_window_x11_destroy (GstVaapiWindow * window)
268 Display *const dpy = GST_VAAPI_OBJECT_XDISPLAY (window);
269 const Window xid = GST_VAAPI_OBJECT_ID (window);
272 GstVaapiWindowX11Private *const priv =
273 GST_VAAPI_WINDOW_X11_GET_PRIVATE (window);
275 GST_VAAPI_OBJECT_LOCK_DISPLAY (window);
276 XRenderFreePicture (dpy, priv->picture);
277 GST_VAAPI_OBJECT_UNLOCK_DISPLAY (window);
278 priv->picture = None;
283 if (!window->use_foreign_window) {
284 GST_VAAPI_OBJECT_LOCK_DISPLAY (window);
285 XDestroyWindow (dpy, xid);
286 GST_VAAPI_OBJECT_UNLOCK_DISPLAY (window);
288 GST_VAAPI_OBJECT_ID (window) = None;
293 gst_vaapi_window_x11_get_geometry (GstVaapiWindow * window,
294 gint * px, gint * py, guint * pwidth, guint * pheight)
296 Display *const dpy = GST_VAAPI_OBJECT_XDISPLAY (window);
297 const Window xid = GST_VAAPI_OBJECT_ID (window);
299 return x11_get_geometry (dpy, xid, px, py, pwidth, pheight, NULL);
303 gst_vaapi_window_x11_set_fullscreen (GstVaapiWindow * window,
306 GstVaapiWindowX11Private *const priv =
307 GST_VAAPI_WINDOW_X11_GET_PRIVATE (window);
308 Display *const dpy = GST_VAAPI_OBJECT_XDISPLAY (window);
309 const Window xid = GST_VAAPI_OBJECT_ID (window);
316 GST_VAAPI_OBJECT_LOCK_DISPLAY (window);
319 if (!priv->is_mapped) {
320 priv->fullscreen_on_map = TRUE;
322 XChangeProperty (dpy,
324 priv->atom_NET_WM_STATE, XA_ATOM, 32,
326 (unsigned char *) &priv->atom_NET_WM_STATE_FULLSCREEN, 1);
328 send_wmspec_change_state (window,
329 priv->atom_NET_WM_STATE_FULLSCREEN, TRUE);
332 if (!priv->is_mapped) {
333 priv->fullscreen_on_map = FALSE;
335 XDeleteProperty (dpy, xid, priv->atom_NET_WM_STATE);
337 send_wmspec_change_state (window,
338 priv->atom_NET_WM_STATE_FULLSCREEN, FALSE);
342 has_errors = x11_untrap_errors () != 0;
343 GST_VAAPI_OBJECT_UNLOCK_DISPLAY (window);
347 /* Try to wait for the completion of the fullscreen mode switch */
348 if (!window->use_foreign_window && priv->is_mapped) {
349 const guint DELAY = 100000; /* 100 ms */
350 g_get_current_time (&now);
351 end_time = DELAY + ((guint64) now.tv_sec * 1000000 + now.tv_usec);
352 while (timed_wait_event (window, ConfigureNotify, end_time, &e)) {
354 gst_vaapi_display_get_size (GST_VAAPI_OBJECT_DISPLAY (window),
356 if (e.xconfigure.width == width && e.xconfigure.height == height)
359 gst_vaapi_window_get_size (window, &width, &height);
360 if (e.xconfigure.width != width || e.xconfigure.height != height)
369 gst_vaapi_window_x11_resize (GstVaapiWindow * window, guint width, guint height)
373 if (!GST_VAAPI_OBJECT_ID (window))
376 GST_VAAPI_OBJECT_LOCK_DISPLAY (window);
378 XResizeWindow (GST_VAAPI_OBJECT_XDISPLAY (window),
379 GST_VAAPI_OBJECT_ID (window), width, height);
380 has_errors = x11_untrap_errors () != 0;
381 GST_VAAPI_OBJECT_UNLOCK_DISPLAY (window);
386 gst_vaapi_window_x11_render (GstVaapiWindow * window,
387 GstVaapiSurface * surface,
388 const GstVaapiRectangle * src_rect,
389 const GstVaapiRectangle * dst_rect, guint flags)
391 VASurfaceID surface_id;
394 surface_id = GST_VAAPI_OBJECT_ID (surface);
395 if (surface_id == VA_INVALID_ID)
398 GST_VAAPI_OBJECT_LOCK_DISPLAY (window);
399 status = vaPutSurface (GST_VAAPI_OBJECT_VADISPLAY (window),
401 GST_VAAPI_OBJECT_ID (window),
409 dst_rect->height, NULL, 0, from_GstVaapiSurfaceRenderFlags (flags)
411 GST_VAAPI_OBJECT_UNLOCK_DISPLAY (window);
412 if (!vaapi_check_status (status, "vaPutSurface()"))
419 gst_vaapi_window_x11_render_pixmap_xrender (GstVaapiWindow * window,
420 GstVaapiPixmap * pixmap,
421 const GstVaapiRectangle * src_rect, const GstVaapiRectangle * dst_rect)
424 GstVaapiWindowX11Private *const priv =
425 GST_VAAPI_WINDOW_X11_GET_PRIVATE (window);
426 Display *const dpy = GST_VAAPI_OBJECT_XDISPLAY (window);
427 const Window win = GST_VAAPI_OBJECT_ID (window);
428 const Pixmap pix = GST_VAAPI_OBJECT_ID (pixmap);
430 XRenderPictFormat *pic_fmt;
431 XWindowAttributes wattr;
433 gboolean success = FALSE;
435 /* Ensure Picture for window is created */
436 if (!priv->picture) {
437 GST_VAAPI_OBJECT_LOCK_DISPLAY (window);
438 XGetWindowAttributes (dpy, win, &wattr);
439 pic_fmt = XRenderFindVisualFormat (dpy, wattr.visual);
441 priv->picture = XRenderCreatePicture (dpy, win, pic_fmt, 0, NULL);
442 GST_VAAPI_OBJECT_UNLOCK_DISPLAY (window);
447 /* Check pixmap format */
448 switch (GST_VAAPI_PIXMAP_FORMAT (pixmap)) {
449 case GST_VIDEO_FORMAT_xRGB:
450 fmt = PictStandardRGB24;
453 case GST_VIDEO_FORMAT_ARGB:
454 fmt = PictStandardARGB32;
457 GST_VAAPI_OBJECT_LOCK_DISPLAY (window);
458 pic_fmt = XRenderFindStandardFormat (dpy, fmt);
459 GST_VAAPI_OBJECT_UNLOCK_DISPLAY (window);
468 GST_VAAPI_OBJECT_LOCK_DISPLAY (window);
470 const double sx = (double) src_rect->width / dst_rect->width;
471 const double sy = (double) src_rect->height / dst_rect->height;
474 picture = XRenderCreatePicture (dpy, pix, pic_fmt, 0, NULL);
478 xform.matrix[0][0] = XDoubleToFixed (sx);
479 xform.matrix[0][1] = XDoubleToFixed (0.0);
480 xform.matrix[0][2] = XDoubleToFixed (src_rect->x);
481 xform.matrix[1][0] = XDoubleToFixed (0.0);
482 xform.matrix[1][1] = XDoubleToFixed (sy);
483 xform.matrix[1][2] = XDoubleToFixed (src_rect->y);
484 xform.matrix[2][0] = XDoubleToFixed (0.0);
485 xform.matrix[2][1] = XDoubleToFixed (0.0);
486 xform.matrix[2][2] = XDoubleToFixed (1.0);
487 XRenderSetPictureTransform (dpy, picture, &xform);
489 XRenderComposite (dpy, op, picture, None, priv->picture,
490 0, 0, 0, 0, dst_rect->x, dst_rect->y,
491 dst_rect->width, dst_rect->height);
496 XRenderFreePicture (dpy, picture);
497 GST_VAAPI_OBJECT_UNLOCK_DISPLAY (window);
504 gst_vaapi_window_x11_render_pixmap (GstVaapiWindow * window,
505 GstVaapiPixmap * pixmap,
506 const GstVaapiRectangle * src_rect, const GstVaapiRectangle * dst_rect)
508 GstVaapiWindowX11Private *const priv =
509 GST_VAAPI_WINDOW_X11_GET_PRIVATE (window);
511 if (priv->has_xrender)
512 return gst_vaapi_window_x11_render_pixmap_xrender (window, pixmap,
515 /* XXX: only X RENDER extension is supported for now */
520 gst_vaapi_window_x11_class_init (GstVaapiWindowX11Class * klass)
522 GstVaapiObjectClass *const object_class = GST_VAAPI_OBJECT_CLASS (klass);
523 GstVaapiWindowClass *const window_class = GST_VAAPI_WINDOW_CLASS (klass);
525 object_class->finalize = (GstVaapiObjectFinalizeFunc)
526 gst_vaapi_window_x11_destroy;
528 window_class->create = gst_vaapi_window_x11_create;
529 window_class->show = gst_vaapi_window_x11_show;
530 window_class->hide = gst_vaapi_window_x11_hide;
531 window_class->get_geometry = gst_vaapi_window_x11_get_geometry;
532 window_class->set_fullscreen = gst_vaapi_window_x11_set_fullscreen;
533 window_class->resize = gst_vaapi_window_x11_resize;
534 window_class->render = gst_vaapi_window_x11_render;
535 window_class->render_pixmap = gst_vaapi_window_x11_render_pixmap;
538 #define gst_vaapi_window_x11_finalize \
539 gst_vaapi_window_x11_destroy
541 GST_VAAPI_OBJECT_DEFINE_CLASS_WITH_CODE (GstVaapiWindowX11,
542 gst_vaapi_window_x11, gst_vaapi_window_x11_class_init (&g_class));
545 * gst_vaapi_window_x11_new:
546 * @display: a #GstVaapiDisplay
547 * @width: the requested window width, in pixels
548 * @height: the requested windo height, in pixels
550 * Creates a window with the specified @width and @height. The window
551 * will be attached to the @display and remains invisible to the user
552 * until gst_vaapi_window_show() is called.
554 * Return value: the newly allocated #GstVaapiWindow object
557 gst_vaapi_window_x11_new (GstVaapiDisplay * display, guint width, guint height)
559 GST_DEBUG ("new window, size %ux%u", width, height);
561 g_return_val_if_fail (GST_VAAPI_IS_DISPLAY_X11 (display), NULL);
564 gst_vaapi_window_new (GST_VAAPI_WINDOW_CLASS (gst_vaapi_window_x11_class
565 ()), display, width, height);
569 * gst_vaapi_window_x11_new_with_xid:
570 * @display: a #GstVaapiDisplay
571 * @xid: an X11 #Window id
573 * Creates a #GstVaapiWindow using the X11 #Window @xid. The caller
574 * still owns the window and must call XDestroyWindow() when all
575 * #GstVaapiWindow references are released. Doing so too early can
576 * yield undefined behaviour.
578 * Return value: the newly allocated #GstVaapiWindow object
581 gst_vaapi_window_x11_new_with_xid (GstVaapiDisplay * display, Window xid)
583 GST_DEBUG ("new window from xid 0x%08x", (guint) xid);
585 g_return_val_if_fail (GST_VAAPI_IS_DISPLAY_X11 (display), NULL);
586 g_return_val_if_fail (xid != None, NULL);
589 gst_vaapi_window_new_from_native (GST_VAAPI_WINDOW_CLASS
590 (gst_vaapi_window_x11_class ()), display, GINT_TO_POINTER (xid));
594 * gst_vaapi_window_x11_get_xid:
595 * @window: a #GstVaapiWindowX11
597 * Returns the underlying X11 #Window that was created by
598 * gst_vaapi_window_x11_new() or that was bound with
599 * gst_vaapi_window_x11_new_with_xid().
601 * Return value: the underlying X11 #Window bound to @window.
604 gst_vaapi_window_x11_get_xid (GstVaapiWindowX11 * window)
606 g_return_val_if_fail (window != NULL, None);
608 return GST_VAAPI_OBJECT_ID (window);
612 * gst_vaapi_window_x11_is_foreign_xid:
613 * @window: a #GstVaapiWindowX11
615 * Checks whether the @window XID was created by gst_vaapi_window_x11_new() or bound with gst_vaapi_window_x11_new_with_xid().
617 * Return value: %TRUE if the underlying X window is owned by the
618 * caller (foreign window)
621 gst_vaapi_window_x11_is_foreign_xid (GstVaapiWindowX11 * window)
623 g_return_val_if_fail (window != NULL, FALSE);
625 return GST_VAAPI_WINDOW (window)->use_foreign_window;