3 * Copyright (C) 2008 Julien Isorce <julien.isorce@gmail.com>
4 * Copyright (C) 2012 Matthew Waters <ystreet00@gmail.com>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 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 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
22 #define GLIB_DISABLE_DEPRECATION_WARNINGS
31 #include "x11_event_source.h"
32 #include "gstglwindow_x11.h"
34 #define GST_GL_WINDOW_X11_GET_PRIVATE(o) \
35 (G_TYPE_INSTANCE_GET_PRIVATE((o), GST_GL_TYPE_WINDOW_X11, GstGLWindowX11Private))
37 #define GST_CAT_DEFAULT gst_gl_window_debug
39 #define gst_gl_window_x11_parent_class parent_class
40 G_DEFINE_TYPE (GstGLWindowX11, gst_gl_window_x11, GST_GL_TYPE_WINDOW);
43 static int TrappedErrorCode = 0;
44 static int (*old_error_handler) (Display *, XErrorEvent *);
52 struct _GstGLWindowX11Private
55 gboolean activate_result;
58 guintptr gst_gl_window_x11_get_display (GstGLWindow * window);
59 guintptr gst_gl_window_x11_get_gl_context (GstGLWindow * window);
60 gboolean gst_gl_window_x11_activate (GstGLWindow * window, gboolean activate);
61 void gst_gl_window_x11_set_window_handle (GstGLWindow * window,
63 guintptr gst_gl_window_x11_get_window_handle (GstGLWindow * window);
64 void gst_gl_window_x11_draw_unlocked (GstGLWindow * window, guint width,
66 void gst_gl_window_x11_draw (GstGLWindow * window, guint width, guint height);
67 void gst_gl_window_x11_run (GstGLWindow * window);
68 void gst_gl_window_x11_quit (GstGLWindow * window);
69 void gst_gl_window_x11_send_message_async (GstGLWindow * window,
70 GstGLWindowCB callback, gpointer data, GDestroyNotify destroy);
71 gboolean gst_gl_window_x11_create_context (GstGLWindow * window,
72 GstGLAPI gl_api, guintptr external_gl_context, GError ** error);
73 gboolean gst_gl_window_x11_open (GstGLWindow * window, GError ** error);
74 void gst_gl_window_x11_close (GstGLWindow * window);
77 gst_gl_window_x11_set_property (GObject * object, guint prop_id,
78 const GValue * value, GParamSpec * pspec)
80 GstGLWindowX11 *window_x11;
82 g_return_if_fail (GST_GL_IS_WINDOW_X11 (object));
84 window_x11 = GST_GL_WINDOW_X11 (object);
88 window_x11->display_name = g_strdup (g_value_get_string (value));
91 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
97 gst_gl_window_x11_get_property (GObject * object, guint prop_id,
98 GValue * value, GParamSpec * pspec)
100 GstGLWindowX11 *window_x11;
102 g_return_if_fail (GST_GL_IS_WINDOW_X11 (object));
104 window_x11 = GST_GL_WINDOW_X11 (object);
108 g_value_set_string (value, window_x11->display_name);
111 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
117 gst_gl_window_x11_finalize (GObject * object)
119 GstGLWindowX11 *window_x11;
121 g_return_if_fail (GST_GL_IS_WINDOW_X11 (object));
123 window_x11 = GST_GL_WINDOW_X11 (object);
125 g_mutex_clear (&window_x11->disp_send_lock);
127 G_OBJECT_CLASS (parent_class)->finalize (object);
131 gst_gl_window_x11_class_init (GstGLWindowX11Class * klass)
133 GObjectClass *obj_class = G_OBJECT_CLASS (klass);
134 GstGLWindowClass *window_class = (GstGLWindowClass *) klass;
136 g_type_class_add_private (klass, sizeof (GstGLWindowX11Private));
138 obj_class->set_property = gst_gl_window_x11_set_property;
139 obj_class->get_property = gst_gl_window_x11_get_property;
140 obj_class->finalize = gst_gl_window_x11_finalize;
142 g_object_class_install_property (obj_class, ARG_DISPLAY,
143 g_param_spec_string ("display", "Display", "X Display name", NULL,
144 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
146 window_class->get_display = GST_DEBUG_FUNCPTR (gst_gl_window_x11_get_display);
147 window_class->set_window_handle =
148 GST_DEBUG_FUNCPTR (gst_gl_window_x11_set_window_handle);
149 window_class->get_window_handle =
150 GST_DEBUG_FUNCPTR (gst_gl_window_x11_get_window_handle);
151 window_class->draw_unlocked =
152 GST_DEBUG_FUNCPTR (gst_gl_window_x11_draw_unlocked);
153 window_class->draw = GST_DEBUG_FUNCPTR (gst_gl_window_x11_draw);
154 window_class->run = GST_DEBUG_FUNCPTR (gst_gl_window_x11_run);
155 window_class->quit = GST_DEBUG_FUNCPTR (gst_gl_window_x11_quit);
156 window_class->send_message_async =
157 GST_DEBUG_FUNCPTR (gst_gl_window_x11_send_message_async);
158 window_class->open = GST_DEBUG_FUNCPTR (gst_gl_window_x11_open);
159 window_class->close = GST_DEBUG_FUNCPTR (gst_gl_window_x11_close);
163 gst_gl_window_x11_init (GstGLWindowX11 * window)
165 window->priv = GST_GL_WINDOW_X11_GET_PRIVATE (window);
167 g_mutex_init (&window->disp_send_lock);
170 /* Must be called in the gl thread */
172 gst_gl_window_x11_new (void)
174 GstGLWindowX11 *window = NULL;
176 window = g_object_new (GST_GL_TYPE_WINDOW_X11, NULL);
178 gst_gl_window_set_need_lock (GST_GL_WINDOW (window), FALSE);
184 gst_gl_window_x11_open (GstGLWindow * window, GError ** error)
186 GstGLWindowX11 *window_x11 = GST_GL_WINDOW_X11 (window);
188 window_x11->device = XOpenDisplay (window_x11->display_name);
189 if (window_x11->device == NULL) {
190 g_set_error (error, GST_GL_WINDOW_ERROR,
191 GST_GL_WINDOW_ERROR_RESOURCE_UNAVAILABLE,
192 "Failed to connect to X display server");
196 XSynchronize (window_x11->device, FALSE);
198 GST_LOG ("gl device id: %ld", (gulong) window_x11->device);
200 window_x11->disp_send = XOpenDisplay (window_x11->display_name);
202 XSynchronize (window_x11->disp_send, FALSE);
204 GST_LOG ("gl display sender: %ld", (gulong) window_x11->disp_send);
206 g_assert (window_x11->device);
208 window_x11->screen = DefaultScreenOfDisplay (window_x11->device);
209 window_x11->screen_num = DefaultScreen (window_x11->device);
211 DefaultVisual (window_x11->device, window_x11->screen_num);
212 window_x11->root = DefaultRootWindow (window_x11->device);
213 window_x11->white = XWhitePixel (window_x11->device, window_x11->screen_num);
214 window_x11->black = XBlackPixel (window_x11->device, window_x11->screen_num);
215 window_x11->depth = DefaultDepthOfScreen (window_x11->screen);
217 GST_LOG ("gl root id: %lud", (gulong) window_x11->root);
219 window_x11->device_width =
220 DisplayWidth (window_x11->device, window_x11->screen_num);
221 window_x11->device_height =
222 DisplayHeight (window_x11->device, window_x11->screen_num);
224 window_x11->x11_source = x11_event_source_new (window_x11);
225 window_x11->main_context = g_main_context_new ();
226 window_x11->loop = g_main_loop_new (window_x11->main_context, FALSE);
228 g_source_attach (window_x11->x11_source, window_x11->main_context);
230 window_x11->allow_extra_expose_events = TRUE;
239 gst_gl_window_x11_create_window (GstGLWindowX11 * window_x11)
241 XSetWindowAttributes win_attr;
242 XTextProperty text_property;
245 const gchar *title = "OpenGL renderer";
247 gint x = 0, y = 0, width = 1, height = 1;
249 if (window_x11->visual_info->visual != window_x11->visual)
250 GST_LOG ("selected visual is different from the default");
252 GST_LOG ("visual XID:%d, screen:%d, visualid:%d, depth:%d, class:%d, "
253 "red_mask:%ld, green_mask:%ld, blue_mask:%ld bpp:%d",
254 (gint) XVisualIDFromVisual (window_x11->visual_info->visual),
255 window_x11->visual_info->screen, (gint) window_x11->visual_info->visualid,
256 window_x11->visual_info->depth, window_x11->visual_info->class,
257 window_x11->visual_info->red_mask, window_x11->visual_info->green_mask,
258 window_x11->visual_info->blue_mask,
259 window_x11->visual_info->bits_per_rgb);
261 win_attr.event_mask =
262 StructureNotifyMask | ExposureMask | VisibilityChangeMask;
263 win_attr.do_not_propagate_mask = NoEventMask;
265 win_attr.background_pixmap = None;
266 win_attr.background_pixel = 0;
267 win_attr.border_pixel = 0;
270 XCreateColormap (window_x11->device, window_x11->root,
271 window_x11->visual_info->visual, AllocNone);
273 mask = CWBackPixmap | CWBorderPixel | CWColormap | CWEventMask;
275 window_x11->internal_win_id =
276 XCreateWindow (window_x11->device,
277 window_x11->parent_win ? window_x11->parent_win : window_x11->root,
278 x, y, width, height, 0,
279 window_x11->visual_info->depth, InputOutput,
280 window_x11->visual_info->visual, mask, &win_attr);
282 XSync (window_x11->device, FALSE);
284 XSetWindowBackgroundPixmap (window_x11->device, window_x11->internal_win_id,
287 GST_LOG ("gl window id: %lud", (gulong) window_x11->internal_win_id);
288 GST_LOG ("gl window props: x:%d y:%d", x, y);
290 wm_atoms[0] = XInternAtom (window_x11->device, "WM_DELETE_WINDOW", True);
291 if (wm_atoms[0] == None)
292 GST_DEBUG ("Cannot create WM_DELETE_WINDOW");
294 XSetWMProtocols (window_x11->device, window_x11->internal_win_id, wm_atoms,
297 wm_hints.flags = StateHint;
298 wm_hints.initial_state = NormalState;
299 wm_hints.input = False;
301 XStringListToTextProperty ((char **) &title, 1, &text_property);
303 XSetWMProperties (window_x11->device, window_x11->internal_win_id,
304 &text_property, &text_property, 0, 0, NULL, &wm_hints, NULL);
306 XFree (text_property.value);
312 gst_gl_window_x11_close (GstGLWindow * window)
314 GstGLWindowX11 *window_x11 = GST_GL_WINDOW_X11 (window);
317 GST_GL_WINDOW_LOCK (window_x11);
319 if (window_x11->device) {
320 if (window_x11->internal_win_id)
321 XUnmapWindow (window_x11->device, window_x11->internal_win_id);
323 XFree (window_x11->visual_info);
325 if (window_x11->internal_win_id) {
326 XReparentWindow (window_x11->device, window_x11->internal_win_id,
327 window_x11->root, 0, 0);
328 XDestroyWindow (window_x11->device, window_x11->internal_win_id);
330 XSync (window_x11->device, FALSE);
332 while (XPending (window_x11->device))
333 XNextEvent (window_x11->device, &event);
335 XSetCloseDownMode (window_x11->device, DestroyAll);
337 /*XAddToSaveSet (display, w)
341 //FIXME: it seems it causes destroy all created windows, even by other display connection:
342 //This is case in: gst-launch-0.10 videotestsrc ! tee name=t t. ! queue ! glimagesink t. ! queue ! glimagesink
343 //When the first window is closed and so its display is closed by the following line, then the other Window managed by the
344 //other glimagesink, is not useable and so each opengl call causes a segmentation fault.
345 //Maybe the solution is to use: XAddToSaveSet
346 //The following line is commented to avoid the disagreement explained before.
347 //XCloseDisplay (window_x11->device);
349 GST_DEBUG ("display receiver closed");
350 XCloseDisplay (window_x11->disp_send);
351 GST_DEBUG ("display sender closed");
354 g_source_destroy (window_x11->x11_source);
355 g_source_unref (window_x11->x11_source);
356 window_x11->x11_source = NULL;
357 g_main_loop_unref (window_x11->loop);
358 window_x11->loop = NULL, g_main_context_unref (window_x11->main_context);
359 window_x11->main_context = NULL;
361 window_x11->running = FALSE;
363 GST_GL_WINDOW_UNLOCK (window_x11);
367 gst_gl_window_x11_get_gl_context (GstGLWindow * window)
369 GstGLWindowX11Class *window_class;
371 window_class = GST_GL_WINDOW_X11_GET_CLASS (window);
373 return window_class->get_gl_context (GST_GL_WINDOW_X11 (window));
377 callback_activate (GstGLWindow * window)
379 GstGLWindowX11Class *window_class;
380 GstGLWindowX11Private *priv;
381 GstGLWindowX11 *window_x11;
383 window_x11 = GST_GL_WINDOW_X11 (window);
384 window_class = GST_GL_WINDOW_X11_GET_CLASS (window_x11);
385 priv = window_x11->priv;
387 priv->activate_result = window_class->activate (window_x11, priv->activate);
391 gst_gl_window_x11_activate (GstGLWindow * window, gboolean activate)
393 GstGLWindowX11 *window_x11;
394 GstGLWindowX11Private *priv;
396 window_x11 = GST_GL_WINDOW_X11 (window);
397 priv = window_x11->priv;
398 priv->activate = activate;
400 gst_gl_window_send_message (window, GST_GL_WINDOW_CB (callback_activate),
403 return priv->activate_result;
406 /* Not called by the gl thread */
408 gst_gl_window_x11_set_window_handle (GstGLWindow * window, guintptr id)
410 GstGLWindowX11 *window_x11;
411 XWindowAttributes attr;
413 window_x11 = GST_GL_WINDOW_X11 (window);
415 window_x11->parent_win = (Window) id;
417 if (g_main_loop_is_running (window_x11->loop)) {
418 GST_LOG ("set parent window id: %lud", id);
420 g_mutex_lock (&window_x11->disp_send_lock);
421 XGetWindowAttributes (window_x11->disp_send, window_x11->parent_win, &attr);
423 XResizeWindow (window_x11->disp_send, window_x11->internal_win_id,
424 attr.width, attr.height);
426 XReparentWindow (window_x11->disp_send, window_x11->internal_win_id,
427 window_x11->parent_win, 0, 0);
429 XSync (window_x11->disp_send, FALSE);
430 g_mutex_unlock (&window_x11->disp_send_lock);
435 gst_gl_window_x11_get_window_handle (GstGLWindow * window)
437 GstGLWindowX11 *window_x11;
439 window_x11 = GST_GL_WINDOW_X11 (window);
441 return window_x11->internal_win_id;
444 /* Called in the gl thread */
446 gst_gl_window_x11_draw_unlocked (GstGLWindow * window, guint width,
449 GstGLWindowX11 *window_x11;
451 window_x11 = GST_GL_WINDOW_X11 (window);
453 if (g_main_loop_is_running (window_x11->loop)
454 && window_x11->allow_extra_expose_events) {
456 XWindowAttributes attr;
458 XGetWindowAttributes (window_x11->device, window_x11->internal_win_id,
461 event.xexpose.type = Expose;
462 event.xexpose.send_event = TRUE;
463 event.xexpose.display = window_x11->device;
464 event.xexpose.window = window_x11->internal_win_id;
465 event.xexpose.x = attr.x;
466 event.xexpose.y = attr.y;
467 event.xexpose.width = attr.width;
468 event.xexpose.height = attr.height;
469 event.xexpose.count = 0;
471 XSendEvent (window_x11->device, window_x11->internal_win_id, FALSE,
472 ExposureMask, &event);
473 XSync (window_x11->device, FALSE);
477 /* Not called by the gl thread */
479 gst_gl_window_x11_draw (GstGLWindow * window, guint width, guint height)
481 GstGLWindowX11 *window_x11;
483 window_x11 = GST_GL_WINDOW_X11 (window);
485 if (g_main_loop_is_running (window_x11->loop)) {
487 XWindowAttributes attr;
489 g_mutex_lock (&window_x11->disp_send_lock);
491 XGetWindowAttributes (window_x11->disp_send, window_x11->internal_win_id,
494 if (!window_x11->visible) {
496 if (!window_x11->parent_win) {
498 attr.height = height;
499 XResizeWindow (window_x11->disp_send, window_x11->internal_win_id,
500 attr.width, attr.height);
501 XSync (window_x11->disp_send, FALSE);
504 XMapWindow (window_x11->disp_send, window_x11->internal_win_id);
505 window_x11->visible = TRUE;
508 if (window_x11->parent_win) {
509 XWindowAttributes attr_parent;
510 XGetWindowAttributes (window_x11->disp_send, window_x11->parent_win,
513 if (attr.width != attr_parent.width || attr.height != attr_parent.height) {
514 XMoveResizeWindow (window_x11->disp_send, window_x11->internal_win_id,
515 0, 0, attr_parent.width, attr_parent.height);
516 XSync (window_x11->disp_send, FALSE);
518 attr.width = attr_parent.width;
519 attr.height = attr_parent.height;
521 GST_LOG ("parent resize: %d, %d",
522 attr_parent.width, attr_parent.height);
526 event.xexpose.type = Expose;
527 event.xexpose.send_event = TRUE;
528 event.xexpose.display = window_x11->disp_send;
529 event.xexpose.window = window_x11->internal_win_id;
530 event.xexpose.x = attr.x;
531 event.xexpose.y = attr.y;
532 event.xexpose.width = attr.width;
533 event.xexpose.height = attr.height;
534 event.xexpose.count = 0;
536 XSendEvent (window_x11->disp_send, window_x11->internal_win_id, FALSE,
537 ExposureMask, &event);
538 XSync (window_x11->disp_send, FALSE);
540 g_mutex_unlock (&window_x11->disp_send_lock);
545 gst_gl_window_x11_run (GstGLWindow * window)
547 GstGLWindowX11 *window_x11;
549 window_x11 = GST_GL_WINDOW_X11 (window);
551 g_main_loop_run (window_x11->loop);
554 static inline gchar *
555 event_type_to_string (guint type)
559 return "CreateNotify";
560 case ConfigureNotify:
561 return "ConfigureNotify";
563 return "DestroyNotify";
567 return "UnmapNotify";
570 case VisibilityNotify:
571 return "VisibilityNotify";
573 return "PropertyNotify";
575 return "SelectionClear";
576 case SelectionNotify:
577 return "SelectionNotify";
578 case SelectionRequest:
579 return "SelectionRequest";
581 return "ClientMessage";
588 gst_gl_window_x11_handle_event (GstGLWindowX11 * window_x11)
590 GstGLContext *context;
591 GstGLContextClass *context_class;
596 window = GST_GL_WINDOW (window_x11);
598 if (g_main_loop_is_running (window_x11->loop)
599 && XPending (window_x11->device)) {
602 /* XSendEvent (which are called in other threads) are done from another display structure */
603 XNextEvent (window_x11->device, &event);
605 window_x11->allow_extra_expose_events = XPending (window_x11->device) <= 2;
607 GST_LOG ("got event %s", event_type_to_string (event.type));
609 switch (event.type) {
613 XInternAtom (window_x11->device, "WM_DELETE_WINDOW", True);
615 if (wm_delete == None)
616 GST_DEBUG ("Cannot create WM_DELETE_WINDOW");
618 /* User clicked on the cross */
619 if (wm_delete != None && (Atom) event.xclient.data.l[0] == wm_delete) {
620 GST_DEBUG ("Close %lud", (gulong) window_x11->internal_win_id);
623 window->close (window->close_data);
631 case ConfigureNotify:
634 window->resize (window->resize_data, event.xconfigure.width,
635 event.xconfigure.height);
644 context = gst_gl_window_get_context (window);
645 context_class = GST_GL_CONTEXT_GET_CLASS (context);
647 window->draw (window->draw_data);
648 context_class->swap_buffers (context);
650 gst_object_unref (context);
654 case VisibilityNotify:
656 switch (event.xvisibility.state) {
657 case VisibilityUnobscured:
659 window->draw (window->draw_data);
662 case VisibilityPartiallyObscured:
664 window->draw (window->draw_data);
667 case VisibilityFullyObscured:
671 GST_DEBUG ("unknown xvisibility event: %d",
672 event.xvisibility.state);
679 GST_DEBUG ("unknown XEvent type: %u", event.type);
688 /* Not called by the gl thread */
690 gst_gl_window_x11_quit (GstGLWindow * window)
692 GstGLWindowX11 *window_x11;
694 window_x11 = GST_GL_WINDOW_X11 (window);
696 GST_LOG ("sending quit");
698 g_main_loop_quit (window_x11->loop);
700 GST_LOG ("quit sent");
703 typedef struct _GstGLMessage
705 GstGLWindowCB callback;
707 GDestroyNotify destroy;
711 _run_message (GstGLMessage * message)
713 if (message->callback)
714 message->callback (message->data);
716 if (message->destroy)
717 message->destroy (message->data);
719 g_slice_free (GstGLMessage, message);
725 gst_gl_window_x11_send_message_async (GstGLWindow * window,
726 GstGLWindowCB callback, gpointer data, GDestroyNotify destroy)
728 GstGLWindowX11 *window_x11;
729 GstGLMessage *message;
731 window_x11 = GST_GL_WINDOW_X11 (window);
732 message = g_slice_new (GstGLMessage);
734 message->callback = callback;
735 message->data = data;
736 message->destroy = destroy;
738 g_main_context_invoke (window_x11->main_context, (GSourceFunc) _run_message,
743 error_handler (Display * xdpy, XErrorEvent * error)
745 TrappedErrorCode = error->error_code;
750 * gst_gl_window_x11_trap_x_errors:
752 * Traps every X error until gst_gl_window_x11_untrap_x_errors() is called.
755 gst_gl_window_x11_trap_x_errors (void)
757 TrappedErrorCode = 0;
758 old_error_handler = XSetErrorHandler (error_handler);
762 * gst_gl_window_x11_untrap_x_errors:
764 * Removes the X error trap and returns the current status.
766 * Return value: the trapped error code, or 0 for success
769 gst_gl_window_x11_untrap_x_errors (void)
771 XSetErrorHandler (old_error_handler);
773 return TrappedErrorCode;
777 gst_gl_window_x11_get_display (GstGLWindow * window)
779 GstGLWindowX11 *window_x11;
781 window_x11 = GST_GL_WINDOW_X11 (window);
783 return (guintptr) window_x11->device;