mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 20:21:24 +00:00
306e610547
saves having every caller do it themselves.
1199 lines
31 KiB
C
1199 lines
31 KiB
C
/*
|
|
* GStreamer
|
|
* Copyright (C) 2012 Matthew Waters <ystreet00@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., 51 Franklin St, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
/**
|
|
* SECTION:gstglwindow
|
|
* @short_description: window/surface abstraction
|
|
* @title: GstGLWindow
|
|
* @see_also: #GstGLContext, #GstGLDisplay
|
|
*
|
|
* GstGLWindow represents a window that elements can render into. A window can
|
|
* either be a user visible window (onscreen) or hidden (offscreen).
|
|
*/
|
|
|
|
#if HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include <gmodule.h>
|
|
#include <stdio.h>
|
|
|
|
#include "gl.h"
|
|
#include "gstglwindow.h"
|
|
|
|
/* FIXME make this work with windowless contexts */
|
|
|
|
#if GST_GL_HAVE_WINDOW_X11
|
|
#include "x11/gstglwindow_x11.h"
|
|
#endif
|
|
#if GST_GL_HAVE_WINDOW_WIN32
|
|
#include "win32/gstglwindow_win32.h"
|
|
#endif
|
|
#if GST_GL_HAVE_WINDOW_COCOA
|
|
#include "cocoa/gstglwindow_cocoa.h"
|
|
#endif
|
|
#if GST_GL_HAVE_WINDOW_WAYLAND
|
|
#include "wayland/gstglwindow_wayland_egl.h"
|
|
#endif
|
|
#if GST_GL_HAVE_WINDOW_ANDROID
|
|
#include "android/gstglwindow_android_egl.h"
|
|
#endif
|
|
#if GST_GL_HAVE_WINDOW_EAGL
|
|
#include "eagl/gstglwindow_eagl.h"
|
|
#endif
|
|
#if GST_GL_HAVE_WINDOW_DISPMANX
|
|
#include "dispmanx/gstglwindow_dispmanx_egl.h"
|
|
#endif
|
|
|
|
#define USING_OPENGL(context) (gst_gl_context_check_gl_version (context, GST_GL_API_OPENGL, 1, 0))
|
|
#define USING_OPENGL3(context) (gst_gl_context_check_gl_version (context, GST_GL_API_OPENGL3, 3, 1))
|
|
#define USING_GLES(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES, 1, 0))
|
|
#define USING_GLES2(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES2, 2, 0))
|
|
#define USING_GLES3(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES2, 3, 0))
|
|
|
|
#define GST_CAT_DEFAULT gst_gl_window_debug
|
|
GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
|
|
|
|
#define gst_gl_window_parent_class parent_class
|
|
G_DEFINE_ABSTRACT_TYPE (GstGLWindow, gst_gl_window, GST_TYPE_OBJECT);
|
|
|
|
#define GST_GL_WINDOW_GET_PRIVATE(o) \
|
|
(G_TYPE_INSTANCE_GET_PRIVATE((o), GST_GL_TYPE_WINDOW, GstGLWindowPrivate))
|
|
|
|
static void gst_gl_window_default_draw (GstGLWindow * window);
|
|
static void gst_gl_window_default_run (GstGLWindow * window);
|
|
static void gst_gl_window_default_quit (GstGLWindow * window);
|
|
static void gst_gl_window_default_send_message (GstGLWindow * window,
|
|
GstGLWindowCB callback, gpointer data);
|
|
static void gst_gl_window_default_send_message_async (GstGLWindow * window,
|
|
GstGLWindowCB callback, gpointer data, GDestroyNotify destroy);
|
|
static gpointer gst_gl_window_navigation_thread (GstGLWindow * window);
|
|
|
|
struct _GstGLWindowPrivate
|
|
{
|
|
GMainContext *main_context;
|
|
GMainLoop *loop;
|
|
|
|
guint surface_width;
|
|
guint surface_height;
|
|
|
|
gboolean alive;
|
|
|
|
GThread *navigation_thread;
|
|
GMainContext *navigation_context;
|
|
GMainLoop *navigation_loop;
|
|
GMutex nav_lock;
|
|
GCond nav_create_cond;
|
|
gboolean nav_alive;
|
|
GMutex sync_message_lock;
|
|
GCond sync_message_cond;
|
|
};
|
|
|
|
static void gst_gl_window_finalize (GObject * object);
|
|
|
|
typedef struct _GstGLDummyWindow
|
|
{
|
|
GstGLWindow parent;
|
|
|
|
guintptr handle;
|
|
} GstGLDummyWindow;
|
|
|
|
typedef struct _GstGLDummyWindowCass
|
|
{
|
|
GstGLWindowClass parent;
|
|
} GstGLDummyWindowClass;
|
|
|
|
GstGLDummyWindow *gst_gl_dummy_window_new (void);
|
|
|
|
enum
|
|
{
|
|
SIGNAL_0,
|
|
EVENT_MOUSE_SIGNAL,
|
|
EVENT_KEY_SIGNAL,
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
static guint gst_gl_window_signals[LAST_SIGNAL] = { 0 };
|
|
|
|
GQuark
|
|
gst_gl_window_error_quark (void)
|
|
{
|
|
return g_quark_from_static_string ("gst-gl-window-error-quark");
|
|
}
|
|
|
|
static gboolean
|
|
gst_gl_window_default_open (GstGLWindow * window, GError ** error)
|
|
{
|
|
GstGLWindowPrivate *priv = window->priv;
|
|
|
|
if (g_main_context_get_thread_default ()) {
|
|
if (priv->main_context)
|
|
g_main_context_unref (priv->main_context);
|
|
if (priv->loop)
|
|
g_main_loop_unref (priv->loop);
|
|
priv->main_context = g_main_context_ref_thread_default ();
|
|
priv->loop = NULL;
|
|
priv->alive = TRUE;
|
|
} else {
|
|
g_main_context_push_thread_default (priv->main_context);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gst_gl_window_default_close (GstGLWindow * window)
|
|
{
|
|
GstGLWindowPrivate *priv = window->priv;
|
|
|
|
if (!priv->loop) {
|
|
priv->alive = FALSE;
|
|
g_main_context_unref (priv->main_context);
|
|
priv->main_context = NULL;
|
|
} else {
|
|
g_main_context_pop_thread_default (priv->main_context);
|
|
}
|
|
}
|
|
|
|
static void
|
|
_init_debug (void)
|
|
{
|
|
static volatile gsize _init = 0;
|
|
|
|
if (g_once_init_enter (&_init)) {
|
|
GST_DEBUG_CATEGORY_INIT (gst_gl_window_debug, "glwindow", 0,
|
|
"glwindow element");
|
|
g_once_init_leave (&_init, 1);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_gl_window_init (GstGLWindow * window)
|
|
{
|
|
GstGLWindowPrivate *priv = GST_GL_WINDOW_GET_PRIVATE (window);
|
|
window->priv = priv;
|
|
|
|
g_mutex_init (&window->lock);
|
|
window->is_drawing = FALSE;
|
|
|
|
g_mutex_init (&window->priv->nav_lock);
|
|
g_cond_init (&window->priv->nav_create_cond);
|
|
window->priv->nav_alive = FALSE;
|
|
|
|
g_weak_ref_init (&window->context_ref, NULL);
|
|
|
|
g_mutex_init (&window->priv->sync_message_lock);
|
|
g_cond_init (&window->priv->sync_message_cond);
|
|
|
|
priv->main_context = g_main_context_new ();
|
|
priv->loop = g_main_loop_new (priv->main_context, FALSE);
|
|
priv->navigation_loop = NULL;
|
|
}
|
|
|
|
static void
|
|
gst_gl_window_class_init (GstGLWindowClass * klass)
|
|
{
|
|
g_type_class_add_private (klass, sizeof (GstGLWindowPrivate));
|
|
|
|
klass->open = GST_DEBUG_FUNCPTR (gst_gl_window_default_open);
|
|
klass->close = GST_DEBUG_FUNCPTR (gst_gl_window_default_close);
|
|
klass->run = GST_DEBUG_FUNCPTR (gst_gl_window_default_run);
|
|
klass->quit = GST_DEBUG_FUNCPTR (gst_gl_window_default_quit);
|
|
klass->draw = GST_DEBUG_FUNCPTR (gst_gl_window_default_draw);
|
|
klass->send_message = GST_DEBUG_FUNCPTR (gst_gl_window_default_send_message);
|
|
klass->send_message_async =
|
|
GST_DEBUG_FUNCPTR (gst_gl_window_default_send_message_async);
|
|
|
|
G_OBJECT_CLASS (klass)->finalize = gst_gl_window_finalize;
|
|
|
|
/**
|
|
* GstGLWindow::mouse-event:
|
|
* @object: the #GstGLWindow
|
|
* @id: the name of the event
|
|
* @button: the id of the button
|
|
* @x: the x coordinate of the mouse event
|
|
* @y: the y coordinate of the mouse event
|
|
*
|
|
* Will be emitted when a mouse event is received by the GstGLwindow.
|
|
*
|
|
* Since: 1.6
|
|
*/
|
|
gst_gl_window_signals[EVENT_MOUSE_SIGNAL] =
|
|
g_signal_new ("mouse-event", G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic,
|
|
G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_INT, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
|
|
|
|
/**
|
|
* GstGLWindow::key-event:
|
|
* @object: the #GstGLWindow
|
|
* @id: the name of the event
|
|
* @key: the id of the key pressed
|
|
*
|
|
* Will be emitted when a key event is received by the GstGLwindow.
|
|
*
|
|
* Since: 1.6
|
|
*/
|
|
gst_gl_window_signals[EVENT_KEY_SIGNAL] =
|
|
g_signal_new ("key-event", G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic,
|
|
G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
|
|
|
|
_init_debug ();
|
|
}
|
|
|
|
/**
|
|
* gst_gl_window_new:
|
|
* @display: a #GstGLDisplay
|
|
*
|
|
* Returns: (transfer full): a new #GstGLWindow using @display's connection
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
GstGLWindow *
|
|
gst_gl_window_new (GstGLDisplay * display)
|
|
{
|
|
GstGLWindow *window = NULL;
|
|
const gchar *user_choice;
|
|
|
|
g_return_val_if_fail (display != NULL, NULL);
|
|
|
|
_init_debug ();
|
|
|
|
user_choice = g_getenv ("GST_GL_WINDOW");
|
|
GST_INFO ("creating a window, user choice:%s", user_choice);
|
|
|
|
#if GST_GL_HAVE_WINDOW_COCOA
|
|
if (!window && (!user_choice || g_strstr_len (user_choice, 5, "cocoa")))
|
|
window = GST_GL_WINDOW (gst_gl_window_cocoa_new (display));
|
|
#endif
|
|
#if GST_GL_HAVE_WINDOW_X11
|
|
if (!window && (!user_choice || g_strstr_len (user_choice, 3, "x11")))
|
|
window = GST_GL_WINDOW (gst_gl_window_x11_new (display));
|
|
#endif
|
|
#if GST_GL_HAVE_WINDOW_WIN32
|
|
if (!window && (!user_choice || g_strstr_len (user_choice, 5, "win32")))
|
|
window = GST_GL_WINDOW (gst_gl_window_win32_new (display));
|
|
#endif
|
|
#if GST_GL_HAVE_WINDOW_WAYLAND
|
|
if (!window && (!user_choice || g_strstr_len (user_choice, 7, "wayland")))
|
|
window = GST_GL_WINDOW (gst_gl_window_wayland_egl_new (display));
|
|
#endif
|
|
#if GST_GL_HAVE_WINDOW_DISPMANX
|
|
if (!window && (!user_choice || g_strstr_len (user_choice, 8, "dispmanx")))
|
|
window = GST_GL_WINDOW (gst_gl_window_dispmanx_egl_new (display));
|
|
#endif
|
|
#if GST_GL_HAVE_WINDOW_ANDROID
|
|
if (!window && (!user_choice || g_strstr_len (user_choice, 7, "android")))
|
|
window = GST_GL_WINDOW (gst_gl_window_android_egl_new (display));
|
|
#endif
|
|
#if GST_GL_HAVE_WINDOW_EAGL
|
|
if (!window && (!user_choice || g_strstr_len (user_choice, 4, "eagl")))
|
|
window = GST_GL_WINDOW (gst_gl_window_eagl_new (display));
|
|
#endif
|
|
if (!window) {
|
|
/* subclass returned a NULL window */
|
|
GST_WARNING ("Could not create window. user specified %s, creating dummy"
|
|
" window", user_choice ? user_choice : "(null)");
|
|
|
|
window = GST_GL_WINDOW (gst_gl_dummy_window_new ());
|
|
}
|
|
|
|
window->display = gst_object_ref (display);
|
|
|
|
g_mutex_lock (&window->priv->nav_lock);
|
|
window->priv->navigation_thread = g_thread_new ("gstglnavigation",
|
|
(GThreadFunc) gst_gl_window_navigation_thread, window);
|
|
|
|
while (!window->priv->nav_alive)
|
|
g_cond_wait (&window->priv->nav_create_cond, &window->priv->nav_lock);
|
|
g_mutex_unlock (&window->priv->nav_lock);
|
|
|
|
return window;
|
|
}
|
|
|
|
static void
|
|
gst_gl_window_finalize (GObject * object)
|
|
{
|
|
GstGLWindow *window = GST_GL_WINDOW (object);
|
|
GstGLWindowPrivate *priv = window->priv;
|
|
|
|
GST_INFO ("quit navigation loop");
|
|
if (window->priv->navigation_loop) {
|
|
g_main_loop_quit (window->priv->navigation_loop);
|
|
/* wait until navigation thread finished */
|
|
g_thread_join (window->priv->navigation_thread);
|
|
window->priv->navigation_thread = NULL;
|
|
}
|
|
|
|
if (priv->loop)
|
|
g_main_loop_unref (priv->loop);
|
|
|
|
if (priv->main_context)
|
|
g_main_context_unref (priv->main_context);
|
|
|
|
g_weak_ref_clear (&window->context_ref);
|
|
|
|
g_mutex_clear (&window->lock);
|
|
g_mutex_clear (&window->priv->nav_lock);
|
|
g_cond_clear (&window->priv->nav_create_cond);
|
|
g_mutex_clear (&window->priv->sync_message_lock);
|
|
g_cond_clear (&window->priv->sync_message_cond);
|
|
gst_object_unref (window->display);
|
|
|
|
G_OBJECT_CLASS (gst_gl_window_parent_class)->finalize (object);
|
|
}
|
|
|
|
typedef struct _GstSetWindowHandleCb
|
|
{
|
|
GstGLWindow *window;
|
|
guintptr handle;
|
|
} GstSetWindowHandleCb;
|
|
|
|
static void
|
|
_set_window_handle_cb (GstSetWindowHandleCb * data)
|
|
{
|
|
GstGLContext *context = gst_gl_window_get_context (data->window);
|
|
GstGLWindowClass *window_class = GST_GL_WINDOW_GET_CLASS (data->window);
|
|
GThread *thread = NULL;
|
|
|
|
/* deactivate if necessary */
|
|
if (context) {
|
|
thread = gst_gl_context_get_thread (context);
|
|
if (thread) {
|
|
/* This is only thread safe iff the context thread == g_thread_self() */
|
|
g_assert (thread == g_thread_self ());
|
|
gst_gl_context_activate (context, FALSE);
|
|
}
|
|
}
|
|
|
|
window_class->set_window_handle (data->window, data->handle);
|
|
|
|
/* reactivate */
|
|
if (context && thread)
|
|
gst_gl_context_activate (context, TRUE);
|
|
|
|
if (context)
|
|
gst_object_unref (context);
|
|
if (thread)
|
|
g_thread_unref (thread);
|
|
}
|
|
|
|
static void
|
|
_free_swh_cb (GstSetWindowHandleCb * data)
|
|
{
|
|
gst_object_unref (data->window);
|
|
g_slice_free (GstSetWindowHandleCb, data);
|
|
}
|
|
|
|
/**
|
|
* gst_gl_window_set_window_handle:
|
|
* @window: a #GstGLWindow
|
|
* @handle: handle to the window
|
|
*
|
|
* Sets the window that this @window should render into. Some implementations
|
|
* require this to be called with a valid handle before drawing can commence.
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
gst_gl_window_set_window_handle (GstGLWindow * window, guintptr handle)
|
|
{
|
|
GstGLWindowClass *window_class;
|
|
GstSetWindowHandleCb *data;
|
|
|
|
g_return_if_fail (GST_IS_GL_WINDOW (window));
|
|
g_return_if_fail (handle != 0);
|
|
window_class = GST_GL_WINDOW_GET_CLASS (window);
|
|
g_return_if_fail (window_class->set_window_handle != NULL);
|
|
|
|
data = g_slice_new (GstSetWindowHandleCb);
|
|
data->window = gst_object_ref (window);
|
|
data->handle = handle;
|
|
|
|
/* FIXME: Move to a message which deactivates, calls implementation, activates */
|
|
gst_gl_window_send_message_async (window,
|
|
(GstGLWindowCB) _set_window_handle_cb, data,
|
|
(GDestroyNotify) _free_swh_cb);
|
|
|
|
/* window_class->set_window_handle (window, handle); */
|
|
}
|
|
|
|
static void
|
|
draw_cb (gpointer data)
|
|
{
|
|
GstGLWindow *window = GST_GL_WINDOW (data);
|
|
GstGLContext *context = gst_gl_window_get_context (window);
|
|
GstGLContextClass *context_class = GST_GL_CONTEXT_GET_CLASS (context);
|
|
|
|
if (window->queue_resize) {
|
|
guint width, height;
|
|
|
|
gst_gl_window_get_surface_dimensions (window, &width, &height);
|
|
gst_gl_window_resize (window, width, height);
|
|
}
|
|
|
|
if (window->draw)
|
|
window->draw (window->draw_data);
|
|
|
|
if (context_class->swap_buffers)
|
|
context_class->swap_buffers (context);
|
|
|
|
gst_object_unref (context);
|
|
}
|
|
|
|
static void
|
|
gst_gl_window_default_draw (GstGLWindow * window)
|
|
{
|
|
gst_gl_window_send_message (window, (GstGLWindowCB) draw_cb, window);
|
|
}
|
|
|
|
/**
|
|
* gst_gl_window_draw:
|
|
* @window: a #GstGLWindow
|
|
*
|
|
* Redraw the window contents. Implementations should invoke the draw callback.
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
gst_gl_window_draw (GstGLWindow * window)
|
|
{
|
|
GstGLWindowClass *window_class;
|
|
|
|
g_return_if_fail (GST_IS_GL_WINDOW (window));
|
|
window_class = GST_GL_WINDOW_GET_CLASS (window);
|
|
g_return_if_fail (window_class->draw != NULL);
|
|
|
|
/* avoid to overload the drawer */
|
|
if (window->is_drawing) {
|
|
return;
|
|
}
|
|
|
|
window_class->draw (window);
|
|
}
|
|
|
|
/**
|
|
* gst_gl_window_set_preferred_size:
|
|
* @window: a #GstGLWindow
|
|
* @width: new preferred width
|
|
* @height: new preferred height
|
|
*
|
|
* Set the preferred width and height of the window. Implementations are free
|
|
* to ignore this information.
|
|
*
|
|
* Since: 1.6
|
|
*/
|
|
void
|
|
gst_gl_window_set_preferred_size (GstGLWindow * window, gint width, gint height)
|
|
{
|
|
GstGLWindowClass *window_class;
|
|
|
|
g_return_if_fail (GST_IS_GL_WINDOW (window));
|
|
window_class = GST_GL_WINDOW_GET_CLASS (window);
|
|
|
|
if (window_class->set_preferred_size)
|
|
window_class->set_preferred_size (window, width, height);
|
|
}
|
|
|
|
/**
|
|
* gst_gl_window_show:
|
|
* @window: a #GstGLWindow
|
|
*
|
|
* Present the window to the screen.
|
|
*
|
|
* Since: 1.6
|
|
*/
|
|
void
|
|
gst_gl_window_show (GstGLWindow * window)
|
|
{
|
|
GstGLWindowClass *window_class;
|
|
|
|
g_return_if_fail (GST_IS_GL_WINDOW (window));
|
|
window_class = GST_GL_WINDOW_GET_CLASS (window);
|
|
|
|
if (window_class->show)
|
|
window_class->show (window);
|
|
}
|
|
|
|
static void
|
|
gst_gl_window_default_run (GstGLWindow * window)
|
|
{
|
|
g_main_loop_run (window->priv->loop);
|
|
}
|
|
|
|
/**
|
|
* gst_gl_window_run:
|
|
* @window: a #GstGLWindow
|
|
*
|
|
* Start the execution of the runloop.
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
gst_gl_window_run (GstGLWindow * window)
|
|
{
|
|
GstGLWindowClass *window_class;
|
|
|
|
g_return_if_fail (GST_IS_GL_WINDOW (window));
|
|
window_class = GST_GL_WINDOW_GET_CLASS (window);
|
|
g_return_if_fail (window_class->run != NULL);
|
|
|
|
window->priv->alive = TRUE;
|
|
window_class->run (window);
|
|
}
|
|
|
|
static void
|
|
gst_gl_window_default_quit (GstGLWindow * window)
|
|
{
|
|
g_main_loop_quit (window->priv->loop);
|
|
}
|
|
|
|
/**
|
|
* gst_gl_window_quit:
|
|
* @window: a #GstGLWindow
|
|
*
|
|
* Quit the runloop's execution.
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
gst_gl_window_quit (GstGLWindow * window)
|
|
{
|
|
GstGLWindowClass *window_class;
|
|
|
|
g_return_if_fail (GST_IS_GL_WINDOW (window));
|
|
window_class = GST_GL_WINDOW_GET_CLASS (window);
|
|
g_return_if_fail (window_class->quit != NULL);
|
|
|
|
GST_GL_WINDOW_LOCK (window);
|
|
|
|
window->priv->alive = FALSE;
|
|
|
|
window_class->quit (window);
|
|
|
|
GST_INFO ("quit sent to gl window loop");
|
|
|
|
GST_GL_WINDOW_UNLOCK (window);
|
|
}
|
|
|
|
typedef struct _GstGLSyncMessage
|
|
{
|
|
GstGLWindow *window;
|
|
gboolean fired;
|
|
|
|
GstGLWindowCB callback;
|
|
gpointer data;
|
|
} GstGLSyncMessage;
|
|
|
|
static void
|
|
_run_message_sync (GstGLSyncMessage * message)
|
|
{
|
|
|
|
if (message->callback)
|
|
message->callback (message->data);
|
|
|
|
g_mutex_lock (&message->window->priv->sync_message_lock);
|
|
message->fired = TRUE;
|
|
g_cond_broadcast (&message->window->priv->sync_message_cond);
|
|
g_mutex_unlock (&message->window->priv->sync_message_lock);
|
|
}
|
|
|
|
void
|
|
gst_gl_window_default_send_message (GstGLWindow * window,
|
|
GstGLWindowCB callback, gpointer data)
|
|
{
|
|
GstGLSyncMessage message;
|
|
|
|
message.window = window;
|
|
message.callback = callback;
|
|
message.data = data;
|
|
message.fired = FALSE;
|
|
|
|
gst_gl_window_send_message_async (window, (GstGLWindowCB) _run_message_sync,
|
|
&message, NULL);
|
|
|
|
g_mutex_lock (&window->priv->sync_message_lock);
|
|
|
|
/* block until opengl calls have been executed in the gl thread */
|
|
while (!message.fired)
|
|
g_cond_wait (&window->priv->sync_message_cond,
|
|
&window->priv->sync_message_lock);
|
|
g_mutex_unlock (&window->priv->sync_message_lock);
|
|
}
|
|
|
|
/**
|
|
* gst_gl_window_send_message:
|
|
* @window: a #GstGLWindow
|
|
* @callback: (scope async): function to invoke
|
|
* @data: (closure): data to invoke @callback with
|
|
*
|
|
* Invoke @callback with data on the window thread. @callback is guarenteed to
|
|
* have executed when this function returns.
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
gst_gl_window_send_message (GstGLWindow * window, GstGLWindowCB callback,
|
|
gpointer data)
|
|
{
|
|
GstGLWindowClass *window_class;
|
|
|
|
g_return_if_fail (GST_IS_GL_WINDOW (window));
|
|
g_return_if_fail (callback != NULL);
|
|
window_class = GST_GL_WINDOW_GET_CLASS (window);
|
|
g_return_if_fail (window_class->send_message != NULL);
|
|
|
|
window_class->send_message (window, callback, data);
|
|
}
|
|
|
|
typedef struct _GstGLAsyncMessage
|
|
{
|
|
GstGLWindowCB callback;
|
|
gpointer data;
|
|
GDestroyNotify destroy;
|
|
} GstGLAsyncMessage;
|
|
|
|
static gboolean
|
|
_run_message_async (GstGLAsyncMessage * message)
|
|
{
|
|
if (message->callback)
|
|
message->callback (message->data);
|
|
|
|
if (message->destroy)
|
|
message->destroy (message->data);
|
|
|
|
g_slice_free (GstGLAsyncMessage, message);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
gst_gl_window_default_send_message_async (GstGLWindow * window,
|
|
GstGLWindowCB callback, gpointer data, GDestroyNotify destroy)
|
|
{
|
|
GstGLWindowPrivate *priv = window->priv;
|
|
GstGLAsyncMessage *message = g_slice_new (GstGLAsyncMessage);
|
|
|
|
message->callback = callback;
|
|
message->data = data;
|
|
message->destroy = destroy;
|
|
|
|
g_main_context_invoke (priv->main_context, (GSourceFunc) _run_message_async,
|
|
message);
|
|
}
|
|
|
|
/**
|
|
* gst_gl_window_send_message_async:
|
|
* @window: a #GstGLWindow
|
|
* @callback: (scope async): function to invoke
|
|
* @data: (closure): data to invoke @callback with
|
|
* @destroy: (destroy): called when @data is not needed anymore
|
|
*
|
|
* Invoke @callback with @data on the window thread. The callback may not
|
|
* have been executed when this function returns.
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
gst_gl_window_send_message_async (GstGLWindow * window, GstGLWindowCB callback,
|
|
gpointer data, GDestroyNotify destroy)
|
|
{
|
|
GstGLWindowClass *window_class;
|
|
|
|
g_return_if_fail (GST_IS_GL_WINDOW (window));
|
|
g_return_if_fail (callback != NULL);
|
|
window_class = GST_GL_WINDOW_GET_CLASS (window);
|
|
g_return_if_fail (window_class->send_message_async != NULL);
|
|
|
|
window_class->send_message_async (window, callback, data, destroy);
|
|
}
|
|
|
|
/**
|
|
* gst_gl_window_set_draw_callback:
|
|
* @window: a #GstGLWindow
|
|
* @callback: (scope notified): function to invoke
|
|
* @data: (closure): data to invoke @callback with
|
|
* @destroy_notify: (destroy): called when @data is not needed any more
|
|
*
|
|
* Sets the draw callback called everytime gst_gl_window_draw() is called
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
gst_gl_window_set_draw_callback (GstGLWindow * window, GstGLWindowCB callback,
|
|
gpointer data, GDestroyNotify destroy_notify)
|
|
{
|
|
g_return_if_fail (GST_IS_GL_WINDOW (window));
|
|
|
|
GST_GL_WINDOW_LOCK (window);
|
|
|
|
if (window->draw_notify)
|
|
window->draw_notify (window->draw_data);
|
|
|
|
window->draw = callback;
|
|
window->draw_data = data;
|
|
window->draw_notify = destroy_notify;
|
|
|
|
GST_GL_WINDOW_UNLOCK (window);
|
|
}
|
|
|
|
/**
|
|
* gst_gl_window_set_resize_callback:
|
|
* @window: a #GstGLWindow
|
|
* @callback: (scope notified): function to invoke
|
|
* @data: (closure): data to invoke @callback with
|
|
* @destroy_notify: (destroy): called when @data is not needed any more
|
|
*
|
|
* Sets the resize callback called everytime a resize of the window occurs.
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
gst_gl_window_set_resize_callback (GstGLWindow * window,
|
|
GstGLWindowResizeCB callback, gpointer data, GDestroyNotify destroy_notify)
|
|
{
|
|
g_return_if_fail (GST_IS_GL_WINDOW (window));
|
|
|
|
GST_GL_WINDOW_LOCK (window);
|
|
|
|
if (window->resize_notify)
|
|
window->resize_notify (window->resize_data);
|
|
|
|
window->resize = callback;
|
|
window->resize_data = data;
|
|
window->resize_notify = destroy_notify;
|
|
|
|
GST_GL_WINDOW_UNLOCK (window);
|
|
}
|
|
|
|
/**
|
|
* gst_gl_window_set_close_callback:
|
|
* @window: a #GstGLWindow
|
|
* @callback: (scope notified): function to invoke
|
|
* @data: (closure): data to invoke @callback with
|
|
* @destroy_notify: (destroy): called when @data is not needed any more
|
|
*
|
|
* Sets the callback called when the window is about to close.
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
gst_gl_window_set_close_callback (GstGLWindow * window, GstGLWindowCB callback,
|
|
gpointer data, GDestroyNotify destroy_notify)
|
|
{
|
|
g_return_if_fail (GST_IS_GL_WINDOW (window));
|
|
|
|
GST_GL_WINDOW_LOCK (window);
|
|
|
|
if (window->close_notify)
|
|
window->close_notify (window->close_data);
|
|
|
|
window->close = callback;
|
|
window->close_data = data;
|
|
window->close_notify = destroy_notify;
|
|
|
|
GST_GL_WINDOW_UNLOCK (window);
|
|
}
|
|
|
|
/**
|
|
* gst_gl_window_is_running:
|
|
* @window: a #GstGLWindow
|
|
*
|
|
* Whether the runloop is running
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
gboolean
|
|
gst_gl_window_is_running (GstGLWindow * window)
|
|
{
|
|
GstGLWindowPrivate *priv = window->priv;
|
|
return priv->alive && (!priv->loop
|
|
|| g_main_loop_is_running (window->priv->loop));
|
|
}
|
|
|
|
/**
|
|
* gst_gl_window_get_display:
|
|
* @window: a #GstGLWindow
|
|
*
|
|
* Returns: the windowing system display handle for this @window
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
guintptr
|
|
gst_gl_window_get_display (GstGLWindow * window)
|
|
{
|
|
GstGLWindowClass *window_class;
|
|
|
|
g_return_val_if_fail (GST_IS_GL_WINDOW (window), 0);
|
|
window_class = GST_GL_WINDOW_GET_CLASS (window);
|
|
g_return_val_if_fail (window_class->get_display != NULL, 0);
|
|
|
|
return window_class->get_display (window);
|
|
}
|
|
|
|
/**
|
|
* gst_gl_window_get_window_handle:
|
|
* @window: a #GstGLWindow
|
|
*
|
|
* Returns: the window handle we are currently rendering into
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
guintptr
|
|
gst_gl_window_get_window_handle (GstGLWindow * window)
|
|
{
|
|
GstGLWindowClass *window_class;
|
|
|
|
g_return_val_if_fail (GST_IS_GL_WINDOW (window), 0);
|
|
window_class = GST_GL_WINDOW_GET_CLASS (window);
|
|
g_return_val_if_fail (window_class->get_window_handle != NULL, 0);
|
|
|
|
return window_class->get_window_handle (window);
|
|
}
|
|
|
|
/**
|
|
* gst_gl_window_get_context:
|
|
* @window: a #GstGLWindow
|
|
*
|
|
* Returns: (transfer full): the #GstGLContext associated with this @window
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
GstGLContext *
|
|
gst_gl_window_get_context (GstGLWindow * window)
|
|
{
|
|
g_return_val_if_fail (GST_IS_GL_WINDOW (window), NULL);
|
|
|
|
return (GstGLContext *) g_weak_ref_get (&window->context_ref);
|
|
}
|
|
|
|
/**
|
|
* gst_gl_window_get_surface_dimensions:
|
|
* @window: a #GstGLWindow
|
|
* @width: (out): resulting surface width
|
|
* @height: (out): resulting surface height
|
|
*
|
|
* Since: 1.6
|
|
*/
|
|
void
|
|
gst_gl_window_get_surface_dimensions (GstGLWindow * window, guint * width,
|
|
guint * height)
|
|
{
|
|
if (width)
|
|
*width = window->priv->surface_width;
|
|
if (height)
|
|
*height = window->priv->surface_height;
|
|
}
|
|
|
|
GType gst_gl_dummy_window_get_type (void);
|
|
|
|
G_DEFINE_TYPE (GstGLDummyWindow, gst_gl_dummy_window, GST_GL_TYPE_WINDOW);
|
|
|
|
static gboolean
|
|
gst_gl_window_navigation_started (gpointer data)
|
|
{
|
|
GstGLWindow *window = data;
|
|
|
|
g_mutex_lock (&window->priv->nav_lock);
|
|
window->priv->nav_alive = TRUE;
|
|
g_cond_signal (&window->priv->nav_create_cond);
|
|
g_mutex_unlock (&window->priv->nav_lock);
|
|
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static gpointer
|
|
gst_gl_window_navigation_thread (GstGLWindow * window)
|
|
{
|
|
GSource *source;
|
|
|
|
g_return_val_if_fail (GST_IS_GL_WINDOW (window), NULL);
|
|
|
|
window->priv->navigation_context = g_main_context_new ();
|
|
window->priv->navigation_loop =
|
|
g_main_loop_new (window->priv->navigation_context, FALSE);
|
|
g_main_context_push_thread_default (window->priv->navigation_context);
|
|
|
|
source = g_idle_source_new ();
|
|
g_source_set_callback (source, (GSourceFunc) gst_gl_window_navigation_started,
|
|
window, NULL);
|
|
g_source_attach (source, window->priv->navigation_context);
|
|
g_source_unref (source);
|
|
|
|
g_main_loop_run (window->priv->navigation_loop);
|
|
|
|
g_main_context_pop_thread_default (window->priv->navigation_context);
|
|
|
|
g_main_loop_unref (window->priv->navigation_loop);
|
|
g_main_context_unref (window->priv->navigation_context);
|
|
window->priv->navigation_loop = NULL;
|
|
window->priv->navigation_context = NULL;
|
|
|
|
GST_INFO ("navigation loop exited\n");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
gst_gl_dummy_window_set_window_handle (GstGLWindow * window, guintptr handle)
|
|
{
|
|
GstGLDummyWindow *dummy = (GstGLDummyWindow *) window;
|
|
|
|
dummy->handle = handle;
|
|
}
|
|
|
|
static guintptr
|
|
gst_gl_dummy_window_get_window_handle (GstGLWindow * window)
|
|
{
|
|
GstGLDummyWindow *dummy = (GstGLDummyWindow *) window;
|
|
|
|
return (guintptr) dummy->handle;
|
|
}
|
|
|
|
static guintptr
|
|
gst_gl_dummy_window_get_display (GstGLWindow * window)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
gst_gl_dummy_window_class_init (GstGLDummyWindowClass * klass)
|
|
{
|
|
GstGLWindowClass *window_class = (GstGLWindowClass *) klass;
|
|
|
|
window_class->get_display =
|
|
GST_DEBUG_FUNCPTR (gst_gl_dummy_window_get_display);
|
|
window_class->get_window_handle =
|
|
GST_DEBUG_FUNCPTR (gst_gl_dummy_window_get_window_handle);
|
|
window_class->set_window_handle =
|
|
GST_DEBUG_FUNCPTR (gst_gl_dummy_window_set_window_handle);
|
|
}
|
|
|
|
static void
|
|
gst_gl_dummy_window_init (GstGLDummyWindow * dummy)
|
|
{
|
|
dummy->handle = 0;
|
|
}
|
|
|
|
GstGLDummyWindow *
|
|
gst_gl_dummy_window_new (void)
|
|
{
|
|
return g_object_new (gst_gl_dummy_window_get_type (), NULL);
|
|
}
|
|
|
|
void
|
|
gst_gl_window_send_key_event (GstGLWindow * window, const char *event_type,
|
|
const char *key_str)
|
|
{
|
|
g_signal_emit (window, gst_gl_window_signals[EVENT_KEY_SIGNAL], 0,
|
|
event_type, key_str);
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
GstGLWindow *window;
|
|
const char *event_type;
|
|
const char *key_str;
|
|
} KeyEventData;
|
|
|
|
static gboolean
|
|
gst_gl_window_key_event_cb (gpointer data)
|
|
{
|
|
KeyEventData *key_data = 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);
|
|
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
void
|
|
gst_gl_window_send_key_event_async (GstGLWindow * window,
|
|
const char *event_type, const char *key_str)
|
|
{
|
|
KeyEventData *key_data = g_new0 (KeyEventData, 1);
|
|
|
|
key_data->window = window;
|
|
key_data->key_str = key_str;
|
|
key_data->event_type = event_type;
|
|
|
|
g_main_context_invoke_full (window->priv->navigation_context,
|
|
G_PRIORITY_DEFAULT, (GSourceFunc) gst_gl_window_key_event_cb, key_data,
|
|
(GDestroyNotify) g_free);
|
|
}
|
|
|
|
void
|
|
gst_gl_window_send_mouse_event (GstGLWindow * window, const char *event_type,
|
|
int button, double posx, double posy)
|
|
{
|
|
g_signal_emit (window, gst_gl_window_signals[EVENT_MOUSE_SIGNAL], 0,
|
|
event_type, button, posx, posy);
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
GstGLWindow *window;
|
|
const char *event_type;
|
|
int button;
|
|
double posx;
|
|
double posy;
|
|
} MouseEventData;
|
|
|
|
static gboolean
|
|
gst_gl_window_mouse_event_cb (gpointer data)
|
|
{
|
|
MouseEventData *mouse_data = 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);
|
|
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
void
|
|
gst_gl_window_send_mouse_event_async (GstGLWindow * window,
|
|
const char *event_type, int button, double posx, double posy)
|
|
{
|
|
MouseEventData *mouse_data = g_new0 (MouseEventData, 1);
|
|
|
|
mouse_data->window = window;
|
|
mouse_data->event_type = event_type;
|
|
mouse_data->button = button;
|
|
mouse_data->posx = posx;
|
|
mouse_data->posy = posy;
|
|
|
|
g_main_context_invoke_full (window->priv->navigation_context,
|
|
G_PRIORITY_DEFAULT, (GSourceFunc) gst_gl_window_mouse_event_cb,
|
|
mouse_data, (GDestroyNotify) g_free);
|
|
}
|
|
|
|
/**
|
|
* gst_gl_window_handle_events:
|
|
* @window: a #GstGLWindow
|
|
* @handle_events: a #gboolean indicating if events should be handled or not.
|
|
*
|
|
* Tell a @window that it should handle events from the window system. These
|
|
* events are forwarded upstream as navigation events. In some window systems
|
|
* events are not propagated in the window hierarchy if a client is listening
|
|
* for them. This method allows you to disable events handling completely
|
|
* from the @window.
|
|
*/
|
|
void
|
|
gst_gl_window_handle_events (GstGLWindow * window, gboolean handle_events)
|
|
{
|
|
GstGLWindowClass *window_class;
|
|
|
|
g_return_if_fail (GST_IS_GL_WINDOW (window));
|
|
window_class = GST_GL_WINDOW_GET_CLASS (window);
|
|
|
|
if (window_class->handle_events)
|
|
window_class->handle_events (window, handle_events);
|
|
}
|
|
|
|
/**
|
|
* gst_gl_window_set_render_rectangle:
|
|
* @window: a #GstGLWindow
|
|
* @x: x position
|
|
* @y: y position
|
|
* @width: width
|
|
* @height: height
|
|
*
|
|
* Tell a @window that it should render into a specific region of the window
|
|
* according to the #GstVideoOverlay interface.
|
|
*
|
|
* Returns: whether the specified region could be set
|
|
*/
|
|
gboolean
|
|
gst_gl_window_set_render_rectangle (GstGLWindow * window, gint x, gint y,
|
|
gint width, gint height)
|
|
{
|
|
GstGLWindowClass *window_class;
|
|
gboolean ret = FALSE;
|
|
|
|
g_return_val_if_fail (GST_IS_GL_WINDOW (window), FALSE);
|
|
window_class = GST_GL_WINDOW_GET_CLASS (window);
|
|
|
|
if (x < 0 || y < 0 || width <= 0 || height <= 0)
|
|
return FALSE;
|
|
|
|
if (window_class->set_render_rectangle)
|
|
ret = window_class->set_render_rectangle (window, x, y, width, height);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
gst_gl_window_queue_resize (GstGLWindow * window)
|
|
{
|
|
GstGLWindowClass *window_class;
|
|
|
|
g_return_if_fail (GST_IS_GL_WINDOW (window));
|
|
window_class = GST_GL_WINDOW_GET_CLASS (window);
|
|
|
|
window->queue_resize = TRUE;
|
|
if (window_class->queue_resize)
|
|
window_class->queue_resize (window);
|
|
}
|
|
|
|
struct resize_data
|
|
{
|
|
GstGLWindow *window;
|
|
guint width, height;
|
|
};
|
|
|
|
static void
|
|
_on_resize (gpointer data)
|
|
{
|
|
struct resize_data *resize = data;
|
|
|
|
resize->window->resize (resize->window->resize_data, resize->width,
|
|
resize->height);
|
|
}
|
|
|
|
void
|
|
gst_gl_window_resize (GstGLWindow * window, guint width, guint height)
|
|
{
|
|
g_return_if_fail (GST_IS_GL_WINDOW (window));
|
|
|
|
if (window->resize) {
|
|
struct resize_data resize = { 0, };
|
|
|
|
resize.window = window;
|
|
resize.width = width;
|
|
resize.height = height;
|
|
|
|
gst_gl_window_send_message (window, (GstGLWindowCB) _on_resize, &resize);
|
|
}
|
|
|
|
window->priv->surface_width = width;
|
|
window->priv->surface_height = height;
|
|
|
|
window->queue_resize = FALSE;
|
|
}
|