gstreamer/gst-libs/gst/gl/gstglwindow_x11.c
Sebastian Dröge 10494006b8 [309/906] Replay: Fix callbacks passed over XEvents on 64 bit architectures
Althought the XEvent's xclient.data.l array is an array of
longs they will be constrained to 32 bit by the X11 protocol.
On 64 bit architectures use two elements of the array to store
one pointer.
This fixes segfaults that happen at least for every example
on startup.
2017-12-09 19:31:21 +00:00

861 lines
24 KiB
C

/*
* GStreamer
* Copyright (C) 2008 Julien Isorce <julien.isorce@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstglwindow.h"
#include <locale.h>
#include <GL/glx.h>
#define GST_GL_WINDOW_GET_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE((o), GST_GL_TYPE_WINDOW, GstGLWindowPrivate))
/* A gl window is created and deleted in a thread dedicated to opengl calls
The name contains "window" because an opengl context is used in cooperation
with a window */
enum
{
ARG_0,
ARG_DISPLAY
};
struct _GstGLWindowPrivate
{
/* X is not thread safe */
GMutex *x_lock;
GCond *cond_send_message;
gboolean running;
gboolean visible;
gboolean allow_extra_expose_events;
/* opengl context */
gchar *display_name;
Display *device;
Screen *screen;
gint screen_num;
Visual *visual;
Window root;
gulong white;
gulong black;
gint depth;
gint device_width;
gint device_height;
gint connection;
XVisualInfo *visual_info;
Window parent;
/* We use a specific connection to send events */
Display *disp_send;
/* X window */
Window internal_win_id;
GLXContext gl_context;
/* frozen callbacks */
GstGLWindowCB draw_cb;
gpointer draw_data;
GstGLWindowCB2 resize_cb;
gpointer resize_data;
GstGLWindowCB close_cb;
gpointer close_data;
};
G_DEFINE_TYPE (GstGLWindow, gst_gl_window, G_TYPE_OBJECT);
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "GstGLWindow"
gboolean _gst_gl_window_debug = FALSE;
/* Must be called in the gl thread */
static void
gst_gl_window_finalize (GObject * object)
{
GstGLWindow *window = GST_GL_WINDOW (object);
GstGLWindowPrivate *priv = window->priv;
XEvent event;
Bool ret = TRUE;
g_mutex_lock (priv->x_lock);
priv->parent = 0;
XUnmapWindow (priv->device, priv->internal_win_id);
ret = glXMakeCurrent (priv->device, None, NULL);
if (!ret)
g_debug ("failed to release opengl context\n");
glXDestroyContext (priv->device, priv->gl_context);
XFree (priv->visual_info);
XReparentWindow (priv->device, priv->internal_win_id, priv->root, 0, 0);
XDestroyWindow (priv->device, priv->internal_win_id);
XSync (priv->device, FALSE);
while(XPending (priv->device))
XNextEvent (priv->device, &event);
XSetCloseDownMode (priv->device, DestroyAll);
/*XAddToSaveSet (display, w)
Display *display;
Window w;*/
//FIXME: it seems it causes destroy all created windows, even by other display connection:
//This is case in: gst-launch-0.10 videotestsrc ! tee name=t t. ! queue ! glimagesink t. ! queue ! glimagesink
//When the first window is closed and so its display is closed by the following line, then the other Window managed by the
//other glimagesink, is not useable and so each opengl call causes a segmentation fault.
//Maybe the solution is to use: XAddToSaveSet
//The following line is commented to avoid the disagreement explained before.
//XCloseDisplay (priv->device);
g_debug ("display receiver closed\n");
XCloseDisplay (priv->disp_send);
g_debug ("display sender closed\n");
if (priv->cond_send_message)
{
g_cond_free (priv->cond_send_message);
priv->cond_send_message = NULL;
}
g_mutex_unlock (priv->x_lock);
if (priv->x_lock)
{
g_mutex_free (priv->x_lock);
priv->x_lock = NULL;
}
G_OBJECT_CLASS (gst_gl_window_parent_class)->finalize (object);
}
static void
gst_gl_window_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstGLWindow *window;
GstGLWindowPrivate *priv;
g_return_if_fail (GST_GL_IS_WINDOW (object));
window = GST_GL_WINDOW (object);
priv = window->priv;
switch (prop_id) {
case ARG_DISPLAY:
priv->display_name = g_strdup (g_value_get_string (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_gl_window_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstGLWindow *window;
GstGLWindowPrivate *priv;
g_return_if_fail (GST_GL_IS_WINDOW (object));
window = GST_GL_WINDOW (object);
priv = window->priv;
switch (prop_id) {
case ARG_DISPLAY:
g_value_set_string (value, priv->display_name);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_gl_window_log_handler (const gchar *domain, GLogLevelFlags flags,
const gchar *message, gpointer user_data)
{
if (_gst_gl_window_debug) {
g_log_default_handler (domain, flags, message, user_data);
}
}
static void
gst_gl_window_class_init (GstGLWindowClass * klass)
{
GObjectClass *obj_class = G_OBJECT_CLASS (klass);
g_type_class_add_private (klass, sizeof (GstGLWindowPrivate));
obj_class->finalize = gst_gl_window_finalize;
obj_class->set_property = gst_gl_window_set_property;
obj_class->get_property = gst_gl_window_get_property;
g_object_class_install_property (obj_class, ARG_DISPLAY,
g_param_spec_string ("display", "Display", "X Display name", NULL,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
}
static void
gst_gl_window_init (GstGLWindow *window)
{
window->priv = GST_GL_WINDOW_GET_PRIVATE (window);
if (g_getenv ("GST_GL_WINDOW_DEBUG") != NULL)
_gst_gl_window_debug = TRUE;
g_log_set_handler ("GstGLWindow", G_LOG_LEVEL_DEBUG,
gst_gl_window_log_handler, NULL);
}
/* Must be called in the gl thread */
GstGLWindow *
gst_gl_window_new (gint width, gint height)
{
GstGLWindow *window = g_object_new (GST_GL_TYPE_WINDOW, NULL);
GstGLWindowPrivate *priv = window->priv;
gint attrib[] = {
GLX_RGBA,
GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1,
GLX_DOUBLEBUFFER,
None
};
Bool ret = FALSE;
gint error_base;
gint event_base;
XSetWindowAttributes win_attr;
XTextProperty text_property;
XWMHints wm_hints;
unsigned long mask;
const gchar *title = "OpenGL renderer";
Atom wm_atoms[3];
static gint x = 0;
static gint y = 0;
setlocale(LC_NUMERIC, "C");
priv->x_lock = g_mutex_new ();
priv->cond_send_message = g_cond_new ();
priv->running = TRUE;
priv->visible = FALSE;
priv->parent = 0;
priv->allow_extra_expose_events = TRUE;
g_mutex_lock (priv->x_lock);
priv->device = XOpenDisplay (priv->display_name);
XSynchronize (priv->device, FALSE);
g_debug ("gl device id: %ld\n", (gulong) priv->device);
priv->disp_send = XOpenDisplay (priv->display_name);
XSynchronize (priv->disp_send, FALSE);
g_debug ("gl display sender: %ld\n", (gulong) priv->disp_send);
priv->screen = DefaultScreenOfDisplay (priv->device);
priv->screen_num = DefaultScreen (priv->device);
priv->visual = DefaultVisual (priv->device, priv->screen_num);
priv->root = DefaultRootWindow (priv->device);
priv->white = XWhitePixel (priv->device, priv->screen_num);
priv->black = XBlackPixel (priv->device, priv->screen_num);
priv->depth = DefaultDepthOfScreen (priv->screen);
g_debug ("gl root id: %" G_GUINT64_FORMAT "\n", (guint64) priv->root);
priv->device_width = DisplayWidth (priv->device, priv->screen_num);
priv->device_height = DisplayHeight (priv->device, priv->screen_num);
priv->connection = ConnectionNumber (priv->device);
ret = glXQueryExtension (priv->device, &error_base, &event_base);
if (!ret)
g_debug ("No GLX extension");
priv->visual_info = glXChooseVisual (priv->device, priv->screen_num, attrib);
if (!priv->visual_info)
{
g_warning ("glx visual is null (bad attributes)\n");
return NULL;
}
if (priv->visual_info->visual != priv->visual)
g_debug ("selected visual is different from the default\n");
if (priv->visual_info->class == TrueColor)
g_debug ("visual is using TrueColor\n");
g_debug ("visual ID: %d\n", (gint)XVisualIDFromVisual(priv->visual_info->visual));
g_debug ("visual info screen: %d\n", priv->visual_info->screen);
g_debug ("visual info visualid: %d\n", (gint) priv->visual_info->visualid);
g_debug ("visual info depth: %d\n", priv->visual_info->depth);
g_debug ("visual info class: %d\n", priv->visual_info->class);
g_debug ("visual info red_mask: %ld\n", priv->visual_info->red_mask);
g_debug ("visual info green_mask: %ld\n", priv->visual_info->green_mask);
g_debug ("visual info blue_mask: %ld\n", priv->visual_info->blue_mask);
g_debug ("visual info bits_per_rgb: %d\n", priv->visual_info->bits_per_rgb);
win_attr.event_mask = StructureNotifyMask | ExposureMask | VisibilityChangeMask;
win_attr.do_not_propagate_mask = NoEventMask;
win_attr.background_pixmap = None;
win_attr.background_pixel = 0;
win_attr.border_pixel = 0;
win_attr.colormap = XCreateColormap(priv->device, priv->root, priv->visual_info->visual, AllocNone);
mask = CWBackPixmap | CWBorderPixel | CWColormap | CWEventMask;
priv->internal_win_id = XCreateWindow (priv->device, priv->root, x, y,
width, height, 0, priv->visual_info->depth, InputOutput,
priv->visual_info->visual, mask, &win_attr);
x += 20;
y += 20;
XSync (priv->device, FALSE);
XSetWindowBackgroundPixmap (priv->device, priv->internal_win_id, None);
g_debug ("gl window id: %" G_GUINT64_FORMAT "\n", (guint64) priv->internal_win_id);
g_debug ("gl window props: x:%d y:%d w:%d h:%d\n", x, y, width, height);
wm_atoms[0] = XInternAtom (priv->device, "WM_DELETE_WINDOW", True);
if (wm_atoms[0] == None)
g_debug ("Cannot create WM_DELETE_WINDOW\n");
wm_atoms[1] = XInternAtom (priv->device, "WM_GL_WINDOW", False);
if (wm_atoms[1] == None)
g_debug ("Cannot create WM_GL_WINDOW\n");
wm_atoms[2] = XInternAtom (priv->device, "WM_QUIT_LOOP", False);
if (wm_atoms[2] == None)
g_debug ("Cannot create WM_QUIT_LOOP\n");
XSetWMProtocols (priv->device, priv->internal_win_id, wm_atoms, 2);
priv->gl_context = glXCreateContext (priv->device, priv->visual_info, NULL, TRUE);
g_debug ("gl context id: %ld\n", (gulong) priv->gl_context);
if (!glXIsDirect(priv->device, priv->gl_context))
g_debug ("direct rendering failed\n");
wm_hints.flags = StateHint;
wm_hints.initial_state = NormalState;
wm_hints.input = False;
XStringListToTextProperty ((char**)&title, 1, &text_property);
XSetWMProperties (priv->device, priv->internal_win_id, &text_property, &text_property, 0, 0,
NULL, &wm_hints, NULL);
XFree (text_property.value);
ret = glXMakeCurrent (priv->device, priv->internal_win_id, priv->gl_context);
if (!ret)
g_debug ("failed to make opengl context current\n");
if (glXIsDirect (priv->device, priv->gl_context))
g_debug ("Direct Rendering: yes\n");
else
g_debug ("Direct Rendering: no\n");
g_mutex_unlock (priv->x_lock);
return window;
}
GQuark
gst_gl_window_error_quark (void)
{
return g_quark_from_static_string ("gst-gl-window-error");
}
/* Not called by the gl thread */
void
gst_gl_window_set_external_window_id (GstGLWindow *window, guint64 id)
{
if (window)
{
GstGLWindowPrivate *priv = window->priv;
XWindowAttributes attr;
g_mutex_lock (priv->x_lock);
priv->parent = (Window) id;
g_debug ("set parent window id: %" G_GUINT64_FORMAT "\n", id);
XGetWindowAttributes (priv->disp_send, priv->parent, &attr);
XResizeWindow (priv->disp_send, priv->internal_win_id, attr.width, attr.height);
XReparentWindow (priv->disp_send, priv->internal_win_id, priv->parent, attr.x, attr.y);
XSync (priv->disp_send, FALSE);
g_mutex_unlock (priv->x_lock);
}
}
void
gst_gl_window_set_external_gl_context (GstGLWindow *window, guint64 context)
{
g_warning ("gst_gl_window_set_external_gl_context: not implemented\n");
}
void
gst_gl_window_set_draw_callback (GstGLWindow *window, GstGLWindowCB callback, gpointer data)
{
GstGLWindowPrivate *priv = window->priv;
g_mutex_lock (priv->x_lock);
priv->draw_cb = callback;
priv->draw_data = data;
g_mutex_unlock (priv->x_lock);
}
void
gst_gl_window_set_resize_callback (GstGLWindow *window, GstGLWindowCB2 callback , gpointer data)
{
GstGLWindowPrivate *priv = window->priv;
g_mutex_lock (priv->x_lock);
priv->resize_cb = callback;
priv->resize_data = data;
g_mutex_unlock (priv->x_lock);
}
void
gst_gl_window_set_close_callback (GstGLWindow *window, GstGLWindowCB callback, gpointer data)
{
GstGLWindowPrivate *priv = window->priv;
g_mutex_lock (priv->x_lock);
priv->close_cb = callback;
priv->close_data = data;
g_mutex_unlock (priv->x_lock);
}
/* Called in the gl thread */
void
gst_gl_window_draw_unlocked (GstGLWindow *window)
{
GstGLWindowPrivate *priv = window->priv;
if (priv->running && priv->allow_extra_expose_events)
{
XEvent event;
XWindowAttributes attr;
XGetWindowAttributes (priv->device, priv->internal_win_id, &attr);
event.xexpose.type = Expose;
event.xexpose.send_event = TRUE;
event.xexpose.display = priv->device;
event.xexpose.window = priv->internal_win_id;
event.xexpose.x = attr.x;
event.xexpose.y = attr.y;
event.xexpose.width = attr.width;
event.xexpose.height = attr.height;
event.xexpose.count = 0;
XSendEvent (priv->device, priv->internal_win_id, FALSE, ExposureMask, &event);
XSync (priv->disp_send, FALSE);
}
}
/* Not called by the gl thread */
void
gst_gl_window_draw (GstGLWindow *window)
{
if (window)
{
GstGLWindowPrivate *priv = window->priv;
g_mutex_lock (priv->x_lock);
if (priv->running)
{
XEvent event;
XWindowAttributes attr;
if (!priv->visible)
{
XMapWindow (priv->disp_send, priv->internal_win_id);
priv->visible = TRUE;
}
XGetWindowAttributes (priv->disp_send, priv->internal_win_id, &attr);
if (priv->parent)
{
XWindowAttributes attr_parent;
XGetWindowAttributes (priv->disp_send, priv->parent, &attr_parent);
if (attr.x != attr_parent.x || attr.y != attr_parent.y ||
attr.width != attr_parent.width || attr.height != attr_parent.height)
{
XMoveResizeWindow (priv->disp_send, priv->internal_win_id, attr_parent.x, attr_parent.y,
attr_parent.width, attr_parent.height);
XSync (priv->disp_send, FALSE);
attr.x = attr_parent.x;
attr.y = attr_parent.y;
attr.width = attr_parent.width;
attr.height = attr_parent.height;
g_debug ("parent resize: %d, %d, %d, %d\n", attr_parent.x, attr_parent.y,
attr_parent.width, attr_parent.height);
}
}
event.xexpose.type = Expose;
event.xexpose.send_event = TRUE;
event.xexpose.display = priv->disp_send;
event.xexpose.window = priv->internal_win_id;
event.xexpose.x = attr.x;
event.xexpose.y = attr.y;
event.xexpose.width = attr.width;
event.xexpose.height = attr.height;
event.xexpose.count = 0;
XSendEvent (priv->disp_send, priv->internal_win_id, FALSE, ExposureMask, &event);
XSync (priv->disp_send, FALSE);
}
g_mutex_unlock (priv->x_lock);
}
}
/* Called in the gl thread */
void
gst_gl_window_run_loop (GstGLWindow *window)
{
GstGLWindowPrivate *priv = window->priv;
g_debug ("begin loop\n");
g_mutex_lock (priv->x_lock);
while (priv->running)
{
XEvent event;
XEvent pending_event;
g_mutex_unlock (priv->x_lock);
/* XSendEvent (which are called in other threads) are done from another display structure */
XNextEvent(priv->device, &event);
g_mutex_lock (priv->x_lock);
// use in generic/cube and other related uses
priv->allow_extra_expose_events = XPending (priv->device) <= 2;
switch (event.type)
{
case ClientMessage:
{
Atom wm_delete = XInternAtom (priv->device, "WM_DELETE_WINDOW", True);
Atom wm_gl = XInternAtom (priv->device, "WM_GL_WINDOW", True);
Atom wm_quit_loop = XInternAtom (priv->device, "WM_QUIT_LOOP", True);
if (wm_delete == None)
g_debug ("Cannot create WM_DELETE_WINDOW\n");
if (wm_gl == None)
g_debug ("Cannot create WM_GL_WINDOW\n");
if (wm_quit_loop == None)
g_debug ("Cannot create WM_QUIT_LOOP\n");
/* Message sent with gst_gl_window_send_message */
if (wm_gl != None && event.xclient.message_type == wm_gl) {
if (priv->running) {
#if SIZEOF_VOID_P == 8
GstGLWindowCB custom_cb =
(GstGLWindowCB) (((event.xclient.
data.l[0] & 0xffffffff) << 32) | (event.xclient.
data.l[1] & 0xffffffff));
gpointer custom_data =
(gpointer) (((event.xclient.
data.l[2] & 0xffffffff) << 32) | (event.xclient.
data.l[3] & 0xffffffff));
#else
GstGLWindowCB custom_cb = (GstGLWindowCB) event.xclient.data.l[0];
gpointer custom_data = (gpointer) event.xclient.data.l[1];
#endif
if (!custom_cb || !custom_data)
g_debug ("custom cb not initialized\n");
custom_cb (custom_data);
}
g_cond_signal (priv->cond_send_message);
}
/* User clicked on the cross */
else if (wm_delete != None && (Atom) event.xclient.data.l[0] == wm_delete)
{
g_debug ("Close %" G_GUINT64_FORMAT "\n", (guint64) priv->internal_win_id);
if (priv->close_cb)
priv->close_cb (priv->close_data);
priv->draw_cb = NULL;
priv->draw_data = NULL;
priv->resize_cb = NULL;
priv->resize_data = NULL;
priv->close_cb = NULL;
priv->close_data = NULL;
}
/* message sent with gst_gl_window_quit_loop */
else if (wm_quit_loop != None
&& event.xclient.message_type == wm_quit_loop) {
#if SIZEOF_VOID_P == 8
GstGLWindowCB destroy_cb =
(GstGLWindowCB) (((event.xclient.
data.l[0] & 0xffffffff) << 32) | (event.xclient.
data.l[1] & 0xffffffff));
gpointer destroy_data =
(gpointer) (((event.xclient.
data.l[2] & 0xffffffff) << 32) | (event.xclient.
data.l[3] & 0xffffffff));
#else
GstGLWindowCB destroy_cb = (GstGLWindowCB) event.xclient.data.l[0];
gpointer destroy_data = (gpointer) event.xclient.data.l[1];
#endif
g_debug ("Quit loop message %" G_GUINT64_FORMAT "\n", (guint64) priv->internal_win_id);
/* exit loop */
priv->running = FALSE;
/* make sure last pendings send message calls are executed */
XFlush (priv->device);
while (XCheckTypedEvent (priv->device, ClientMessage, &pending_event)) {
#if SIZEOF_VOID_P == 8
GstGLWindowCB custom_cb =
(GstGLWindowCB) (((event.xclient.
data.l[0] & 0xffffffff) << 32) | (event.xclient.
data.l[1] & 0xffffffff));
gpointer custom_data =
(gpointer) (((event.xclient.
data.l[2] & 0xffffffff) << 32) | (event.xclient.
data.l[3] & 0xffffffff));
#else
GstGLWindowCB custom_cb = (GstGLWindowCB) event.xclient.data.l[0];
gpointer custom_data = (gpointer) event.xclient.data.l[1];
#endif
g_debug ("execute last pending custom x events\n");
if (!custom_cb || !custom_data)
g_debug ("custom cb not initialized\n");
custom_cb (custom_data);
g_cond_signal (priv->cond_send_message);
}
/* Finally we can destroy opengl ressources (texture/shaders/fbo) */
if (!destroy_cb || !destroy_data)
g_debug ("destroy cb not correclty set\n");
destroy_cb (destroy_data);
}
else
g_debug("client message not reconized \n");
break;
}
case CreateNotify:
case ConfigureNotify:
{
if (priv->resize_cb)
priv->resize_cb (priv->resize_data, event.xconfigure.width, event.xconfigure.height);
break;
}
case DestroyNotify:
g_debug ("DestroyNotify\n");
break;
case Expose:
if (priv->draw_cb)
{
priv->draw_cb (priv->draw_data);
glFlush();
glXSwapBuffers (priv->device, priv->internal_win_id);
}
break;
case VisibilityNotify:
{
switch (event.xvisibility.state)
{
case VisibilityUnobscured:
if (priv->draw_cb)
priv->draw_cb (priv->draw_data);
break;
case VisibilityPartiallyObscured:
if (priv->draw_cb)
priv->draw_cb (priv->draw_data);
break;
case VisibilityFullyObscured:
break;
default:
g_debug("unknown xvisibility event: %d\n", event.xvisibility.state);
break;
}
break;
}
default:
g_debug ("unknow\n");
break;
}// switch
}// while running
g_mutex_unlock (priv->x_lock);
g_debug ("end loop\n");
}
/* Not called by the gl thread */
void
gst_gl_window_quit_loop (GstGLWindow *window, GstGLWindowCB callback, gpointer data)
{
if (window)
{
GstGLWindowPrivate *priv = window->priv;
g_mutex_lock (priv->x_lock);
if (priv->running)
{
XEvent event;
event.xclient.type = ClientMessage;
event.xclient.send_event = TRUE;
event.xclient.display = priv->disp_send;
event.xclient.window = priv->internal_win_id;
event.xclient.message_type = XInternAtom (priv->disp_send, "WM_QUIT_LOOP", True);;
event.xclient.format = 32;
#if SIZEOF_VOID_P == 8
event.xclient.data.l[0] = (((long) callback) >> 32) & 0xffffffff;
event.xclient.data.l[1] = (((long) callback)) & 0xffffffff;
event.xclient.data.l[2] = (((long) data) >> 32) & 0xffffffff;
event.xclient.data.l[3] = (((long) data)) & 0xffffffff;
#else
event.xclient.data.l[0] = (long) callback;
event.xclient.data.l[1] = (long) data;
#endif
XSendEvent (priv->disp_send, priv->internal_win_id, FALSE, NoEventMask, &event);
XSync (priv->disp_send, FALSE);
}
g_mutex_unlock (priv->x_lock);
}
}
/* Not called by the gl thread */
void
gst_gl_window_send_message (GstGLWindow *window, GstGLWindowCB callback, gpointer data)
{
if (window)
{
GstGLWindowPrivate *priv = window->priv;
g_mutex_lock (priv->x_lock);
if (priv->running)
{
XEvent event;
event.xclient.type = ClientMessage;
event.xclient.send_event = TRUE;
event.xclient.display = priv->disp_send;
event.xclient.window = priv->internal_win_id;
event.xclient.message_type = XInternAtom (priv->disp_send, "WM_GL_WINDOW", True);
event.xclient.format = 32;
#if SIZEOF_VOID_P == 8
event.xclient.data.l[0] = (((long) callback) >> 32) & 0xffffffff;
event.xclient.data.l[1] = (((long) callback)) & 0xffffffff;
event.xclient.data.l[2] = (((long) data) >> 32) & 0xffffffff;
event.xclient.data.l[3] = (((long) data)) & 0xffffffff;
#else
event.xclient.data.l[0] = (long) callback;
event.xclient.data.l[1] = (long) data;
#endif
XSendEvent (priv->disp_send, priv->internal_win_id, FALSE, NoEventMask, &event);
XSync (priv->disp_send, FALSE);
/* block until opengl calls have been executed in the gl thread */
g_cond_wait (priv->cond_send_message, priv->x_lock);
}
g_mutex_unlock (priv->x_lock);
}
}