From 72435a6ce36b1304a8da40bd65bf0978d88b6557 Mon Sep 17 00:00:00 2001 From: Rafostar Date: Wed, 14 Oct 2020 15:44:54 +0200 Subject: [PATCH] gtkglsink: add GTK4 support This commit adds required changes to compile the "gtk" plugin against GTK4 from the same source code. The output "gtk4" plugin includes new "gtk4glsink". --- ext/gtk/gstgtkbasesink.c | 70 ++++++++++++--- ext/gtk/gstgtkglsink.c | 32 +++++-- ext/gtk/gstplugin.c | 19 ++-- ext/gtk/gtkconfig.h | 29 ++++++ ext/gtk/gtkgstbasewidget.c | 176 ++++++++++++++++++++++++++++++------- ext/gtk/gtkgstbasewidget.h | 17 +++- ext/gtk/gtkgstglwidget.c | 15 +++- ext/gtk/meson.build | 100 +++++++++++++++------ meson.build | 2 +- meson_options.txt | 4 + 10 files changed, 372 insertions(+), 92 deletions(-) create mode 100644 ext/gtk/gtkconfig.h diff --git a/ext/gtk/gstgtkbasesink.c b/ext/gtk/gstgtkbasesink.c index 0c48f54d6..1f5319089 100644 --- a/ext/gtk/gstgtkbasesink.c +++ b/ext/gtk/gstgtkbasesink.c @@ -1,6 +1,7 @@ /* * GStreamer * Copyright (C) 2015 Matthew Waters + * Copyright (C) 2020 Rafał Dzięgiel * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -77,7 +78,7 @@ G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstGtkBaseSink, gst_gtk_base_sink, G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION, gst_gtk_base_sink_navigation_interface_init); GST_DEBUG_CATEGORY_INIT (gst_debug_gtk_base_sink, - "gtkbasesink", 0, "Gtk Video Sink base class")); + "gtkbasesink", 0, "GTK Video Sink base class")); static void @@ -97,7 +98,7 @@ gst_gtk_base_sink_class_init (GstGtkBaseSinkClass * klass) gobject_class->get_property = gst_gtk_base_sink_get_property; g_object_class_install_property (gobject_class, PROP_WIDGET, - g_param_spec_object ("widget", "Gtk Widget", + g_param_spec_object ("widget", "GTK Widget", "The GtkWidget to place in the widget hierarchy " "(must only be get from the GTK main thread)", GTK_TYPE_WIDGET, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); @@ -114,10 +115,13 @@ gst_gtk_base_sink_class_init (GstGtkBaseSinkClass * klass) "The pixel aspect ratio of the device", DEFAULT_PAR_N, DEFAULT_PAR_D, G_MAXINT, 1, 1, 1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /* Disabling alpha was removed in GTK4 */ +#if !defined(BUILD_FOR_GTK4) g_object_class_install_property (gobject_class, PROP_IGNORE_ALPHA, g_param_spec_boolean ("ignore-alpha", "Ignore Alpha", "When enabled, alpha will be ignored and converted to black", DEFAULT_IGNORE_ALPHA, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +#endif gobject_class->finalize = gst_gtk_base_sink_finalize; @@ -182,7 +186,11 @@ gst_gtk_base_sink_get_widget (GstGtkBaseSink * gtk_sink) /* Ensure GTK is initialized, this has no side effect if it was already * initialized. Also, we do that lazily, so the application can be first */ - if (!gtk_init_check (NULL, NULL)) { + if (!gtk_init_check ( +#if !defined(BUILD_FOR_GTK4) + NULL, NULL +#endif + )) { GST_ERROR_OBJECT (gtk_sink, "Could not ensure GTK initialization."); return NULL; } @@ -197,9 +205,11 @@ gst_gtk_base_sink_get_widget (GstGtkBaseSink * gtk_sink) gtk_sink->bind_pixel_aspect_ratio = g_object_bind_property (gtk_sink, "pixel-aspect-ratio", gtk_sink->widget, "pixel-aspect-ratio", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); +#if !defined(BUILD_FOR_GTK4) gtk_sink->bind_ignore_alpha = g_object_bind_property (gtk_sink, "ignore-alpha", gtk_sink->widget, "ignore-alpha", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); +#endif /* Take the floating ref, other wise the destruction of the container will * make this widget disappear possibly before we are done. */ @@ -313,25 +323,55 @@ gst_gtk_base_sink_start_on_main (GstBaseSink * bsink) GstGtkBaseSink *gst_sink = GST_GTK_BASE_SINK (bsink); GstGtkBaseSinkClass *klass = GST_GTK_BASE_SINK_GET_CLASS (bsink); GtkWidget *toplevel; +#if defined(BUILD_FOR_GTK4) + GtkRoot *root; +#endif if (gst_gtk_base_sink_get_widget (gst_sink) == NULL) return FALSE; /* After this point, gtk_sink->widget will always be set */ +#if defined(BUILD_FOR_GTK4) + root = gtk_widget_get_root (GTK_WIDGET (gst_sink->widget)); + if (!GTK_IS_ROOT (root)) { + GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (gst_sink->widget)); + if (parent) { + GtkWidget *temp_parent; + while ((temp_parent = gtk_widget_get_parent (parent))) + parent = temp_parent; + } + toplevel = (parent) ? parent : GTK_WIDGET (gst_sink->widget); +#else toplevel = gtk_widget_get_toplevel (GTK_WIDGET (gst_sink->widget)); if (!gtk_widget_is_toplevel (toplevel)) { +#endif /* sanity check */ g_assert (klass->window_title); /* User did not add widget its own UI, let's popup a new GtkWindow to * make gst-launch-1.0 work. */ - gst_sink->window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gst_sink->window = gtk_window_new ( +#if !defined(BUILD_FOR_GTK4) + GTK_WINDOW_TOPLEVEL +#endif + ); gtk_window_set_default_size (GTK_WINDOW (gst_sink->window), 640, 480); gtk_window_set_title (GTK_WINDOW (gst_sink->window), klass->window_title); - gtk_container_add (GTK_CONTAINER (gst_sink->window), toplevel); - gst_sink->window_destroy_id = g_signal_connect (gst_sink->window, "destroy", - G_CALLBACK (window_destroy_cb), gst_sink); +#if defined(BUILD_FOR_GTK4) + gtk_window_set_child (GTK_WINDOW ( +#else + gtk_container_add (GTK_CONTAINER ( +#endif + gst_sink->window), toplevel); + + gst_sink->window_destroy_id = g_signal_connect ( +#if defined(BUILD_FOR_GTK4) + GTK_WINDOW (gst_sink->window), +#else + gst_sink->window, +#endif + "destroy", G_CALLBACK (window_destroy_cb), gst_sink); } return TRUE; @@ -350,7 +390,11 @@ gst_gtk_base_sink_stop_on_main (GstBaseSink * bsink) GstGtkBaseSink *gst_sink = GST_GTK_BASE_SINK (bsink); if (gst_sink->window) { +#if defined(BUILD_FOR_GTK4) + gtk_window_destroy (GTK_WINDOW (gst_sink->window)); +#else gtk_widget_destroy (gst_sink->window); +#endif gst_sink->window = NULL; gst_sink->widget = NULL; } @@ -371,10 +415,14 @@ gst_gtk_base_sink_stop (GstBaseSink * bsink) } static void -gst_gtk_widget_show_all_and_unref (GtkWidget * widget) +gst_gtk_window_show_all_and_unref (GtkWidget * window) { - gtk_widget_show_all (widget); - g_object_unref (widget); +#if defined(BUILD_FOR_GTK4) + gtk_window_present (GTK_WINDOW (window)); +#else + gtk_widget_show_all (window); +#endif + g_object_unref (window); } static GstStateChangeReturn @@ -402,7 +450,7 @@ gst_gtk_base_sink_change_state (GstElement * element, GstStateChange transition) GST_OBJECT_UNLOCK (gtk_sink); if (window) - gst_gtk_invoke_on_main ((GThreadFunc) gst_gtk_widget_show_all_and_unref, + gst_gtk_invoke_on_main ((GThreadFunc) gst_gtk_window_show_all_and_unref, window); break; diff --git a/ext/gtk/gstgtkglsink.c b/ext/gtk/gstgtkglsink.c index 1102d47c9..3b0059906 100644 --- a/ext/gtk/gstgtkglsink.c +++ b/ext/gtk/gstgtkglsink.c @@ -1,6 +1,7 @@ /* * GStreamer * Copyright (C) 2015 Matthew Waters + * Copyright (C) 2020 Rafał Dzięgiel * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -23,12 +24,18 @@ * @title: gtkglsink */ +/** + * SECTION:element-gtk4glsink + * @title: gtk4glsink + */ + #ifdef HAVE_CONFIG_H #include "config.h" #endif #include +#include "gtkconfig.h" #include "gstgtkglsink.h" #include "gtkgstglwidget.h" @@ -58,7 +65,7 @@ static GstStaticPadTemplate gst_gtk_gl_sink_template = #define gst_gtk_gl_sink_parent_class parent_class G_DEFINE_TYPE_WITH_CODE (GstGtkGLSink, gst_gtk_gl_sink, GST_TYPE_GTK_BASE_SINK, GST_DEBUG_CATEGORY_INIT (gst_debug_gtk_gl_sink, - "gtkglsink", 0, "Gtk GL Video Sink")); + GTKCONFIG_GLSINK, 0, GTKCONFIG_NAME " GL Video Sink")); static void gst_gtk_gl_sink_class_init (GstGtkGLSinkClass * klass) @@ -82,11 +89,16 @@ gst_gtk_gl_sink_class_init (GstGtkGLSinkClass * klass) gstbasesink_class->get_caps = gst_gtk_gl_sink_get_caps; gstgtkbasesink_class->create_widget = gtk_gst_gl_widget_new; - gstgtkbasesink_class->window_title = "Gtk+ GL renderer"; + gstgtkbasesink_class->window_title = GTKCONFIG_NAME " GL Renderer"; - gst_element_class_set_metadata (gstelement_class, "Gtk GL Video Sink", + gst_element_class_set_metadata (gstelement_class, + GTKCONFIG_NAME " GL Video Sink", "Sink/Video", "A video sink that renders to a GtkWidget using OpenGL", - "Matthew Waters "); + "Matthew Waters " +#if defined(BUILD_FOR_GTK4) + ", Rafał Dzięgiel " +#endif + ); gst_element_class_add_static_pad_template (gstelement_class, &gst_gtk_gl_sink_template); @@ -119,6 +131,7 @@ gst_gtk_gl_sink_query (GstBaseSink * bsink, GstQuery * query) return res; } +#if !defined(BUILD_FOR_GTK4) static void _size_changed_cb (GtkWidget * widget, GdkRectangle * rectangle, GstGtkGLSink * gtk_sink) @@ -138,11 +151,12 @@ _size_changed_cb (GtkWidget * widget, GdkRectangle * rectangle, GST_OBJECT_UNLOCK (gtk_sink); if (reconfigure) { - GST_DEBUG_OBJECT (gtk_sink, "Sending reconfigure event on sinkpad."); + GST_DEBUG_OBJECT (gtk_sink, "Sending reconfigure event on sinkpad"); gst_pad_push_event (GST_BASE_SINK (gtk_sink)->sinkpad, gst_event_new_reconfigure ()); } } +#endif static void destroy_cb (GtkWidget * widget, GstGtkGLSink * gtk_sink) @@ -171,20 +185,20 @@ gst_gtk_gl_sink_start (GstBaseSink * bsink) /* After this point, gtk_sink->widget will always be set */ gst_widget = GTK_GST_GL_WIDGET (base_sink->widget); +#if !defined(BUILD_FOR_GTK4) /* Track the allocation size */ gtk_sink->size_allocate_sig_handler = g_signal_connect (gst_widget, "size-allocate", G_CALLBACK (_size_changed_cb), gtk_sink); +#endif gtk_sink->widget_destroy_sig_handler = g_signal_connect (gst_widget, "destroy", G_CALLBACK (destroy_cb), gtk_sink); - _size_changed_cb (GTK_WIDGET (gst_widget), NULL, gtk_sink); - if (!gtk_gst_gl_widget_init_winsys (gst_widget)) { GST_ELEMENT_ERROR (bsink, RESOURCE, NOT_FOUND, ("%s", - "Failed to initialize OpenGL with Gtk"), (NULL)); + "Failed to initialize OpenGL with GTK"), (NULL)); return FALSE; } @@ -194,7 +208,7 @@ gst_gtk_gl_sink_start (GstBaseSink * bsink) if (!gtk_sink->display || !gtk_sink->context || !gtk_sink->gtk_context) { GST_ELEMENT_ERROR (bsink, RESOURCE, NOT_FOUND, ("%s", - "Failed to retrieve OpenGL context from Gtk"), (NULL)); + "Failed to retrieve OpenGL context from GTK"), (NULL)); return FALSE; } diff --git a/ext/gtk/gstplugin.c b/ext/gtk/gstplugin.c index ed275785b..788f4f9dd 100644 --- a/ext/gtk/gstplugin.c +++ b/ext/gtk/gstplugin.c @@ -1,6 +1,7 @@ /* * GStreamer * Copyright (C) 2015 Matthew Waters + * Copyright (C) 2020 Rafał Dzięgiel * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -22,31 +23,37 @@ #include "config.h" #endif +#include "gtkconfig.h" + +#if !defined(BUILD_FOR_GTK4) #include "gstgtksink.h" -#if defined(HAVE_GTK3_GL) +#endif + +#if defined(HAVE_GTK_GL) #include "gstgtkglsink.h" #endif static gboolean plugin_init (GstPlugin * plugin) { +#if !defined(BUILD_FOR_GTK4) if (!gst_element_register (plugin, "gtksink", GST_RANK_NONE, GST_TYPE_GTK_SINK)) { return FALSE; } -#if defined(HAVE_GTK3_GL) - if (!gst_element_register (plugin, "gtkglsink", +#endif + +#if defined(HAVE_GTK_GL) + if (!gst_element_register (plugin, GTKCONFIG_GLSINK, GST_RANK_NONE, GST_TYPE_GTK_GL_SINK)) { return FALSE; } #endif - return TRUE; } GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, - gtk, - "Gtk+ sink", + GTKCONFIG_PLUGIN, GTKCONFIG_NAME " sink", plugin_init, PACKAGE_VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/ext/gtk/gtkconfig.h b/ext/gtk/gtkconfig.h new file mode 100644 index 000000000..8dd28dc00 --- /dev/null +++ b/ext/gtk/gtkconfig.h @@ -0,0 +1,29 @@ +/* + * GStreamer + * Copyright (C) 2020 Rafał Dzięgiel + * + * 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. + */ + +#if defined(BUILD_FOR_GTK4) +#define GTKCONFIG_PLUGIN gtk4 +#define GTKCONFIG_NAME "GTK4" +#define GTKCONFIG_GLSINK "gtk4glsink" +#else +#define GTKCONFIG_PLUGIN gtk +#define GTKCONFIG_NAME "GTK" +#define GTKCONFIG_GLSINK "gtkglsink" +#endif diff --git a/ext/gtk/gtkgstbasewidget.c b/ext/gtk/gtkgstbasewidget.c index 4858f2764..5335478cb 100644 --- a/ext/gtk/gtkgstbasewidget.c +++ b/ext/gtk/gtkgstbasewidget.c @@ -1,6 +1,7 @@ /* * GStreamer * Copyright (C) 2015 Matthew Waters + * Copyright (C) 2020 Rafał Dzięgiel * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -74,6 +75,22 @@ gtk_gst_base_widget_get_preferred_height (GtkWidget * widget, gint * min, *natural = video_height; } +#if defined(BUILD_FOR_GTK4) +static void +gtk_gst_base_widget_measure (GtkWidget * widget, GtkOrientation orientation, + int for_size, int *min, int *natural, + int *minimum_baseline, int *natural_baseline) +{ + if (orientation == GTK_ORIENTATION_HORIZONTAL) + gtk_gst_base_widget_get_preferred_width (widget, min, natural); + else + gtk_gst_base_widget_get_preferred_height (widget, min, natural); + + *minimum_baseline = -1; + *natural_baseline = -1; +} +#endif + static void gtk_gst_base_widget_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) @@ -235,22 +252,46 @@ _gdk_key_to_navigation_string (guint keyval) } } +static GdkEvent * +_get_current_event (GtkEventController * controller) +{ +#if defined(BUILD_FOR_GTK4) + return gtk_event_controller_get_current_event (controller); +#else + return gtk_get_current_event (); +#endif +} + +static void +_gdk_event_free (GdkEvent * event) +{ +#if !defined(BUILD_FOR_GTK4) + if (event) + gdk_event_free (event); +#endif +} + static gboolean -gtk_gst_base_widget_key_event (GtkWidget * widget, GdkEventKey * event) +gtk_gst_base_widget_key_event (GtkEventControllerKey * key_controller, + guint keyval, guint keycode, GdkModifierType state) { + GtkEventController *controller = GTK_EVENT_CONTROLLER (key_controller); + GtkWidget *widget = gtk_event_controller_get_widget (controller); GtkGstBaseWidget *base_widget = GTK_GST_BASE_WIDGET (widget); GstElement *element; if ((element = g_weak_ref_get (&base_widget->element))) { if (GST_IS_NAVIGATION (element)) { - const gchar *str = _gdk_key_to_navigation_string (event->keyval); - const gchar *key_type = - event->type == GDK_KEY_PRESS ? "key-press" : "key-release"; - - if (!str) - str = event->string; - - gst_navigation_send_key_event (GST_NAVIGATION (element), key_type, str); + GdkEvent *event = _get_current_event (controller); + const gchar *str = _gdk_key_to_navigation_string (keyval); + + if (str) { + const gchar *key_type = + gdk_event_get_event_type (event) == + GDK_KEY_PRESS ? "key-press" : "key-release"; + gst_navigation_send_key_event (GST_NAVIGATION (element), key_type, str); + } + _gdk_event_free (event); } g_object_unref (element); } @@ -325,22 +366,43 @@ _display_size_to_stream_size (GtkGstBaseWidget * base_widget, gdouble x, } static gboolean -gtk_gst_base_widget_button_event (GtkWidget * widget, GdkEventButton * event) +gtk_gst_base_widget_button_event ( +#if defined(BUILD_FOR_GTK4) + GtkGestureClick * gesture, +#else + GtkGestureMultiPress * gesture, +#endif + int n_press, double x, double y) { + GtkEventController *controller = GTK_EVENT_CONTROLLER (gesture); + GtkWidget *widget = gtk_event_controller_get_widget (controller); GtkGstBaseWidget *base_widget = GTK_GST_BASE_WIDGET (widget); GstElement *element; if ((element = g_weak_ref_get (&base_widget->element))) { if (GST_IS_NAVIGATION (element)) { + GdkEvent *event = _get_current_event (controller); const gchar *key_type = - event->type == - GDK_BUTTON_PRESS ? "mouse-button-press" : "mouse-button-release"; - gdouble x, y; + gdk_event_get_event_type (event) == GDK_BUTTON_PRESS + ? "mouse-button-press" : "mouse-button-release"; + gdouble stream_x, stream_y; +#if !defined(BUILD_FOR_GTK4) + guint button; + gdk_event_get_button (event, &button); +#endif - _display_size_to_stream_size (base_widget, event->x, event->y, &x, &y); + _display_size_to_stream_size (base_widget, x, y, &stream_x, &stream_y); gst_navigation_send_mouse_event (GST_NAVIGATION (element), key_type, - event->button, x, y); +#if defined(BUILD_FOR_GTK4) + /* Gesture is set to ignore other buttons so we do not have to check */ + GDK_BUTTON_PRIMARY, +#else + button, +#endif + stream_x, stream_y); + + _gdk_event_free (event); } g_object_unref (element); } @@ -349,19 +411,30 @@ gtk_gst_base_widget_button_event (GtkWidget * widget, GdkEventButton * event) } static gboolean -gtk_gst_base_widget_motion_event (GtkWidget * widget, GdkEventMotion * event) +gtk_gst_base_widget_motion_event (GtkEventControllerMotion * motion_controller, + double x, double y) { + GtkEventController *controller = GTK_EVENT_CONTROLLER (motion_controller); + GtkWidget *widget = gtk_event_controller_get_widget (controller); GtkGstBaseWidget *base_widget = GTK_GST_BASE_WIDGET (widget); GstElement *element; + /* Sometimes GTK might generate motion events with the same coords during + * window redraws, so we check for the differences to prevent that */ + if (base_widget->cursor_coords_x == x && base_widget->cursor_coords_y == y) + return FALSE; + + base_widget->cursor_coords_x = x; + base_widget->cursor_coords_y = y; + if ((element = g_weak_ref_get (&base_widget->element))) { if (GST_IS_NAVIGATION (element)) { - gdouble x, y; + gdouble stream_x, stream_y; - _display_size_to_stream_size (base_widget, event->x, event->y, &x, &y); + _display_size_to_stream_size (base_widget, x, y, &stream_x, &stream_y); gst_navigation_send_mouse_event (GST_NAVIGATION (element), "mouse-move", - 0, x, y); + 0, stream_x, stream_y); } g_object_unref (element); } @@ -395,22 +468,27 @@ gtk_gst_base_widget_class_init (GtkGstBaseWidgetClass * klass) "When enabled, alpha will be ignored and converted to black", DEFAULT_IGNORE_ALPHA, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +#if defined(BUILD_FOR_GTK4) + widget_klass->measure = gtk_gst_base_widget_measure; +#else widget_klass->get_preferred_width = gtk_gst_base_widget_get_preferred_width; widget_klass->get_preferred_height = gtk_gst_base_widget_get_preferred_height; - widget_klass->key_press_event = gtk_gst_base_widget_key_event; - widget_klass->key_release_event = gtk_gst_base_widget_key_event; - widget_klass->button_press_event = gtk_gst_base_widget_button_event; - widget_klass->button_release_event = gtk_gst_base_widget_button_event; - widget_klass->motion_notify_event = gtk_gst_base_widget_motion_event; +#endif GST_DEBUG_CATEGORY_INIT (gst_debug_gtk_base_widget, "gtkbasewidget", 0, - "Gtk Video Base Widget"); + "GTK Video Base Widget"); } void gtk_gst_base_widget_init (GtkGstBaseWidget * widget) { - int event_mask; + /* Event controllers were added in GTK 3.24 */ + GtkEventController *key_controller; + GtkEventController *motion_controller; + GtkGesture *click_gesture; + + widget->cursor_coords_x = 0; + widget->cursor_coords_y = 0; widget->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO; widget->par_n = DEFAULT_PAR_N; @@ -423,14 +501,46 @@ gtk_gst_base_widget_init (GtkGstBaseWidget * widget) g_weak_ref_init (&widget->element, NULL); g_mutex_init (&widget->lock); + key_controller = gtk_event_controller_key_new ( +#if !defined(BUILD_FOR_GTK4) + GTK_WIDGET (widget) +#endif + ); + g_signal_connect (key_controller, "key-pressed", + G_CALLBACK (gtk_gst_base_widget_key_event), NULL); + g_signal_connect (key_controller, "key-released", + G_CALLBACK (gtk_gst_base_widget_key_event), NULL); + + motion_controller = gtk_event_controller_motion_new ( +#if !defined(BUILD_FOR_GTK4) + GTK_WIDGET (widget) +#endif + ); + g_signal_connect (motion_controller, "motion", + G_CALLBACK (gtk_gst_base_widget_motion_event), NULL); + + click_gesture = +#if defined(BUILD_FOR_GTK4) + gtk_gesture_click_new (); +#else + gtk_gesture_multi_press_new (GTK_WIDGET (widget)); +#endif + g_signal_connect (click_gesture, "pressed", + G_CALLBACK (gtk_gst_base_widget_button_event), NULL); + g_signal_connect (click_gesture, "released", + G_CALLBACK (gtk_gst_base_widget_button_event), NULL); + +#if defined(BUILD_FOR_GTK4) + gtk_widget_set_focusable (GTK_WIDGET (widget), TRUE); + gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (click_gesture), + GDK_BUTTON_PRIMARY); + + gtk_widget_add_controller (GTK_WIDGET (widget), key_controller); + gtk_widget_add_controller (GTK_WIDGET (widget), motion_controller); + gtk_widget_add_controller (GTK_WIDGET (widget), + GTK_EVENT_CONTROLLER (click_gesture)); +#endif gtk_widget_set_can_focus (GTK_WIDGET (widget), TRUE); - event_mask = gtk_widget_get_events (GTK_WIDGET (widget)); - event_mask |= GDK_KEY_PRESS_MASK - | GDK_KEY_RELEASE_MASK - | GDK_BUTTON_PRESS_MASK - | GDK_BUTTON_RELEASE_MASK - | GDK_POINTER_MOTION_MASK | GDK_BUTTON_MOTION_MASK; - gtk_widget_set_events (GTK_WIDGET (widget), event_mask); } void diff --git a/ext/gtk/gtkgstbasewidget.h b/ext/gtk/gtkgstbasewidget.h index 13737c632..640a1c742 100644 --- a/ext/gtk/gtkgstbasewidget.h +++ b/ext/gtk/gtkgstbasewidget.h @@ -1,6 +1,7 @@ /* * GStreamer * Copyright (C) 2015 Matthew Waters + * Copyright (C) 2020 Rafał Dzięgiel * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -25,6 +26,10 @@ #include #include +#if !defined(BUILD_FOR_GTK4) +#include +#endif + #define GTK_GST_BASE_WIDGET(w) ((GtkGstBaseWidget *)(w)) #define GTK_GST_BASE_WIDGET_CLASS(k) ((GtkGstBaseWidgetClass *)(k)) #define GTK_GST_BASE_WIDGET_LOCK(w) g_mutex_lock(&((GtkGstBaseWidget*)(w))->lock) @@ -38,10 +43,10 @@ typedef struct _GtkGstBaseWidgetClass GtkGstBaseWidgetClass; struct _GtkGstBaseWidget { union { +#if !defined(BUILD_FOR_GTK4) GtkDrawingArea drawing_area; -#if GTK_CHECK_VERSION(3, 15, 0) - GtkGLArea gl_area; #endif + GtkGLArea gl_area; } parent; /* properties */ @@ -63,6 +68,10 @@ struct _GtkGstBaseWidget guint display_ratio_num; guint display_ratio_den; + /* cursor motion coords */ + gdouble cursor_coords_x; + gdouble cursor_coords_y; + /*< private >*/ GMutex lock; GWeakRef element; @@ -74,10 +83,10 @@ struct _GtkGstBaseWidget struct _GtkGstBaseWidgetClass { union { +#if !defined(BUILD_FOR_GTK4) GtkDrawingAreaClass drawing_area_class; -#if GTK_CHECK_VERSION(3, 15, 0) - GtkGLAreaClass gl_area_class; #endif + GtkGLAreaClass gl_area_class; } parent_class; }; diff --git a/ext/gtk/gtkgstglwidget.c b/ext/gtk/gtkgstglwidget.c index 6c423ad89..186144a1c 100644 --- a/ext/gtk/gtkgstglwidget.c +++ b/ext/gtk/gtkgstglwidget.c @@ -1,6 +1,7 @@ /* * GStreamer * Copyright (C) 2015 Matthew Waters + * Copyright (C) 2020 Rafał Dzięgiel * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -30,12 +31,20 @@ #include #if GST_GL_HAVE_WINDOW_X11 && defined (GDK_WINDOWING_X11) +#if defined(BUILD_FOR_GTK4) +#include +#else #include +#endif #include #endif #if GST_GL_HAVE_WINDOW_WAYLAND && defined (GDK_WINDOWING_WAYLAND) +#if defined(BUILD_FOR_GTK4) +#include +#else #include +#endif #include #endif @@ -78,8 +87,7 @@ static const GLfloat vertices[] = { G_DEFINE_TYPE_WITH_CODE (GtkGstGLWidget, gtk_gst_gl_widget, GTK_TYPE_GL_AREA, G_ADD_PRIVATE (GtkGstGLWidget) GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "gtkgstglwidget", 0, - "Gtk Gst GL Widget"); - ); + "GTK Gst GL Widget")); static void gtk_gst_gl_widget_bind_buffer (GtkGstGLWidget * gst_widget) @@ -407,8 +415,11 @@ gtk_gst_gl_widget_init (GtkGstGLWidget * gst_widget) GST_INFO ("Created %" GST_PTR_FORMAT, priv->display); + /* GTK4 always has alpha */ +#if !defined(BUILD_FOR_GTK4) gtk_gl_area_set_has_alpha (GTK_GL_AREA (gst_widget), !base_widget->ignore_alpha); +#endif } static void diff --git a/ext/gtk/meson.build b/ext/gtk/meson.build index 3a30017e7..6f7fd6a67 100644 --- a/ext/gtk/meson.build +++ b/ext/gtk/meson.build @@ -1,59 +1,107 @@ +gtk_versions = [3, 4] gtk_sources = [ 'gstgtkbasesink.c', - 'gstgtksink.c', 'gstgtkutils.c', 'gstplugin.c', 'gtkgstbasewidget.c', - 'gtkgstwidget.c', ] +gtk_lib_added = false +gtk_dep = dependency('gtk+-3.0', required : get_option('gtk3')) +gtk4_dep = dependency('gtk4', required : get_option('gtk4')) -gtk_defines = [] -optional_deps = [] +foreach gtk_ver : gtk_versions + gtkv = 'gtk' + gtk_ver.to_string() -gtk_dep = dependency('gtk+-3.0', required : get_option('gtk3')) -if gtk_dep.found() - # FIXME: automagic - if have_gstgl and gtk_dep.version().version_compare('>=3.15.0') - have_gtk3_gl_windowing = false + if gtk_ver > 3 + allow_experiments = get_option(gtkv + '-experiments') + if not allow_experiments + continue + endif + endif + + gtk_state = get_option(gtkv) + if gtk_state.disabled() + continue + endif + + min_ver = gtk_ver >= 4 ? '3.99.2' : '3.24.0' + x11_dep = gtk_ver >= 4 ? gtkv + '-x11' : 'gtk+-x11-3.0' + way_dep = gtk_ver >= 4 ? gtkv + '-wayland' : 'gtk+-wayland-3.0' + lib_dep = gtk_ver >= 4 ? gtk4_dep : gtk_dep + + if not lib_dep.found() or not lib_dep.version().version_compare('>=' + min_ver) + continue + endif + + lib_sources = [] + gtk_defines = [] + optional_deps = [] + have_gtk_gl_windowing = false + lib_sources += gtk_sources + if gtk_ver == 3 + lib_sources += [ + 'gstgtksink.c', + 'gtkgstwidget.c', + ] + endif + + if have_gstgl if gst_gl_have_window_x11 and gst_gl_have_platform_glx # FIXME: automagic - gtk_x11_dep = dependency('gtk+-x11-3.0', required : false) + gtk_x11_dep = dependency(x11_dep, required : false) if gtk_x11_dep.found() optional_deps += [gtk_x11_dep, gstglx11_dep] - have_gtk3_gl_windowing = true + have_gtk_gl_windowing = true endif endif if gst_gl_have_window_wayland and gst_gl_have_platform_egl # FIXME: automagic - gtk_wayland_dep = dependency('gtk+-wayland-3.0', required : false) + gtk_wayland_dep = dependency(way_dep, required : false) if gtk_wayland_dep.found() optional_deps += [gtk_wayland_dep, gstglegl_dep, gstglwayland_dep] - have_gtk3_gl_windowing = true + have_gtk_gl_windowing = true endif endif + endif + + if gtk_ver > 3 and not have_gtk_gl_windowing + continue + endif - if have_gtk3_gl_windowing - gtk_sources += [ - 'gstgtkglsink.c', - 'gtkgstglwidget.c', - ] - optional_deps += [gstgl_dep, gstglproto_dep] - gtk_defines += ['-DGST_USE_UNSTABLE_API', '-DHAVE_GTK3_GL'] + if have_gtk_gl_windowing + lib_sources += [ + 'gstgtkglsink.c', + 'gtkgstglwidget.c', + ] + optional_deps += [gstgl_dep, gstglproto_dep] + gtk_defines += ['-DGST_USE_UNSTABLE_API', '-DHAVE_GTK_GL'] + if gtk_ver == 4 + gtk_defines += '-DBUILD_FOR_GTK4' endif endif - gstgtk = library('gstgtk', - gtk_sources, + lib_name = 'gstgtk' + if gtk_ver > 3 + lib_name += gtk_ver.to_string() + endif + + # GStreamer loads all plugins at once. To avoid GTK versions conflicts, + # do not install more than one plugin version on "auto". + # The "gtk4" option should be set to "enabled" for packaging. + is_gstgtk_install = not gtk_lib_added or not gtk_state.auto() + + gstgtk = library(lib_name, + lib_sources, c_args : gst_plugins_good_args + gtk_defines, link_args : noseh_link_args, include_directories : [configinc], - dependencies : [gtk_dep, gstvideo_dep, gstbase_dep, libm] + optional_deps, - install : true, + dependencies : [lib_dep, gstvideo_dep, gstbase_dep, libm] + optional_deps, + install : is_gstgtk_install, install_dir : plugins_install_dir, ) pkgconfig.generate(gstgtk, install_dir : plugins_pkgconfig_install_dir) plugins += [gstgtk] -endif - + gtk_lib_added = true +endforeach diff --git a/meson.build b/meson.build index 77ab1a7d2..e6973b318 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project('gst-plugins-good', 'c', version : '1.18.1', - meson_version : '>= 0.48', + meson_version : '>= 0.49', default_options : [ 'warning_level=1', 'buildtype=debugoptimized' ]) diff --git a/meson_options.txt b/meson_options.txt index 5c96f6542..772628cd5 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -53,6 +53,7 @@ option('dv1394', type : 'feature', value : 'auto', description : 'Digital IEEE13 option('flac', type : 'feature', value : 'auto', description : 'FLAC audio codec plugin') option('gdk-pixbuf', type : 'feature', value : 'auto', description : 'gdk-pixbuf image decoder, overlay, and sink plugin') option('gtk3', type : 'feature', value : 'auto', description : 'GTK+ video sink plugin') +option('gtk4', type : 'feature', value : 'auto', description : 'GTK4 video sink plugin') option('jack', type : 'feature', value : 'auto', description : 'JACK audio source/sink plugin') option('jpeg', type : 'feature', value : 'auto', description : 'JPEG image codec plugin') option('lame', type : 'feature', value : 'auto', description : 'LAME mp3 audio encoder plugin') @@ -74,6 +75,9 @@ option('vpx', type : 'feature', value : 'auto', description : 'VP8 and VP9 video option('waveform', type : 'feature', value : 'auto', description : 'Windows waveform audio sink plugin') option('wavpack', type : 'feature', value : 'auto', description : 'Wavpack audio codec plugin') +# gtk plugin options +option('gtk4-experiments', type : 'boolean', value : false, description : 'Allow compiling experimental GTK4 plugin') + # rpicamsrc plugin options option('rpicamsrc', type : 'feature', value : 'auto', description : 'Raspberry Pi camera module plugin') option('rpi-header-dir', type : 'string', value : '/opt/vc/include', description : 'Directory where VideoCore/MMAL headers and bcm_host.h can be found') -- 2.26.2