From 3c3b78508fb7f283606db661c4e838daa4b3d865 Mon Sep 17 00:00:00 2001 From: Vasilis Liaskovitis Date: Tue, 9 Sep 2014 12:01:47 +0200 Subject: [PATCH] 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) --- gst-libs/gst/gl/gstglwindow.c | 127 +++++++++++++++++++++++++++++++++- gst-libs/gst/gl/gstglwindow.h | 28 ++++++++ gst-libs/gst/gl/x11/gstglwindow_x11.c | 47 +++++++++---- gst-libs/gst/gl/x11/gstglwindow_x11.h | 2 + 4 files changed, 191 insertions(+), 13 deletions(-) diff --git a/gst-libs/gst/gl/gstglwindow.c b/gst-libs/gst/gl/gstglwindow.c index 6732a14..d3d9c45 100644 --- a/gst-libs/gst/gl/gstglwindow.c +++ b/gst-libs/gst/gl/gstglwindow.c @@ -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) diff --git a/gst-libs/gst/gl/gstglwindow.h b/gst-libs/gst/gl/gstglwindow.h index 2337849..6f244ea 100644 --- a/gst-libs/gst/gl/gstglwindow.h +++ b/gst-libs/gst/gl/gstglwindow.h @@ -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 * diff --git a/gst-libs/gst/gl/x11/gstglwindow_x11.c b/gst-libs/gst/gl/x11/gstglwindow_x11.c index b654a04..84c71af 100644 --- a/gst-libs/gst/gl/x11/gstglwindow_x11.c +++ b/gst-libs/gst/gl/x11/gstglwindow_x11.c @@ -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; } diff --git a/gst-libs/gst/gl/x11/gstglwindow_x11.h b/gst-libs/gst/gl/x11/gstglwindow_x11.h index 02a3aaa..56e876d 100644 --- a/gst-libs/gst/gl/x11/gstglwindow_x11.h +++ b/gst-libs/gst/gl/x11/gstglwindow_x11.h @@ -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; -- 2.7.4