aboutsummaryrefslogtreecommitdiff
path: root/x11-toolkits
diff options
context:
space:
mode:
authorJan Beich <jbeich@FreeBSD.org>2021-08-23 10:30:02 +0000
committerJan Beich <jbeich@FreeBSD.org>2021-08-23 12:52:23 +0000
commitd88adb1c0af3a6b9160a0ecdcc242ad91973d6dd (patch)
treefb23e2b2adfa31d93f5793fd730d88e7ff1acc4d /x11-toolkits
parentee68b684176b7b19cb107ab10e68f32230c0d6eb (diff)
downloadports-d88adb1c0af3a6b9160a0ecdcc242ad91973d6dd.tar.gz
ports-d88adb1c0af3a6b9160a0ecdcc242ad91973d6dd.zip
x11-toolkits/gstreamer1-plugins-gtk4: add new port
GTK4 plugin contains: - gtk4glsink: A video sink that renders to a GtkWidget using OpenGL - gtk4sink: A video sink that renders to a GtkWidget https://gstreamer.freedesktop.org/documentation/gtk/gtksink.html
Diffstat (limited to 'x11-toolkits')
-rw-r--r--x11-toolkits/Makefile1
-rw-r--r--x11-toolkits/gstreamer1-plugins-gtk4/Makefile20
-rw-r--r--x11-toolkits/gstreamer1-plugins-gtk4/distinfo3
-rw-r--r--x11-toolkits/gstreamer1-plugins-gtk4/files/patch-gtk41145
-rw-r--r--x11-toolkits/gstreamer1-plugins-gtk4/pkg-descr6
5 files changed, 1175 insertions, 0 deletions
diff --git a/x11-toolkits/Makefile b/x11-toolkits/Makefile
index 4c1274243d7b..a0a801bfc0e7 100644
--- a/x11-toolkits/Makefile
+++ b/x11-toolkits/Makefile
@@ -23,6 +23,7 @@
SUBDIR += gnustep-gui
SUBDIR += granite
SUBDIR += gstreamer1-plugins-gtk
+ SUBDIR += gstreamer1-plugins-gtk4
SUBDIR += gstreamer1-plugins-pango
SUBDIR += gtk-layer-shell
SUBDIR += gtk-sharp-beans
diff --git a/x11-toolkits/gstreamer1-plugins-gtk4/Makefile b/x11-toolkits/gstreamer1-plugins-gtk4/Makefile
new file mode 100644
index 000000000000..61882b85e10e
--- /dev/null
+++ b/x11-toolkits/gstreamer1-plugins-gtk4/Makefile
@@ -0,0 +1,20 @@
+PORTNAME= gstreamer1-plugins-gtk4
+DISTVERSION= 1.16.2
+CATEGORIES= x11-toolkits
+MASTER_SITES= GNOME/sources/${DISTNAME:S/-${DISTVERSION}/\/${DISTVERSION:R}/} \
+ https://gstreamer.freedesktop.org/src/${DISTNAME:S/-${DISTVERSION}//}/
+DISTNAME= gst-plugins-good-${DISTVERSION}
+
+MAINTAINER= jbeich@FreeBSD.org
+COMMENT= GStreamer GTK4 video sink plugin
+
+LICENSE= LGPL21+
+LICENSE_FILE= ${WRKSRC}/COPYING
+
+USES= compiler:c11 gnome meson pkgconfig tar:xz
+USE_GNOME= cairo gtk40
+USE_GSTREAMER1= gl
+MESON_ARGS= --auto-features=disabled -Dgtk4=enabled
+PLIST_FILES= lib/gstreamer-1.0/libgstgtk4.so
+
+.include <bsd.port.mk>
diff --git a/x11-toolkits/gstreamer1-plugins-gtk4/distinfo b/x11-toolkits/gstreamer1-plugins-gtk4/distinfo
new file mode 100644
index 000000000000..c4d7ce4bad12
--- /dev/null
+++ b/x11-toolkits/gstreamer1-plugins-gtk4/distinfo
@@ -0,0 +1,3 @@
+TIMESTAMP = 1575371194
+SHA256 (gst-plugins-good-1.16.2.tar.xz) = 40bb3bafda25c0b739c8fc36e48380fccf61c4d3f83747e97ac3f9b0171b1319
+SIZE (gst-plugins-good-1.16.2.tar.xz) = 3897172
diff --git a/x11-toolkits/gstreamer1-plugins-gtk4/files/patch-gtk4 b/x11-toolkits/gstreamer1-plugins-gtk4/files/patch-gtk4
new file mode 100644
index 000000000000..8b1fe8e0a512
--- /dev/null
+++ b/x11-toolkits/gstreamer1-plugins-gtk4/files/patch-gtk4
@@ -0,0 +1,1145 @@
+https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/merge_requests/767
+
+--- ext/gtk/gstgtkbasesink.c.orig 2019-12-03 11:06:37 UTC
++++ ext/gtk/gstgtkbasesink.c
+@@ -1,6 +1,7 @@
+ /*
+ * GStreamer
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
++ * Copyright (C) 2020 Rafał Dzięgiel <rafostar.github@gmail.com>
+ *
+ * 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_
+ 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 * kl
+ 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 * kl
+ "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;
+
+@@ -144,6 +148,8 @@ gst_gtk_base_sink_finalize (GObject * object)
+ {
+ GstGtkBaseSink *gtk_sink = GST_GTK_BASE_SINK (object);
+
++ GST_DEBUG ("finalizing base sink");
++
+ GST_OBJECT_LOCK (gtk_sink);
+ if (gtk_sink->window && gtk_sink->window_destroy_id)
+ g_signal_handler_disconnect (gtk_sink->window, gtk_sink->window_destroy_id);
+@@ -168,6 +174,14 @@ static void
+ window_destroy_cb (GtkWidget * widget, GstGtkBaseSink * gtk_sink)
+ {
+ GST_OBJECT_LOCK (gtk_sink);
++ if (gtk_sink->widget) {
++ if (gtk_sink->widget_destroy_id) {
++ g_signal_handler_disconnect (gtk_sink->widget,
++ gtk_sink->widget_destroy_id);
++ gtk_sink->widget_destroy_id = 0;
++ }
++ g_clear_object (&gtk_sink->widget);
++ }
+ gtk_sink->window = NULL;
+ GST_OBJECT_UNLOCK (gtk_sink);
+ }
+@@ -180,7 +194,11 @@ gst_gtk_base_sink_get_widget (GstGtkBaseSink * gtk_sin
+
+ /* 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;
+ }
+@@ -195,13 +213,16 @@ gst_gtk_base_sink_get_widget (GstGtkBaseSink * gtk_sin
+ 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 disapear possibly before we are done. */
+ gst_object_ref_sink (gtk_sink->widget);
++
+ gtk_sink->widget_destroy_id = g_signal_connect (gtk_sink->widget, "destroy",
+ G_CALLBACK (widget_destroy_cb), gtk_sink);
+
+@@ -311,25 +332,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;
+@@ -348,7 +399,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;
+ }
+@@ -369,10 +424,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
+@@ -400,7 +459,7 @@ gst_gtk_base_sink_change_state (GstElement * element,
+ 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;
+--- ext/gtk/gstgtkbasesink.h.orig 2019-12-03 11:06:37 UTC
++++ ext/gtk/gstgtkbasesink.h
+@@ -51,14 +51,14 @@ GType gst_gtk_base_sink_get_type (void);
+ struct _GstGtkBaseSink
+ {
+ /* <private> */
+- GstVideoSink parent;
++ GstVideoSink parent;
+
+- GstVideoInfo v_info;
++ GstVideoInfo v_info;
+
+ GtkGstBaseWidget *widget;
+
+ /* properties */
+- gboolean force_aspect_ratio;
++ gboolean force_aspect_ratio;
+ GBinding *bind_aspect_ratio;
+
+ gint par_n;
+@@ -69,8 +69,8 @@ struct _GstGtkBaseSink
+ GBinding *bind_ignore_alpha;
+
+ GtkWidget *window;
+- gulong widget_destroy_id;
+- gulong window_destroy_id;
++ gulong widget_destroy_id;
++ gulong window_destroy_id;
+ };
+
+ /**
+--- ext/gtk/gstgtkglsink.c.orig 2019-12-03 11:06:37 UTC
++++ ext/gtk/gstgtkglsink.c
+@@ -1,6 +1,7 @@
+ /*
+ * GStreamer
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
++ * Copyright (C) 2020 Rafał Dzięgiel <rafostar.github@gmail.com>
+ *
+ * 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 <gst/gl/gstglfuncs.h>
+
++#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,13 @@ 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@centricular.com>");
++ "Matthew Waters <matthew@centricular.com>, "
++ "Rafał Dzięgiel <rafostar.github@gmail.com>");
+
+ gst_element_class_add_static_pad_template (gstelement_class,
+ &gst_gtk_gl_sink_template);
+@@ -120,16 +129,18 @@ gst_gtk_gl_sink_query (GstBaseSink * bsink, GstQuery *
+ }
+
+ static void
+-_size_changed_cb (GtkWidget * widget, GdkRectangle * rectangle,
+- GstGtkGLSink * gtk_sink)
++_size_changed_cb (GtkWidget * widget, gint width,
++ gint height, GstGtkGLSink * gtk_sink)
+ {
+- gint scale_factor, width, height;
+ gboolean reconfigure;
+
+- scale_factor = gtk_widget_get_scale_factor (widget);
+- width = scale_factor * gtk_widget_get_allocated_width (widget);
+- height = scale_factor * gtk_widget_get_allocated_height (widget);
++ GtkGstBaseWidget *base_widget = GTK_GST_BASE_WIDGET (widget);
+
++ /* Ignore size changes before widget is negotiated
++ * we are going to queue a resize after negotiation */
++ if (!base_widget->negotiated)
++ return;
++
+ GST_OBJECT_LOCK (gtk_sink);
+ reconfigure =
+ (width != gtk_sink->display_width || height != gtk_sink->display_height);
+@@ -138,7 +149,7 @@ _size_changed_cb (GtkWidget * widget, GdkRectangle * r
+ 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 ());
+ }
+@@ -147,9 +158,9 @@ _size_changed_cb (GtkWidget * widget, GdkRectangle * r
+ static void
+ destroy_cb (GtkWidget * widget, GstGtkGLSink * gtk_sink)
+ {
+- if (gtk_sink->size_allocate_sig_handler) {
+- g_signal_handler_disconnect (widget, gtk_sink->size_allocate_sig_handler);
+- gtk_sink->size_allocate_sig_handler = 0;
++ if (gtk_sink->widget_resize_sig_handler) {
++ g_signal_handler_disconnect (widget, gtk_sink->widget_resize_sig_handler);
++ gtk_sink->widget_resize_sig_handler = 0;
+ }
+
+ if (gtk_sink->widget_destroy_sig_handler) {
+@@ -172,29 +183,34 @@ gst_gtk_gl_sink_start (GstBaseSink * bsink)
+ gst_widget = GTK_GST_GL_WIDGET (base_sink->widget);
+
+ /* Track the allocation size */
+- gtk_sink->size_allocate_sig_handler =
+- g_signal_connect (gst_widget, "size-allocate",
+- G_CALLBACK (_size_changed_cb), gtk_sink);
++ if (!gtk_sink->widget_resize_sig_handler) {
++ gtk_sink->widget_resize_sig_handler =
++ g_signal_connect (gst_widget, "resize",
++ G_CALLBACK (_size_changed_cb), gtk_sink);
++ }
+
+- gtk_sink->widget_destroy_sig_handler =
+- g_signal_connect (gst_widget, "destroy", G_CALLBACK (destroy_cb),
+- gtk_sink);
++ if (!gtk_sink->widget_destroy_sig_handler) {
++ 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;
+ }
+
+- gtk_sink->display = gtk_gst_gl_widget_get_display (gst_widget);
+- gtk_sink->context = gtk_gst_gl_widget_get_context (gst_widget);
+- gtk_sink->gtk_context = gtk_gst_gl_widget_get_gtk_context (gst_widget);
++ if (!gtk_sink->display)
++ gtk_sink->display = gtk_gst_gl_widget_get_display (gst_widget);
++ if (!gtk_sink->context)
++ gtk_sink->context = gtk_gst_gl_widget_get_context (gst_widget);
++ if (!gtk_sink->gtk_context)
++ gtk_sink->gtk_context = gtk_gst_gl_widget_get_gtk_context (gst_widget);
+
+ 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;
+ }
+
+@@ -208,7 +224,14 @@ static gboolean
+ gst_gtk_gl_sink_stop (GstBaseSink * bsink)
+ {
+ GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (bsink);
++ GstGtkBaseSink *base_sink = GST_GTK_BASE_SINK (bsink);
+
++ if (gtk_sink->widget_resize_sig_handler) {
++ g_signal_handler_disconnect (base_sink->widget,
++ gtk_sink->widget_resize_sig_handler);
++ gtk_sink->widget_resize_sig_handler = 0;
++ }
++
+ if (gtk_sink->display) {
+ gst_object_unref (gtk_sink->display);
+ gtk_sink->display = NULL;
+@@ -348,10 +371,10 @@ gst_gtk_gl_sink_finalize (GObject * object)
+ GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (object);
+ GstGtkBaseSink *base_sink = GST_GTK_BASE_SINK (object);
+
+- if (gtk_sink->size_allocate_sig_handler) {
++ if (gtk_sink->widget_resize_sig_handler) {
+ g_signal_handler_disconnect (base_sink->widget,
+- gtk_sink->size_allocate_sig_handler);
+- gtk_sink->size_allocate_sig_handler = 0;
++ gtk_sink->widget_resize_sig_handler);
++ gtk_sink->widget_resize_sig_handler = 0;
+ }
+
+ if (gtk_sink->widget_destroy_sig_handler) {
+--- ext/gtk/gstgtkglsink.h.orig 2019-12-03 11:06:37 UTC
++++ ext/gtk/gstgtkglsink.h
+@@ -65,7 +65,7 @@ struct _GstGtkGLSink
+ gint display_width;
+ gint display_height;
+
+- gulong size_allocate_sig_handler;
++ gulong widget_resize_sig_handler;
+ gulong widget_destroy_sig_handler;
+ };
+
+--- ext/gtk/gstgtksink.c.orig 2019-12-03 11:06:37 UTC
++++ ext/gtk/gstgtksink.c
+@@ -19,15 +19,22 @@
+ */
+
+ /**
+- * SECTION:element-gtkgstsink
+- * @title: gtkgstsink
++ * SECTION:element-gtksink
++ * @title: gtksink
+ *
+ */
+
++/**
++ * SECTION:element-gtk4sink
++ * @title: gtk4sink
++ *
++ */
++
+ #ifdef HAVE_CONFIG_H
+ #include "config.h"
+ #endif
+
++#include "gtkconfig.h"
+ #include "gtkgstwidget.h"
+ #include "gstgtksink.h"
+
+@@ -49,8 +56,8 @@ GST_STATIC_PAD_TEMPLATE ("sink",
+
+ #define gst_gtk_sink_parent_class parent_class
+ G_DEFINE_TYPE_WITH_CODE (GstGtkSink, gst_gtk_sink, GST_TYPE_GTK_BASE_SINK,
+- GST_DEBUG_CATEGORY_INIT (gst_debug_gtk_sink, "gtksink", 0,
+- "Gtk Video Sink"));
++ GST_DEBUG_CATEGORY_INIT (gst_debug_gtk_sink, GTKCONFIG_SINK, 0,
++ GTKCONFIG_NAME " Video Sink"));
+
+ static void
+ gst_gtk_sink_class_init (GstGtkSinkClass * klass)
+@@ -62,9 +69,10 @@ gst_gtk_sink_class_init (GstGtkSinkClass * klass)
+ base_class = (GstGtkBaseSinkClass *) klass;
+
+ base_class->create_widget = gtk_gst_widget_new;
+- base_class->window_title = "Gtk+ Cairo renderer";
++ base_class->window_title = GTKCONFIG_NAME " Cairo Renderer";
+
+- gst_element_class_set_metadata (gstelement_class, "Gtk Video Sink",
++ gst_element_class_set_metadata (gstelement_class,
++ GTKCONFIG_NAME " Video Sink",
+ "Sink/Video", "A video sink that renders to a GtkWidget",
+ "Matthew Waters <matthew@centricular.com>");
+
+--- ext/gtk/gstplugin.c.orig 2019-12-03 11:06:37 UTC
++++ ext/gtk/gstplugin.c
+@@ -1,6 +1,7 @@
+ /*
+ * GStreamer
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
++ * Copyright (C) 2020 Rafał Dzięgiel <rafostar.github@gmail.com>
+ *
+ * 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,31 @@
+ #include "config.h"
+ #endif
+
++#include "gtkconfig.h"
+ #include "gstgtksink.h"
+-#if defined(HAVE_GTK3_GL)
++
++#if defined(HAVE_GTK_GL)
+ #include "gstgtkglsink.h"
+ #endif
+
+ static gboolean
+ plugin_init (GstPlugin * plugin)
+ {
+- if (!gst_element_register (plugin, "gtksink",
++ if (!gst_element_register (plugin, GTKCONFIG_SINK,
+ GST_RANK_NONE, GST_TYPE_GTK_SINK)) {
+ return FALSE;
+ }
+-#if defined(HAVE_GTK3_GL)
+- if (!gst_element_register (plugin, "gtkglsink",
++#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)
+--- ext/gtk/gtkconfig.h.orig 2021-08-23 11:11:05 UTC
++++ ext/gtk/gtkconfig.h
+@@ -0,0 +1,31 @@
++/*
++ * GStreamer
++ * Copyright (C) 2020 Rafał Dzięgiel <rafostar.github@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.
++ */
++
++#if defined(BUILD_FOR_GTK4)
++#define GTKCONFIG_PLUGIN gtk4
++#define GTKCONFIG_NAME "GTK4"
++#define GTKCONFIG_SINK "gtk4sink"
++#define GTKCONFIG_GLSINK "gtk4glsink"
++#else
++#define GTKCONFIG_PLUGIN gtk
++#define GTKCONFIG_NAME "GTK"
++#define GTKCONFIG_SINK "gtksink"
++#define GTKCONFIG_GLSINK "gtkglsink"
++#endif
+--- ext/gtk/gtkgstbasewidget.c.orig 2019-12-03 11:06:37 UTC
++++ ext/gtk/gtkgstbasewidget.c
+@@ -1,6 +1,7 @@
+ /*
+ * GStreamer
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
++ * Copyright (C) 2020 Rafał Dzięgiel <rafostar.github@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+@@ -74,7 +75,23 @@ gtk_gst_base_widget_get_preferred_height (GtkWidget *
+ *natural = video_height;
+ }
+
++#if defined(BUILD_FOR_GTK4)
+ static void
++gtk_gst_base_widget_measure (GtkWidget * widget, GtkOrientation orientation,
++ gint for_size, gint * min, gint * natural,
++ gint * minimum_baseline, gint * 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";
++ GdkEvent *event = _get_current_event (controller);
++ const gchar *str = _gdk_key_to_navigation_string (keyval);
+
+- if (!str)
+- str = event->string;
+-
+- gst_navigation_send_key_event (GST_NAVIGATION (element), key_type, str);
++ 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_
+ }
+
+ 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
++ gint n_press, gdouble x, gdouble 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,22 @@ gtk_gst_base_widget_button_event (GtkWidget * widget,
+ }
+
+ static gboolean
+-gtk_gst_base_widget_motion_event (GtkWidget * widget, GdkEventMotion * event)
++gtk_gst_base_widget_motion_event (GtkEventControllerMotion * motion_controller,
++ gdouble x, gdouble 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;
+
+ 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,23 +460,20 @@ gtk_gst_base_widget_class_init (GtkGstBaseWidgetClass
+ "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;
+-
+ widget->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO;
+ widget->par_n = DEFAULT_PAR_N;
+ widget->par_d = DEFAULT_PAR_D;
+@@ -423,20 +485,66 @@ gtk_gst_base_widget_init (GtkGstBaseWidget * widget)
+ g_weak_ref_init (&widget->element, NULL);
+ g_mutex_init (&widget->lock);
+
++ widget->key_controller = gtk_event_controller_key_new (
++#if !defined(BUILD_FOR_GTK4)
++ GTK_WIDGET (widget)
++#endif
++ );
++ g_signal_connect (widget->key_controller, "key-pressed",
++ G_CALLBACK (gtk_gst_base_widget_key_event), NULL);
++ g_signal_connect (widget->key_controller, "key-released",
++ G_CALLBACK (gtk_gst_base_widget_key_event), NULL);
++
++ widget->motion_controller = gtk_event_controller_motion_new (
++#if !defined(BUILD_FOR_GTK4)
++ GTK_WIDGET (widget)
++#endif
++ );
++ g_signal_connect (widget->motion_controller, "motion",
++ G_CALLBACK (gtk_gst_base_widget_motion_event), NULL);
++
++ widget->click_gesture =
++#if defined(BUILD_FOR_GTK4)
++ gtk_gesture_click_new ();
++#else
++ gtk_gesture_multi_press_new (GTK_WIDGET (widget));
++#endif
++ g_signal_connect (widget->click_gesture, "pressed",
++ G_CALLBACK (gtk_gst_base_widget_button_event), NULL);
++ g_signal_connect (widget->click_gesture, "released",
++ G_CALLBACK (gtk_gst_base_widget_button_event), NULL);
++
++#if defined(BUILD_FOR_GTK4)
++ /* Otherwise widget in grid will appear as a 1x1px
++ * video which might be misleading for users */
++ gtk_widget_set_hexpand (GTK_WIDGET (widget), TRUE);
++ gtk_widget_set_vexpand (GTK_WIDGET (widget), TRUE);
++
++ gtk_widget_set_focusable (GTK_WIDGET (widget), TRUE);
++ gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (widget->click_gesture),
++ GDK_BUTTON_PRIMARY);
++
++ gtk_widget_add_controller (GTK_WIDGET (widget), widget->key_controller);
++ gtk_widget_add_controller (GTK_WIDGET (widget), widget->motion_controller);
++ gtk_widget_add_controller (GTK_WIDGET (widget),
++ GTK_EVENT_CONTROLLER (widget->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
+ gtk_gst_base_widget_finalize (GObject * object)
+ {
+ GtkGstBaseWidget *widget = GTK_GST_BASE_WIDGET (object);
++
++ /* GTK4 takes ownership of EventControllers
++ * while GTK3 still needs manual unref */
++#if !defined(BUILD_FOR_GTK4)
++ g_object_unref (widget->key_controller);
++ g_object_unref (widget->motion_controller);
++ g_object_unref (widget->click_gesture);
++#endif
+
+ gst_buffer_replace (&widget->pending_buffer, NULL);
+ gst_buffer_replace (&widget->buffer, NULL);
+--- ext/gtk/gtkgstbasewidget.h.orig 2019-12-03 11:06:37 UTC
++++ ext/gtk/gtkgstbasewidget.h
+@@ -1,6 +1,7 @@
+ /*
+ * GStreamer
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
++ * Copyright (C) 2020 Rafał Dzięgiel <rafostar.github@gmail.com>
+ *
+ * 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 <gst/gst.h>
+ #include <gst/video/video.h>
+
++#if !defined(BUILD_FOR_GTK4)
++#include <gdk/gdk.h>
++#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)
+@@ -39,9 +44,7 @@ struct _GtkGstBaseWidget
+ {
+ union {
+ GtkDrawingArea drawing_area;
+-#if GTK_CHECK_VERSION(3, 15, 0)
+ GtkGLArea gl_area;
+-#endif
+ } parent;
+
+ /* properties */
+@@ -67,6 +70,11 @@ struct _GtkGstBaseWidget
+ GMutex lock;
+ GWeakRef element;
+
++ /* event controllers */
++ GtkEventController *key_controller;
++ GtkEventController *motion_controller;
++ GtkGesture *click_gesture;
++
+ /* Pending draw idles callback */
+ guint draw_id;
+ };
+@@ -75,9 +83,7 @@ struct _GtkGstBaseWidgetClass
+ {
+ union {
+ GtkDrawingAreaClass drawing_area_class;
+-#if GTK_CHECK_VERSION(3, 15, 0)
+ GtkGLAreaClass gl_area_class;
+-#endif
+ } parent_class;
+ };
+
+--- ext/gtk/gtkgstglwidget.c.orig 2019-12-03 11:06:37 UTC
++++ ext/gtk/gtkgstglwidget.c
+@@ -1,6 +1,7 @@
+ /*
+ * GStreamer
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
++ * Copyright (C) 2020 Rafał Dzięgiel <rafostar.github@gmail.com>
+ *
+ * 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 <gst/video/video.h>
+
+ #if GST_GL_HAVE_WINDOW_X11 && defined (GDK_WINDOWING_X11)
++#if defined(BUILD_FOR_GTK4)
++#include <gdk/x11/gdkx.h>
++#else
+ #include <gdk/gdkx.h>
++#endif
+ #include <gst/gl/x11/gstgldisplay_x11.h>
+ #endif
+
+ #if GST_GL_HAVE_WINDOW_WAYLAND && defined (GDK_WINDOWING_WAYLAND)
++#if defined(BUILD_FOR_GTK4)
++#include <gdk/wayland/gdkwayland.h>
++#else
+ #include <gdk/gdkwayland.h>
++#endif
+ #include <gst/gl/wayland/gstgldisplay_wayland.h>
+ #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
+--- ext/gtk/gtkgstwidget.c.orig 2019-12-03 11:06:37 UTC
++++ ext/gtk/gtkgstwidget.c
+@@ -38,17 +38,15 @@
+
+ G_DEFINE_TYPE (GtkGstWidget, gtk_gst_widget, GTK_TYPE_DRAWING_AREA);
+
+-static gboolean
+-gtk_gst_widget_draw (GtkWidget * widget, cairo_t * cr)
++static void
++_drawing_area_draw (GtkDrawingArea * da, cairo_t * cr,
++ gint widget_width, gint widget_height, gpointer data)
+ {
++ GtkWidget *widget = GTK_WIDGET (da);
+ GtkGstBaseWidget *gst_widget = (GtkGstBaseWidget *) widget;
+- guint widget_width, widget_height;
+ cairo_surface_t *surface;
+ GstVideoFrame frame;
+
+- widget_width = gtk_widget_get_allocated_width (widget);
+- widget_height = gtk_widget_get_allocated_height (widget);
+-
+ GTK_GST_BASE_WIDGET_LOCK (gst_widget);
+
+ /* There is not much to optimize in term of redisplay, so simply swap the
+@@ -148,7 +146,10 @@ gtk_gst_widget_draw (GtkWidget * widget, cairo_t * cr)
+ color.alpha = 1.0;
+ } else {
+ gtk_style_context_get_color (gtk_widget_get_style_context (widget),
+- GTK_STATE_FLAG_NORMAL, &color);
++#if !defined(BUILD_FOR_GTK4)
++ GTK_STATE_FLAG_NORMAL,
++#endif
++ &color);
+ }
+ gdk_cairo_set_source_rgba (cr, &color);
+ cairo_rectangle (cr, 0, 0, widget_width, widget_height);
+@@ -156,8 +157,20 @@ gtk_gst_widget_draw (GtkWidget * widget, cairo_t * cr)
+ }
+
+ GTK_GST_BASE_WIDGET_UNLOCK (gst_widget);
++}
++
++#if !defined(BUILD_FOR_GTK4)
++static gboolean
++gtk_gst_widget_draw (GtkWidget * widget, cairo_t * cr)
++{
++ gint width = gtk_widget_get_allocated_width (widget);
++ gint height = gtk_widget_get_allocated_height (widget);
++
++ _drawing_area_draw (GTK_DRAWING_AREA (widget), cr, width, height, NULL);
++
+ return FALSE;
+ }
++#endif
+
+ static void
+ gtk_gst_widget_finalize (GObject * object)
+@@ -171,17 +184,25 @@ static void
+ gtk_gst_widget_class_init (GtkGstWidgetClass * klass)
+ {
+ GObjectClass *gobject_klass = (GObjectClass *) klass;
++#if !defined(BUILD_FOR_GTK4)
+ GtkWidgetClass *widget_klass = (GtkWidgetClass *) klass;
++#endif
+
+ gtk_gst_base_widget_class_init (GTK_GST_BASE_WIDGET_CLASS (klass));
+ gobject_klass->finalize = gtk_gst_widget_finalize;
++#if !defined(BUILD_FOR_GTK4)
+ widget_klass->draw = gtk_gst_widget_draw;
++#endif
+ }
+
+ static void
+ gtk_gst_widget_init (GtkGstWidget * widget)
+ {
+ gtk_gst_base_widget_init (GTK_GST_BASE_WIDGET (widget));
++#if defined(BUILD_FOR_GTK4)
++ gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (widget),
++ _drawing_area_draw, NULL, NULL);
++#endif
+ }
+
+ GtkWidget *
+--- ext/gtk/meson.build.orig 2019-12-03 11:06:37 UTC
++++ ext/gtk/meson.build
+@@ -1,3 +1,4 @@
++gtk_versions = [3, 4]
+ gtk_sources = [
+ 'gstgtkbasesink.c',
+ 'gstgtksink.c',
+@@ -6,52 +7,86 @@ gtk_sources = [
+ 'gtkgstbasewidget.c',
+ 'gtkgstwidget.c',
+ ]
++gtk_dep = dependency('gtk+-3.0', required : get_option('gtk3'))
++gtk_optional_deps = []
+
+-gtk_defines = []
+-optional_deps = []
++gtk4_dep = dependency('gtk4', required : get_option('gtk4'))
++gtk4_optional_deps = []
+
+-gtk_dep = dependency('gtk+-3.0', required : get_option('gtk3'))
+-if gtk_dep.found()
+- if build_gstgl and gstgl_dep.found() and gtk_dep.version().version_compare('>=3.15.0')
+- have_gtk3_gl_windowing = false
++foreach gtk_ver : gtk_versions
++ gtkv = 'gtk' + gtk_ver.to_string()
+
++ gtk_state = get_option(gtkv)
++ if gtk_state.disabled()
++ continue
++ endif
++
++ min_ver = gtk_ver >= 4 ? '3.99.2' : '3.24.0'
++ x11_str = gtk_ver >= 4 ? gtkv + '-x11' : 'gtk+-x11-3.0'
++ way_str = 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 build_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_str, required : false)
+ if gtk_x11_dep.found()
+ optional_deps += gtk_x11_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_str, required : false)
+ if gtk_wayland_dep.found()
+ optional_deps += gtk_wayland_dep
+- have_gtk3_gl_windowing = true
++ have_gtk_gl_windowing = true
+ endif
+ endif
++ endif
+
+- if have_gtk3_gl_windowing
+- gtk_sources += [
+- 'gstgtkglsink.c',
+- 'gtkgstglwidget.c',
+- ]
+- optional_deps += gstgl_dep
+- gtk_defines += ['-DGST_USE_UNSTABLE_API', '-DHAVE_GTK3_GL']
+- endif
++ if have_gtk_gl_windowing
++ lib_sources += [
++ 'gstgtkglsink.c',
++ 'gtkgstglwidget.c',
++ ]
++ optional_deps += gstgl_dep
++ gtk_defines += ['-DGST_USE_UNSTABLE_API', '-DHAVE_GTK_GL']
+ endif
+
+- gstgtk = library('gstgtk',
+- gtk_sources,
++ if gtk_ver == 3
++ gtk_optional_deps = optional_deps
++ elif gtk_ver == 4
++ gtk4_optional_deps = optional_deps
++ gtk_defines += '-DBUILD_FOR_GTK4'
++ endif
++
++ lib_name = 'gstgtk'
++ if gtk_ver > 3
++ lib_name += gtk_ver.to_string()
++ endif
++
++ 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,
++ dependencies : [lib_dep, gstvideo_dep, gstbase_dep, libm] + optional_deps,
+ install : true,
+ install_dir : plugins_install_dir,
+ )
+ pkgconfig.generate(gstgtk, install_dir : plugins_pkgconfig_install_dir)
+-endif
++endforeach
+
+--- meson_options.txt.orig 2019-12-03 11:06:37 UTC
++++ meson_options.txt
+@@ -53,6 +53,7 @@ option('dv1394', type : 'feature', value : 'auto', des
+ 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 : 'disabled', 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')
+--- tests/examples/gtk/meson.build.orig 2019-12-03 11:06:37 UTC
++++ tests/examples/gtk/meson.build
+@@ -1,5 +1,5 @@
+ executable('gtksink', 'gtksink.c',
+- dependencies: [gst_dep, gtk_dep, optional_deps],
++ dependencies: [gst_dep, gtk_dep, gtk_optional_deps],
+ c_args: gst_plugins_good_args,
+ include_directories: [configinc],
+ install: false)
diff --git a/x11-toolkits/gstreamer1-plugins-gtk4/pkg-descr b/x11-toolkits/gstreamer1-plugins-gtk4/pkg-descr
new file mode 100644
index 000000000000..3c967400de47
--- /dev/null
+++ b/x11-toolkits/gstreamer1-plugins-gtk4/pkg-descr
@@ -0,0 +1,6 @@
+GTK4 plugin features:
+
+- gtk4glsink: A video sink that renders to a GtkWidget using OpenGL
+- gtk4sink: A video sink that renders to a GtkWidget
+
+WWW: https://gstreamer.freedesktop.org/documentation/gtk/gtksink.html