GstGLWindow: Introduce navigation thread
authorVasilis Liaskovitis <vliaskov@gmail.com>
Tue, 9 Sep 2014 10:01:47 +0000 (12:01 +0200)
committerMatthew Waters <ystreet00@gmail.com>
Tue, 9 Sep 2014 11:47:02 +0000 (21:47 +1000)
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)

gst-libs/gst/gl/gstglwindow.c
gst-libs/gst/gl/gstglwindow.h
gst-libs/gst/gl/x11/gstglwindow_x11.c
gst-libs/gst/gl/x11/gstglwindow_x11.h

index 6732a14..d3d9c45 100644 (file)
@@ -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);
@@ -337,6 +372,21 @@ gst_gl_window_run (GstGLWindow * 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)
index 2337849..6f244ea 100644 (file)
@@ -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 *
index b654a04..84c71af 100644 (file)
@@ -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;
 }
index 02a3aaa..56e876d 100644 (file)
@@ -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;