mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-25 09:40:37 +00:00
GstGLWindow: Introduce navigation thread
This thread dispatches navigation events. It is needed to avoid deadlocks between window backend threads that emit navigation events (e.g. X11/GMainLoop thread) and consumers of navigation events such as glimagesink, see https://bugzilla.gnome.org/show_bug.cgi?id=733661 GstGlWindow_x11 thread is changed to invoke the navigation thread for navigation dispatching, instead of emiting the event itself. Othe backends beside X11 do not dispatch navigation events yet, but should use this thread when dispatching these events in the future. The navigation thread is currently part of GstGLWindow and not implemented in separate subclasses / backends. This will be needed in the future. gst_gl_window_x11_get_surface_dimensions is also changed to use a cached value of the window's width, height. These values are now retrieved in the X11 thread, function gst_gl_window_x11_handle_event. This change is needed because otherwise the XGetWindowAttributes gets called from the navigation thread, leading to xlib aborting due to multithreaded access (if XInitThreads is not called before, as is the case for gst-launch)
This commit is contained in:
parent
10ece74f10
commit
3c3b78508f
4 changed files with 191 additions and 13 deletions
|
@ -79,10 +79,16 @@ G_DEFINE_ABSTRACT_TYPE (GstGLWindow, gst_gl_window, GST_TYPE_OBJECT);
|
|||
|
||||
static void gst_gl_window_default_send_message (GstGLWindow * window,
|
||||
GstGLWindowCB callback, gpointer data);
|
||||
static gpointer gst_gl_window_navigation_thread (GstGLWindow * window);
|
||||
void gst_gl_window_run_navigation (GstGLWindow * window);
|
||||
void gst_gl_window_open_navigation (GstGLWindow * window);
|
||||
void gst_gl_window_close_navigation (GstGLWindow * window);
|
||||
void gst_gl_window_quit_navigation (GstGLWindow * window);
|
||||
|
||||
struct _GstGLWindowPrivate
|
||||
{
|
||||
GThread *gl_thread;
|
||||
GThread *navigation_thread;
|
||||
|
||||
gboolean alive;
|
||||
};
|
||||
|
@ -128,6 +134,11 @@ gst_gl_window_init (GstGLWindow * window)
|
|||
window->priv = GST_GL_WINDOW_GET_PRIVATE (window);
|
||||
|
||||
g_mutex_init (&window->lock);
|
||||
g_mutex_init (&window->nav_lock);
|
||||
g_cond_init (&window->nav_create_cond);
|
||||
g_cond_init (&window->nav_destroy_cond);
|
||||
window->nav_created = FALSE;
|
||||
window->nav_alive = FALSE;
|
||||
window->is_drawing = FALSE;
|
||||
|
||||
g_weak_ref_init (&window->context_ref, NULL);
|
||||
|
@ -235,6 +246,17 @@ gst_gl_window_new (GstGLDisplay * display)
|
|||
|
||||
window->display = gst_object_ref (display);
|
||||
|
||||
g_mutex_lock (&window->nav_lock);
|
||||
|
||||
if (!window->nav_created) {
|
||||
window->priv->navigation_thread = g_thread_new ("gstglnavigation",
|
||||
(GThreadFunc) gst_gl_window_navigation_thread, window);
|
||||
|
||||
g_cond_wait (&window->nav_create_cond, &window->nav_lock);
|
||||
window->nav_created = TRUE;
|
||||
}
|
||||
g_mutex_unlock (&window->nav_lock);
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
|
@ -243,9 +265,22 @@ gst_gl_window_finalize (GObject * object)
|
|||
{
|
||||
GstGLWindow *window = GST_GL_WINDOW (object);
|
||||
|
||||
if (window->nav_alive) {
|
||||
g_mutex_lock (&window->nav_lock);
|
||||
GST_INFO ("send quit navigation loop");
|
||||
gst_gl_window_quit_navigation (window);
|
||||
while (window->nav_alive) {
|
||||
g_cond_wait (&window->nav_destroy_cond, &window->nav_lock);
|
||||
}
|
||||
g_mutex_unlock (&window->nav_lock);
|
||||
}
|
||||
|
||||
g_weak_ref_clear (&window->context_ref);
|
||||
|
||||
g_mutex_clear (&window->lock);
|
||||
g_mutex_clear (&window->nav_lock);
|
||||
g_cond_clear (&window->nav_create_cond);
|
||||
g_cond_clear (&window->nav_destroy_cond);
|
||||
gst_object_unref (window->display);
|
||||
|
||||
G_OBJECT_CLASS (gst_gl_window_parent_class)->finalize (object);
|
||||
|
@ -336,6 +371,21 @@ gst_gl_window_run (GstGLWindow * window)
|
|||
window_class->run (window);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_gl_window_run_navigation:
|
||||
* @window: a #GstGLWindow
|
||||
*
|
||||
* Start the execution of the navigation runloop.
|
||||
*/
|
||||
void
|
||||
gst_gl_window_run_navigation (GstGLWindow * window)
|
||||
{
|
||||
g_return_if_fail (GST_GL_IS_WINDOW (window));
|
||||
g_return_if_fail (window->navigation_context != NULL);
|
||||
g_return_if_fail (window->navigation_loop != NULL);
|
||||
g_main_loop_run (window->navigation_loop);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_gl_window_quit:
|
||||
* @window: a #GstGLWindow
|
||||
|
@ -613,7 +663,6 @@ gst_gl_window_get_surface_dimensions (GstGLWindow * window, guint * width,
|
|||
guint * height)
|
||||
{
|
||||
GstGLWindowClass *window_class;
|
||||
|
||||
g_return_if_fail (GST_GL_IS_WINDOW (window));
|
||||
window_class = GST_GL_WINDOW_GET_CLASS (window);
|
||||
g_return_if_fail (window_class->get_surface_dimensions != NULL);
|
||||
|
@ -660,6 +709,54 @@ gst_gl_dummy_window_run (GstGLWindow * window)
|
|||
g_main_loop_run (dummy->loop);
|
||||
}
|
||||
|
||||
void
|
||||
gst_gl_window_open_navigation (GstGLWindow * window)
|
||||
{
|
||||
g_return_if_fail (GST_GL_IS_WINDOW (window));
|
||||
g_mutex_lock (&window->nav_lock);
|
||||
window->navigation_context = g_main_context_new ();
|
||||
window->navigation_loop = g_main_loop_new (window->navigation_context, FALSE);
|
||||
g_main_context_push_thread_default (window->navigation_context);
|
||||
window->nav_alive = TRUE;
|
||||
g_cond_signal (&window->nav_create_cond);
|
||||
g_mutex_unlock (&window->nav_lock);
|
||||
}
|
||||
|
||||
void
|
||||
gst_gl_window_close_navigation (GstGLWindow * window)
|
||||
{
|
||||
g_return_if_fail (GST_GL_IS_WINDOW (window));
|
||||
g_return_if_fail (window->navigation_context != NULL);
|
||||
g_return_if_fail (window->navigation_loop != NULL);
|
||||
|
||||
g_mutex_lock (&window->nav_lock);
|
||||
window->nav_alive = FALSE;
|
||||
g_main_context_pop_thread_default (window->navigation_context);
|
||||
g_main_loop_unref (window->navigation_loop);
|
||||
g_main_context_unref (window->navigation_context);
|
||||
g_cond_signal (&window->nav_destroy_cond);
|
||||
g_mutex_unlock (&window->nav_lock);
|
||||
}
|
||||
|
||||
void
|
||||
gst_gl_window_quit_navigation (GstGLWindow * window)
|
||||
{
|
||||
g_return_if_fail (GST_GL_IS_WINDOW (window));
|
||||
|
||||
g_main_loop_quit (window->navigation_loop);
|
||||
}
|
||||
|
||||
static gpointer
|
||||
gst_gl_window_navigation_thread (GstGLWindow * window)
|
||||
{
|
||||
gst_gl_window_open_navigation (window);
|
||||
gst_gl_window_run_navigation (window);
|
||||
GST_INFO ("navigation loop exited\n");
|
||||
gst_gl_window_close_navigation (window);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
typedef struct _GstGLMessage
|
||||
{
|
||||
GstGLWindowCB callback;
|
||||
|
@ -797,6 +894,20 @@ gst_gl_dummy_window_new (void)
|
|||
return g_object_new (gst_gl_dummy_window_get_type (), NULL);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_gl_window_key_event_cb (gpointer data)
|
||||
{
|
||||
struct key_event *key_data = (struct key_event *) data;
|
||||
GST_DEBUG
|
||||
("%s called data struct %p window %p key %s event %s ",
|
||||
__func__, key_data, key_data->window, key_data->key_str,
|
||||
key_data->event_type);
|
||||
gst_gl_window_send_key_event (GST_GL_WINDOW (key_data->window),
|
||||
key_data->event_type, key_data->key_str);
|
||||
g_slice_free (struct key_event, key_data);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
void
|
||||
gst_gl_window_send_key_event (GstGLWindow * window, const char *event_type,
|
||||
const char *key_str)
|
||||
|
@ -805,6 +916,20 @@ gst_gl_window_send_key_event (GstGLWindow * window, const char *event_type,
|
|||
event_type, key_str);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_gl_window_mouse_event_cb (gpointer data)
|
||||
{
|
||||
struct mouse_event *mouse_data = (struct mouse_event *) data;
|
||||
GST_DEBUG ("%s called data struct %p mouse event %s button %d at %g, %g",
|
||||
__func__, mouse_data, mouse_data->event_type, mouse_data->button,
|
||||
mouse_data->posx, mouse_data->posy);
|
||||
gst_gl_window_send_mouse_event (GST_GL_WINDOW (mouse_data->window),
|
||||
mouse_data->event_type, mouse_data->button, mouse_data->posx,
|
||||
mouse_data->posy);
|
||||
g_slice_free (struct mouse_event, mouse_data);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
void
|
||||
gst_gl_window_send_mouse_event (GstGLWindow * window, const char *event_type,
|
||||
int button, double posx, double posy)
|
||||
|
|
|
@ -67,6 +67,11 @@ struct _GstGLWindow {
|
|||
GstObject parent;
|
||||
|
||||
GMutex lock;
|
||||
GMutex nav_lock;
|
||||
GCond nav_create_cond;
|
||||
GCond nav_destroy_cond;
|
||||
gboolean nav_created;
|
||||
gboolean nav_alive;
|
||||
|
||||
GstGLDisplay *display;
|
||||
GWeakRef context_ref;
|
||||
|
@ -87,6 +92,8 @@ struct _GstGLWindow {
|
|||
|
||||
/*< private >*/
|
||||
gpointer _reserved[GST_PADDING];
|
||||
GMainContext *navigation_context;
|
||||
GMainLoop *navigation_loop;
|
||||
|
||||
GstGLWindowPrivate *priv;
|
||||
};
|
||||
|
@ -129,6 +136,21 @@ struct _GstGLWindowClass {
|
|||
gpointer _reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
struct key_event
|
||||
{
|
||||
GstGLWindow *window;
|
||||
const char *event_type;
|
||||
const char *key_str;
|
||||
};
|
||||
|
||||
struct mouse_event
|
||||
{
|
||||
GstGLWindow *window;
|
||||
const char *event_type;
|
||||
int button;
|
||||
double posx;
|
||||
double posy;
|
||||
};
|
||||
/* methods */
|
||||
|
||||
GQuark gst_gl_window_error_quark (void);
|
||||
|
@ -156,6 +178,12 @@ GstGLContext * gst_gl_window_get_context (GstGLWindow *window);
|
|||
|
||||
gboolean gst_gl_window_is_running (GstGLWindow *window);
|
||||
|
||||
gboolean
|
||||
gst_gl_window_key_event_cb (gpointer data);
|
||||
|
||||
gboolean
|
||||
gst_gl_window_mouse_event_cb (gpointer data);
|
||||
|
||||
void gst_gl_window_send_key_event(GstGLWindow * window, const char * event_type,
|
||||
const char * key_str);
|
||||
void gst_gl_window_send_mouse_event(GstGLWindow * window, const char *
|
||||
|
|
|
@ -507,6 +507,9 @@ gst_gl_window_x11_handle_event (GstGLWindowX11 * window_x11)
|
|||
gboolean ret = TRUE;
|
||||
const char *key_str = NULL;
|
||||
KeySym keysym;
|
||||
struct mouse_event *mouse_data;
|
||||
struct key_event *key_data;
|
||||
XWindowAttributes attr;
|
||||
|
||||
window = GST_GL_WINDOW (window_x11);
|
||||
|
||||
|
@ -516,6 +519,10 @@ gst_gl_window_x11_handle_event (GstGLWindowX11 * window_x11)
|
|||
|
||||
/* XSendEvent (which are called in other threads) are done from another display structure */
|
||||
XNextEvent (window_x11->device, &event);
|
||||
XGetWindowAttributes (window_x11->device, window_x11->internal_win_id,
|
||||
&attr);
|
||||
window_x11->current_width = attr.width;
|
||||
window_x11->current_height = attr.height;
|
||||
|
||||
window_x11->allow_extra_expose_events = XPending (window_x11->device) <= 2;
|
||||
|
||||
|
@ -583,26 +590,44 @@ gst_gl_window_x11_handle_event (GstGLWindowX11 * window_x11)
|
|||
keysym = XkbKeycodeToKeysym (window_x11->device,
|
||||
event.xkey.keycode, 0, 0);
|
||||
key_str = XKeysymToString (keysym);
|
||||
key_data = g_slice_new (struct key_event);
|
||||
key_data->window = window;
|
||||
key_data->key_str = XKeysymToString (keysym);
|
||||
key_data->event_type =
|
||||
event.type == KeyPress ? "key-press" : "key-release";
|
||||
GST_DEBUG ("input event key %d pressed over window at %d,%d (%s)",
|
||||
event.xkey.keycode, event.xkey.x, event.xkey.y, key_str);
|
||||
gst_gl_window_send_key_event (window,
|
||||
event.type == KeyPress ? "key-press" : "key-release", key_str);
|
||||
g_main_context_invoke (window->navigation_context,
|
||||
(GSourceFunc) gst_gl_window_key_event_cb, key_data);
|
||||
break;
|
||||
case ButtonPress:
|
||||
case ButtonRelease:
|
||||
GST_DEBUG ("input event mouse button %d pressed over window at %d,%d",
|
||||
event.xbutton.button, event.xbutton.x, event.xbutton.y);
|
||||
gst_gl_window_send_mouse_event (window,
|
||||
mouse_data = g_slice_new (struct mouse_event);
|
||||
mouse_data->window = window;
|
||||
mouse_data->event_type =
|
||||
event.type ==
|
||||
ButtonPress ? "mouse-button-press" : "mouse-button-release",
|
||||
event.xbutton.button, (double) event.xbutton.x,
|
||||
(double) event.xbutton.y);
|
||||
ButtonPress ? "mouse-button-press" : "mouse-button-release";
|
||||
mouse_data->button = event.xbutton.button;
|
||||
mouse_data->posx = (double) event.xbutton.x;
|
||||
mouse_data->posy = (double) event.xbutton.y;
|
||||
|
||||
g_main_context_invoke (window->navigation_context,
|
||||
(GSourceFunc) gst_gl_window_mouse_event_cb, mouse_data);
|
||||
break;
|
||||
case MotionNotify:
|
||||
GST_DEBUG ("input event pointer moved over window at %d,%d",
|
||||
event.xmotion.x, event.xmotion.y);
|
||||
gst_gl_window_send_mouse_event (window, "mouse-move", 0,
|
||||
(double) event.xmotion.x, (double) event.xmotion.y);
|
||||
mouse_data = g_slice_new (struct mouse_event);
|
||||
mouse_data->window = window;
|
||||
mouse_data->event_type = "mouse-move";
|
||||
mouse_data->button = 0;
|
||||
mouse_data->posx = (double) event.xbutton.x;
|
||||
mouse_data->posy = (double) event.xbutton.y;
|
||||
|
||||
g_main_context_invoke (window->navigation_context, (GSourceFunc)
|
||||
gst_gl_window_mouse_event_cb, mouse_data);
|
||||
break;
|
||||
default:
|
||||
GST_DEBUG ("unknown XEvent type: %u", event.type);
|
||||
|
@ -715,10 +740,8 @@ gst_gl_window_x11_get_surface_dimensions (GstGLWindow * window, guint * width,
|
|||
guint * height)
|
||||
{
|
||||
GstGLWindowX11 *window_x11 = GST_GL_WINDOW_X11 (window);
|
||||
XWindowAttributes attr;
|
||||
XGetWindowAttributes (window_x11->device, window_x11->internal_win_id, &attr);
|
||||
if (width != NULL)
|
||||
*width = attr.width;
|
||||
*width = window_x11->current_width;
|
||||
if (height != NULL)
|
||||
*height = attr.height;
|
||||
*height = window_x11->current_height;
|
||||
}
|
||||
|
|
|
@ -64,6 +64,8 @@ struct _GstGLWindowX11
|
|||
gint depth;
|
||||
gint device_width;
|
||||
gint device_height;
|
||||
gint current_width;
|
||||
gint current_height;
|
||||
gint connection;
|
||||
XVisualInfo *visual_info;
|
||||
Window parent_win;
|
||||
|
|
Loading…
Reference in a new issue