2 * gstvaapiwindow_x11.c - VA/X11 window abstraction
4 * Copyright (C) 2010-2011 Splitted-Desktop Systems
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.
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.
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
23 * SECTION:gstvaapiwindow_x11
24 * @short_description: VA/X11 window abstraction
29 #include <X11/Xatom.h>
30 #include "gstvaapicompat.h"
31 #include "gstvaapiwindow_x11.h"
32 #include "gstvaapidisplay_x11.h"
33 #include "gstvaapidisplay_x11_priv.h"
34 #include "gstvaapiutils.h"
35 #include "gstvaapiutils_x11.h"
36 #include "gstvaapi_priv.h"
39 #include "gstvaapidebug.h"
41 G_DEFINE_TYPE(GstVaapiWindowX11, gst_vaapi_window_x11, GST_VAAPI_TYPE_WINDOW);
43 #define GST_VAAPI_WINDOW_X11_GET_PRIVATE(obj) \
44 (G_TYPE_INSTANCE_GET_PRIVATE((obj), \
45 GST_VAAPI_TYPE_WINDOW_X11, \
46 GstVaapiWindowX11Private))
48 struct _GstVaapiWindowX11Private {
49 Atom atom_NET_WM_STATE;
50 Atom atom_NET_WM_STATE_FULLSCREEN;
51 guint create_window : 1;
53 guint fullscreen_on_map : 1;
57 * is_pixmap will be used by _create(), but _new_with_xid() doesn't have
58 * a chance to set the flag, so add this flag here.
59 * TODO, we'd better add is_pixmap flag to base class: GstVaapiWindow
62 static gboolean _is_pixmap = FALSE;
64 #define _NET_WM_STATE_REMOVE 0 /* remove/unset property */
65 #define _NET_WM_STATE_ADD 1 /* add/set property */
66 #define _NET_WM_STATE_TOGGLE 2 /* toggle property */
69 send_wmspec_change_state(GstVaapiWindowX11 *window, Atom state, gboolean add)
71 GstVaapiWindowX11Private * const priv = window->priv;
72 Display * const dpy = GST_VAAPI_OBJECT_XDISPLAY(window);
73 XClientMessageEvent xclient;
75 memset(&xclient, 0, sizeof(xclient));
77 xclient.type = ClientMessage;
78 xclient.window = GST_VAAPI_OBJECT_ID(window);
79 xclient.message_type = priv->atom_NET_WM_STATE;
82 xclient.data.l[0] = add ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
83 xclient.data.l[1] = state;
84 xclient.data.l[2] = 0;
85 xclient.data.l[3] = 0;
86 xclient.data.l[4] = 0;
90 DefaultRootWindow(dpy),
92 SubstructureRedirectMask|SubstructureNotifyMask,
98 wait_event(GstVaapiWindow *window, int type)
100 Display * const dpy = GST_VAAPI_OBJECT_XDISPLAY(window);
101 const Window xid = GST_VAAPI_OBJECT_ID(window);
106 GST_VAAPI_OBJECT_LOCK_DISPLAY(window);
107 got_event = XCheckTypedWindowEvent(dpy, xid, type, &e);
108 GST_VAAPI_OBJECT_UNLOCK_DISPLAY(window);
116 timed_wait_event(GstVaapiWindow *window, int type, guint64 end_time, XEvent *e)
118 Display * const dpy = GST_VAAPI_OBJECT_XDISPLAY(window);
119 const Window xid = GST_VAAPI_OBJECT_ID(window);
128 GST_VAAPI_OBJECT_LOCK_DISPLAY(window);
129 got_event = XCheckTypedWindowEvent(dpy, xid, type, e);
130 GST_VAAPI_OBJECT_UNLOCK_DISPLAY(window);
136 GST_VAAPI_OBJECT_LOCK_DISPLAY(window);
137 got_event = XCheckTypedWindowEvent(dpy, xid, type, e);
138 GST_VAAPI_OBJECT_UNLOCK_DISPLAY(window);
141 g_get_current_time(&now);
142 now_time = (guint64)now.tv_sec * 1000000 + now.tv_usec;
143 } while (now_time < end_time);
148 gst_vaapi_window_x11_show(GstVaapiWindow *window)
150 GstVaapiWindowX11Private * const priv = GST_VAAPI_WINDOW_X11(window)->priv;
151 Display * const dpy = GST_VAAPI_OBJECT_XDISPLAY(window);
152 const Window xid = GST_VAAPI_OBJECT_ID(window);
153 XWindowAttributes wattr;
159 GST_VAAPI_OBJECT_LOCK_DISPLAY(window);
161 if (!priv->create_window) {
162 XGetWindowAttributes(dpy, xid, &wattr);
163 if (!(wattr.your_event_mask & StructureNotifyMask))
164 XSelectInput(dpy, xid, StructureNotifyMask);
166 XMapWindow(dpy, xid);
167 has_errors = x11_untrap_errors() != 0;
168 GST_VAAPI_OBJECT_UNLOCK_DISPLAY(window);
171 wait_event(window, MapNotify);
172 if (!priv->create_window &&
173 !(wattr.your_event_mask & StructureNotifyMask)) {
174 GST_VAAPI_OBJECT_LOCK_DISPLAY(window);
176 XSelectInput(dpy, xid, wattr.your_event_mask);
177 has_errors = x11_untrap_errors() != 0;
178 GST_VAAPI_OBJECT_UNLOCK_DISPLAY(window);
180 priv->is_mapped = TRUE;
182 if (priv->fullscreen_on_map)
183 gst_vaapi_window_set_fullscreen(window, TRUE);
189 gst_vaapi_window_x11_hide(GstVaapiWindow *window)
191 GstVaapiWindowX11Private * const priv = GST_VAAPI_WINDOW_X11(window)->priv;
192 Display * const dpy = GST_VAAPI_OBJECT_XDISPLAY(window);
193 const Window xid = GST_VAAPI_OBJECT_ID(window);
194 XWindowAttributes wattr;
197 if (!priv->is_mapped)
200 GST_VAAPI_OBJECT_LOCK_DISPLAY(window);
202 if (!priv->create_window) {
203 XGetWindowAttributes(dpy, xid, &wattr);
204 if (!(wattr.your_event_mask & StructureNotifyMask))
205 XSelectInput(dpy, xid, StructureNotifyMask);
207 XUnmapWindow(dpy, xid);
208 has_errors = x11_untrap_errors() != 0;
209 GST_VAAPI_OBJECT_UNLOCK_DISPLAY(window);
212 wait_event(window, UnmapNotify);
213 if (!priv->create_window &&
214 !(wattr.your_event_mask & StructureNotifyMask)) {
215 GST_VAAPI_OBJECT_LOCK_DISPLAY(window);
217 XSelectInput(dpy, xid, wattr.your_event_mask);
218 has_errors = x11_untrap_errors() != 0;
219 GST_VAAPI_OBJECT_UNLOCK_DISPLAY(window);
221 priv->is_mapped = FALSE;
227 gst_vaapi_window_x11_create(GstVaapiWindow *window, guint *width, guint *height)
229 GstVaapiWindowX11Private * const priv = GST_VAAPI_WINDOW_X11(window)->priv;
230 Display * const dpy = GST_VAAPI_OBJECT_XDISPLAY(window);
231 Window xid = GST_VAAPI_OBJECT_ID(window);
233 Colormap cmap = None;
234 GstVaapiWindowX11Class *klass;
235 XWindowAttributes wattr;
239 static const char *atom_names[2] = {
241 "_NET_WM_STATE_FULLSCREEN",
244 if (!priv->create_window && xid) {
245 GST_VAAPI_OBJECT_LOCK_DISPLAY(window);
247 priv->is_mapped = TRUE;
250 XGetWindowAttributes(dpy, xid, &wattr);
251 priv->is_mapped = wattr.map_state == IsViewable;
253 ok = x11_get_geometry(dpy, xid, NULL, NULL, width, height);
254 GST_VAAPI_OBJECT_UNLOCK_DISPLAY(window);
258 klass = GST_VAAPI_WINDOW_X11_GET_CLASS(window);
260 if (klass->get_visual)
261 vis = klass->get_visual(window);
262 if (klass->get_colormap)
263 cmap = klass->get_colormap(window);
266 GST_VAAPI_OBJECT_LOCK_DISPLAY(window);
269 (char **)atom_names, G_N_ELEMENTS(atom_names),
273 priv->atom_NET_WM_STATE = atoms[0];
274 priv->atom_NET_WM_STATE_FULLSCREEN = atoms[1];
276 xid = x11_create_window(dpy, *width, *height, vis, cmap);
278 XRaiseWindow(dpy, xid);
279 GST_VAAPI_OBJECT_UNLOCK_DISPLAY(window);
281 GST_DEBUG("xid %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS(xid));
282 GST_VAAPI_OBJECT_ID(window) = xid;
287 gst_vaapi_window_x11_destroy(GstVaapiWindow *window)
289 GstVaapiWindowX11Private * const priv = GST_VAAPI_WINDOW_X11(window)->priv;
290 Display * const dpy = GST_VAAPI_OBJECT_XDISPLAY(window);
291 const Window xid = GST_VAAPI_OBJECT_ID(window);
294 if (priv->create_window) {
295 GST_VAAPI_OBJECT_LOCK_DISPLAY(window);
296 XDestroyWindow(dpy, xid);
297 GST_VAAPI_OBJECT_UNLOCK_DISPLAY(window);
299 GST_VAAPI_OBJECT_ID(window) = None;
304 gst_vaapi_window_x11_get_geometry(
305 GstVaapiWindow *window,
311 Display * const dpy = GST_VAAPI_OBJECT_XDISPLAY(window);
312 const Window xid = GST_VAAPI_OBJECT_ID(window);
314 return x11_get_geometry(dpy, xid, px, py, pwidth, pheight);
318 gst_vaapi_window_x11_set_fullscreen(GstVaapiWindow *window, gboolean fullscreen)
320 GstVaapiWindowX11Private * const priv = GST_VAAPI_WINDOW_X11(window)->priv;
321 Display * const dpy = GST_VAAPI_OBJECT_XDISPLAY(window);
322 const Window xid = GST_VAAPI_OBJECT_ID(window);
329 GST_VAAPI_OBJECT_LOCK_DISPLAY(window);
332 if (!priv->is_mapped) {
333 priv->fullscreen_on_map = TRUE;
338 priv->atom_NET_WM_STATE, XA_ATOM, 32,
340 (unsigned char *)&priv->atom_NET_WM_STATE_FULLSCREEN, 1
344 send_wmspec_change_state(
345 GST_VAAPI_WINDOW_X11(window),
346 priv->atom_NET_WM_STATE_FULLSCREEN,
352 if (!priv->is_mapped) {
353 priv->fullscreen_on_map = FALSE;
358 priv->atom_NET_WM_STATE
362 send_wmspec_change_state(
363 GST_VAAPI_WINDOW_X11(window),
364 priv->atom_NET_WM_STATE_FULLSCREEN,
370 has_errors = x11_untrap_errors() != 0;
371 GST_VAAPI_OBJECT_UNLOCK_DISPLAY(window);
375 /* Try to wait for the completion of the fullscreen mode switch */
376 if (priv->create_window && priv->is_mapped) {
377 const guint DELAY = 100000; /* 100 ms */
378 g_get_current_time(&now);
379 end_time = DELAY + ((guint64)now.tv_sec * 1000000 + now.tv_usec);
380 while (timed_wait_event(window, ConfigureNotify, end_time, &e)) {
382 gst_vaapi_display_get_size(
383 GST_VAAPI_OBJECT_DISPLAY(window),
387 if (e.xconfigure.width == width && e.xconfigure.height == height)
391 gst_vaapi_window_get_size(window, &width, &height);
392 if (e.xconfigure.width != width || e.xconfigure.height != height)
401 gst_vaapi_window_x11_resize(GstVaapiWindow *window, guint width, guint height)
405 if (!GST_VAAPI_OBJECT_ID(window))
408 GST_VAAPI_OBJECT_LOCK_DISPLAY(window);
411 GST_VAAPI_OBJECT_XDISPLAY(window),
412 GST_VAAPI_OBJECT_ID(window),
416 has_errors = x11_untrap_errors() != 0;
417 GST_VAAPI_OBJECT_UNLOCK_DISPLAY(window);
422 gst_vaapi_window_x11_render(
423 GstVaapiWindow *window,
424 GstVaapiSurface *surface,
425 const GstVaapiRectangle *src_rect,
426 const GstVaapiRectangle *dst_rect,
430 VASurfaceID surface_id;
433 surface_id = GST_VAAPI_OBJECT_ID(surface);
434 if (surface_id == VA_INVALID_ID)
437 GST_VAAPI_OBJECT_LOCK_DISPLAY(window);
438 status = vaPutSurface(
439 GST_VAAPI_OBJECT_VADISPLAY(window),
441 GST_VAAPI_OBJECT_ID(window),
451 from_GstVaapiSurfaceRenderFlags(flags)
453 GST_VAAPI_OBJECT_UNLOCK_DISPLAY(window);
454 if (!vaapi_check_status(status, "vaPutSurface()"))
461 gst_vaapi_window_x11_finalize(GObject *object)
463 G_OBJECT_CLASS(gst_vaapi_window_x11_parent_class)->finalize(object);
467 gst_vaapi_window_x11_constructed(GObject *object)
469 GstVaapiWindowX11 * const window = GST_VAAPI_WINDOW_X11(object);
470 GObjectClass *parent_class;
472 window->priv->create_window = GST_VAAPI_OBJECT_ID(object) == None;
474 parent_class = G_OBJECT_CLASS(gst_vaapi_window_x11_parent_class);
475 if (parent_class->constructed)
476 parent_class->constructed(object);
480 gst_vaapi_window_x11_class_init(GstVaapiWindowX11Class *klass)
482 GObjectClass * const object_class = G_OBJECT_CLASS(klass);
483 GstVaapiWindowClass * const window_class = GST_VAAPI_WINDOW_CLASS(klass);
485 g_type_class_add_private(klass, sizeof(GstVaapiWindowX11Private));
487 object_class->finalize = gst_vaapi_window_x11_finalize;
488 object_class->constructed = gst_vaapi_window_x11_constructed;
490 window_class->create = gst_vaapi_window_x11_create;
491 window_class->destroy = gst_vaapi_window_x11_destroy;
492 window_class->show = gst_vaapi_window_x11_show;
493 window_class->hide = gst_vaapi_window_x11_hide;
494 window_class->get_geometry = gst_vaapi_window_x11_get_geometry;
495 window_class->set_fullscreen = gst_vaapi_window_x11_set_fullscreen;
496 window_class->resize = gst_vaapi_window_x11_resize;
497 window_class->render = gst_vaapi_window_x11_render;
501 gst_vaapi_window_x11_init(GstVaapiWindowX11 *window)
503 GstVaapiWindowX11Private *priv = GST_VAAPI_WINDOW_X11_GET_PRIVATE(window);
506 priv->create_window = TRUE;
507 priv->is_mapped = FALSE;
508 priv->fullscreen_on_map = FALSE;
509 priv->is_pixmap = FALSE;
513 * gst_vaapi_window_x11_new:
514 * @display: a #GstVaapiDisplay
515 * @width: the requested window width, in pixels
516 * @height: the requested windo height, in pixels
518 * Creates a window with the specified @width and @height. The window
519 * will be attached to the @display and remains invisible to the user
520 * until gst_vaapi_window_show() is called.
522 * Return value: the newly allocated #GstVaapiWindow object
525 gst_vaapi_window_x11_new(GstVaapiDisplay *display, guint width, guint height)
527 GST_DEBUG("new window, size %ux%u", width, height);
529 g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), NULL);
530 g_return_val_if_fail(width > 0, NULL);
531 g_return_val_if_fail(height > 0, NULL);
533 return g_object_new(GST_VAAPI_TYPE_WINDOW_X11,
535 "id", GST_VAAPI_ID(None),
542 * gst_vaapi_window_x11_new_with_xid:
543 * @display: a #GstVaapiDisplay
544 * @xid: an X11 #Window id
546 * Creates a #GstVaapiWindow using the X11 #Window @xid. The caller
547 * still owns the window and must call XDestroyWindow() when all
548 * #GstVaapiWindow references are released. Doing so too early can
549 * yield undefined behaviour.
551 * Return value: the newly allocated #GstVaapiWindow object
554 gst_vaapi_window_x11_new_with_xid(GstVaapiDisplay *display, Window xid, gboolean is_pixmap)
556 GstVaapiWindow * window = NULL;
557 GST_DEBUG("new window from xid 0x%08x", xid);
559 g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), NULL);
560 g_return_val_if_fail(xid != None, NULL);
562 _is_pixmap = is_pixmap;
563 window = g_object_new(GST_VAAPI_TYPE_WINDOW_X11,
565 "id", GST_VAAPI_ID(xid),
569 GstVaapiWindowX11Private *priv = GST_VAAPI_WINDOW_X11_GET_PRIVATE(window);
570 priv->is_pixmap = is_pixmap;
577 * gst_vaapi_window_x11_get_xid:
578 * @window: a #GstVaapiWindowX11
580 * Returns the underlying X11 #Window that was created by
581 * gst_vaapi_window_x11_new() or that was bound with
582 * gst_vaapi_window_x11_new_with_xid().
584 * Return value: the underlying X11 #Window bound to @window.
587 gst_vaapi_window_x11_get_xid(GstVaapiWindowX11 *window)
589 g_return_val_if_fail(GST_VAAPI_IS_WINDOW_X11(window), None);
591 return GST_VAAPI_OBJECT_ID(window);
595 * gst_vaapi_window_x11_is_foreign_xid:
596 * @window: a #GstVaapiWindowX11
598 * Checks whether the @window XID was created by gst_vaapi_window_x11_new() or bound with gst_vaapi_window_x11_new_with_xid().
600 * Return value: %TRUE if the underlying X window is owned by the
601 * caller (foreign window)
604 gst_vaapi_window_x11_is_foreign_xid(GstVaapiWindowX11 *window)
606 g_return_val_if_fail(GST_VAAPI_IS_WINDOW_X11(window), FALSE);
608 return !window->priv->create_window;
612 * gst_vaapi_window_x11_is_pixmap:
613 * @window: a #GstVaapiWindowX11
615 * Checks whether the @window XID is Pixmap or Window
617 * Return value: %TRUE if the underlying X Drawble is Pixmap
620 gst_vaapi_window_x11_is_pixmap(GstVaapiWindowX11 *window)
622 g_return_val_if_fail(GST_VAAPI_IS_WINDOW_X11(window), FALSE);
624 return window->priv->is_pixmap;